ทางเลือกของประโยคที่ [ปิด]


16

มีวิธีSELECTแถวเฉพาะกับข้อมูลบางอย่างในคอลัมน์โดยไม่ใช้WHEREหรือไม่?

เช่นถ้าฉันมีสิ่งนี้:

SELECT * FROM Users
WHERE town = 'Townsville'

มีวิธีการใช้WHEREประโยคในSELECTคำสั่งหรือไม่

สิ่งที่ต้องการ

SELECT *, town('Townsville') FROM Users

มันเป็นคำถามที่แปลกประหลาด แต่เป็นคำถามที่ฉันถูกถามจากคนรอบข้าง


4
ถามพวกเขาว่าทำไมพวกเขาถึงถามเรื่องนี้ บริบทเป็นสิ่งสำคัญ
Aaron Bertrand

@AaronBertrand, MikaelEriksson - โดยทั่วไปแล้วมันเป็นแบบสอบถามเล็ก ๆ น้อย ๆ เกี่ยวกับ SQL ในที่ทำงานฉันเจอคำถามที่ระบุว่า "เลือกผู้ใช้ทั้งหมดจากตารางผู้ใช้ที่มาจาก Townsville โดยไม่ต้องใช้คำสั่งที่ไหน" และฉันไม่รู้ เป็นไปได้! บางทีฉันกำลังเข้าใกล้สิ่งนี้ในทางที่ผิด .. ?
Josh Stevenson

เพียงแค่ระบุว่าแบบสอบถามนี้ไม่ได้เชื่อมโยงกับสถานะการจ้างของฉันกับ บริษัท ! เป็นเรื่องสนุกนิดหน่อย
Josh Stevenson

คำตอบ:


17

ไม่แน่ใจว่านี่เป็นสิ่งที่บ้าหรือเปล่าที่คุณกำลังมองหา ...

คำเตือน : ฉันไม่มีความคิดอย่างแน่นอนว่าทำไมคุณต้องการใช้สิ่งนี้

SELECT * 
FROM Users AS u
INNER JOIN (SELECT 'Townsville' town) towns 
  ON towns.town = u.Town;

17

ข้อมูล

DECLARE @Example AS table
(
    UserName varchar(30) NULL,
    Town varchar(30) NULL
);

INSERT @Example
    (UserName, Town)
VALUES
    ('Aaron', 'Not Townsville'),
    ('Bob', 'Not Townsville'),
    ('Charles', 'Townsville'),
    ('Charles', 'Townsville'),
    ('Charles', 'Townsville'),
    ('Charles', 'Townsville'),
    ('Dan', 'Townsville'),
    ('Eric', 'Not Townsville');

โซลูชั่นทางเลือก

SELECT E.UserName, E.Town
FROM @Example AS E
GROUP BY E.Town, E.UserName
HAVING E.Town = 'Townsville'

-- OR

SELECT E.UserName, 'Townsville' AS Town
FROM @Example AS E
GROUP BY E.UserName
HAVING 1 = MAX(CASE WHEN E.Town = 'Townsville' THEN 1 ELSE 0 END);

-- OR

SELECT E.UserName, E.Town
FROM @Example AS E
INTERSECT
SELECT E.UserName, 'Townsville' AS Town
FROM @Example AS E

เก็บรักษาข้อมูลที่ซ้ำกัน

-- :)
SELECT E.UserName, E.Town
FROM @Example AS E
CROSS APPLY (VALUES(NEWID())) AS CA (n)
GROUP BY E.Town, E.UserName, CA.n
HAVING E.Town = 'Townsville'

-- Simulating INTERSECT ALL
SELECT
    R.UserName,
    R.Town
FROM 
(
    SELECT 
        E.UserName, 
        E.Town, 
        rn =
            ROW_NUMBER() OVER (
                PARTITION BY E.UserName, E.Town 
                ORDER BY E.UserName, E.Town)
    FROM @Example AS E
    INTERSECT
    SELECT 
        E.UserName, 
        'Townsville', 
        rn = 
        ROW_NUMBER() OVER (
            PARTITION BY E.UserName 
            ORDER BY E.UserName)
    FROM @Example AS E
) AS R;

เอาท์พุท:

╔══════════╦════════════╗
 UserName     Town    
╠══════════╬════════════╣
 Charles   Townsville 
 Dan       Townsville 
╚══════════╩════════════╝

สำหรับตัวอย่างสุดท้าย:

╔══════════╦════════════╗
 UserName     Town    
╠══════════╬════════════╣
 Charles   Townsville 
 Charles   Townsville 
 Charles   Townsville 
 Charles   Townsville 
 Dan       Townsville 
╚══════════╩════════════╝

ลองที่นี่: Stack Exchange Data Explorer


ดีมาก! ฉันคาดเดาว่าฉันจะไม่สามารถใช้สิ่งนี้ในสถานการณ์ที่ฉันไม่มีคอลัมน์ที่มีข้อมูลเฉพาะเช่น 'ชื่อผู้ใช้' หรือไม่ เช่นถ้าฉันมีแค่ชื่อนามสกุลเมือง
Josh Stevenson

3
@JoshStevenson ถูกต้อง แต่ฉันได้เพิ่มวิธีที่บ้าคลั่งที่เหมาะสมในการเก็บข้อมูลที่ซ้ำกันเป็นตัวอย่างสุดท้ายและจากนั้นเป็นวิธีที่เหมาะสม
Paul White Reinstate Monica

1
สำหรับGROUP BYโซลูชันคุณสามารถเพิ่ม PK ในกลุ่มตามรายการ (เพื่อให้แน่ใจ 100% ว่าคิวรีส่งคืนจำนวนแถวเท่ากันกับ WHERE) สมมติว่ามีการ PK;)
ypercubeᵀᴹ


14

"เพื่อความสนุก" คุณสามารถใช้order byกับtop(1) with ties

select top(1) with ties *
from dbo.Users
order by case when town = 'Townsville' then 1 else 2 end;

นี้จะสั่งซื้อแถวทั้งหมดกับTownsvilleครั้งแรกนับตั้งแต่ผลตอบแทนกรณีที่หาก1 town = 'Townsville'แถวอื่น ๆ ทั้งหมดจะได้รับการ2ส่งคืนจากเคส

ส่วนwith tiesคำสั่งทำให้แบบสอบถามส่งคืนแถวทั้งหมดที่เป็น "เสมอ" สำหรับตำแหน่งสุดท้ายในแถวที่ส่งคืน การใช้top(1)ร่วมกับwith tiesจะส่งคืนแถวทั้งหมดที่มีค่าเท่ากับแถวแรกในนิพจน์ที่ใช้ในการเรียงลำดับตามข้อ

หมายเหตุมาร์ตินสมิ ธ ชี้ให้เห็นในความคิดเห็นมันจะกลับมาทุกแถวถ้าคุณขอเมืองที่ไม่มีอยู่ในตาราง

หากคุณไม่กลัวสิ่ง XML ของฐานข้อมูลคุณสามารถใช้เพรดิเคตในฟังก์ชั่น nodes ()

ยืมการติดตั้งจาก Paul White

select T.X.value('(UserName/text())[1]', 'varchar(30)') as UserName,
       T.X.value('(Town/text())[1]', 'varchar(30)') as Town
from (
     select *
     from @Example
     for xml path('row'), type 
     ) as C(X)
  cross apply C.X.nodes('/row[Town = "Townsville"]') as T(X);

อีกรุ่นที่มีtopและorder byที่ใช้งานได้จริงเมื่อค้นหาเมืองที่ไม่มีอยู่

select top(
          select sum(case when town = 'Townsville' then 1 end)
          from @Example
          ) *
from @Example
order by case when town = 'Townsville' then 1 else 2 end

7

คุณมีสองสิ่งที่แตกต่างกันที่นี่

SELECT * FROM Users
WHERE town = 'Townsville'

จะ จำกัด จำนวนแถวที่คุณกลับไปที่ที่ = เมืองTownsville

SELECT *, town('Townsville') FROM Users

เป็นไปได้ที่จะผ่านที่แท้จริงกับการทำงานที่เรียกว่าTownsville townมันจะไม่ จำกัด แถวที่ส่งคืนโดยแบบสอบถามและในความเป็นจริงหากฟังก์ชันส่งคืนอะไร แต่มีค่าเดียวคุณจะได้รับข้อผิดพลาด

มีวิธีอื่นในการ จำกัด จำนวนแถวที่คุณได้รับกลับมาจากแบบสอบถาม ยกตัวอย่างเช่น HAVING clause แต่มันมีข้อกำหนดอื่นอีกหลายอย่าง

SELECT town FROM Users
GROUP BY town
HAVING town = 'Townsville'

หรือ INNER JOIN ถึงแม้ว่าอันนี้จะค่อนข้างแปลกถ้าคุณไม่มีตารางที่สอง

SELECT * FROM Users
INNER JOIN (SELECT 1 col1) UselessTable
    ON Users.town = 'Townsville'

5

นี่คือตัวอย่างการใช้นิพจน์ตารางทั่วไป (CTE)

with Town as 
(
    select 'Townsville' as Town
)
select *
  from Users u
  join Town  t on u.Town = t.Town

5

คุณสามารถทำสิ่งนี้ได้:

    SELECT A.* 
    FROM Users A
         INNER JOIN Users B ON A.Id = B.Id AND B.town = 'Townsville'

พูดอย่างเคร่งครัดว่าคุณไม่ได้ใช้ส่วนคำสั่ง WHERE


5

นี่เป็นวิธีที่โง่ที่สุดในการทำสิ่งที่ฉันยังไม่เห็น ....

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;  -- Our important work should be all the database cares about
GO
BEGIN TRANSACTION

DECLARE @MyTableVar table(<all columns in order from the user table>, oldtown VARCHAR(50));

UPDATE users
SET town = N'Townsville'
OUTPUT 
     inserted.*  -- We don't want to have to type out the columns because that would be too much work
    deleted.town
INTO @MyTableVar;

--Display the result set of the table variable to prevent undesirables from sullying our output by inserting incorrect data even though we should have exclusive access.
SELECT * -- Select everything we want except for the 'oldtown' column because that data was probably wrong anyway
FROM @MyTableVar;

UPDATE u -- We don't want to be bad stewards of our data
SET
    town = oldtown
FROM users u
    INNER JOIN @MyTableVar mtv ON mtv.town = u.town, <Match up all the columns to REALLY ensure we are matching the proper row>

COMMIT TRANSACTION -- Make sure we save our work

ฉันไม่สามารถจินตนาการได้ว่าทำไมนี่ไม่ใช่สิ่งแรกที่ควรได้รับการแนะนำ :)


-2
SELECT *, 
    case when town='Townsville' then 'Townsville' 
         else null 
    end as Town
FROM Users

เมืองทั้งหมดที่อยู่นอกเหนือจาก Townsville จะเป็นโมฆะ แก้ไขปัญหา.

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.