เลือกแถวที่ตรงตามเงื่อนไขสำหรับกลุ่ม (ไม่มีตารางชั่วคราว)


10

มีตารางที่มี 3 คอลัมน์:

ID  category    flag
1       A       1
2       A       0
3       A       0
4       B       0
5       C       0

ฉันต้องการเลือกแถวทั้งหมดที่มีflag = 1อย่างน้อยหนึ่งครั้งต่อหมวดหมู่

ผลลัพธ์ที่คาดหวัง:

ID  category    flag
1       A       1
2       A       0
3       A       0

สามารถแก้ไขได้โดยใช้ตารางชั่วคราวเช่นนี้

select ID into #tempTable from someTable where flag = 1
select * from someTable join #tempTable on someTable.ID = #tempTable.ID

แต่ฉันต้องการทางออกสำหรับการจัดกลุ่มซึ่งฉันพยายามหาวิธี ความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชม.

คำตอบ:


16

GROUP BYไม่สามารถใช้คนเดียวเพราะมันจะส่งคืนเพียง 1 แถวต่อกลุ่ม ( category)


  • คุณสามารถใช้แบบสอบถามย่อยด้วยflag = 1และINNER JOIN:

    SELECT d1.ID, d1.category, d1.flag
    FROM data d1
    INNER JOIN (
        SELECT DISTINCT category FROM data WHERE flag = 1
    ) d2 
        ON d2.category = d1.category ;
  • คุณสามารถใช้EXISTSข้อ:

    SELECT d.ID, d.category, d.flag
    FROM data d
    WHERE EXISTS (
        SELECT 1 FROM data WHERE flag = 1 AND category = d.category
    ) ;   
  • คุณสามารถใช้INข้อ (แม้ว่าEXISTSจะดีกว่า):

    SELECT d.ID, d.category, d.flag
    FROM data d
    WHERE d.category IN (SELECT category FROM data WHERE flag = 1) ;
  • คุณยังสามารถใช้CROSS APPLYกับแบบสอบถามย่อยในflag = 1:

    SELECT d.ID, d.category, d.flag
    FROM data d
    CROSS APPLY (
        SELECT TOP (1) category 
        FROM data 
        WHERE flag = 1 AND category = d.category
    ) ca ;

DISTINCTไม่จำเป็นถ้าสำหรับแต่ละหมวดหมู่จะมีได้เพียง 1 แถวflag = 1เท่านั้น

เอาท์พุท:

ID  category    flag
1       A       1
2       A       0
3       A       0

DISTINCT ไม่จำเป็นสำหรับเพรดิเคต IN และหากมีเพียงหนึ่งแถวต่อหมวดหมู่เท่านั้นที่สามารถมีค่าสถานะเป็น 1 ได้ DISTINCT นั้นไม่จำเป็นเลย
Andriy M

@AndriyM แก้ไขเกี่ยวกับINแบบสอบถาม แต่ OP มี " ฉันต้องการเลือกแถวทั้งหมดที่มีการตั้งค่าสถานะ = 1 อย่างน้อยหนึ่งครั้งต่อหมวดหมู่ " ซึ่งทำให้ฉันคิดว่าDISTINCTจำเป็นสำหรับการค้นหาอื่น ๆ
ypercubeᵀᴹ

1
และในCROSS APPLYที่อาจจะมีประสิทธิภาพมากขึ้นถ้าแทนที่ด้วยSELECT DISTINCT category SELECT TOP (1) whateverมันจะเป็นอีกวิธีหนึ่งในการเขียนEXISTSคิวรีได้อย่างมีประสิทธิภาพ
ypercubeᵀᴹ

@Andriy นี่คือเหตุผลที่ฉันเพิ่มบันทึกเมื่อวานนี้ตามความคิดเห็นเริ่มต้นของคุณ: ไม่จำเป็นถ้ามีเพียง 1 แถวที่มีธง = 1
Julien Vavasseur

4

สมมติว่าFlagเป็นBITคอลัมน์หรือINTที่ใช้เวลาเท่านั้น0และ1เป็นค่านี้สามารถทำได้โดยใช้ฟังก์ชั่นหน้าต่างเช่นกัน ตัวอย่างเช่น

DECLARE @Test TABLE
(
  ID INT
  , Category VARCHAR(1)
  , Flag BIT
);

INSERT INTO @Test (ID, Category, Flag)
VALUES (1, 'A', 1)
  , (2, 'A', 0)
  , (3, 'A', 0)
  , (4, 'B', 0)
  , (5, 'C', 0);

SELECT T.ID
  , T.Category
  , T.Flag
FROM (
  SELECT ID
    , Category
    , Flag
    , MAX(CAST(Flag AS TINYINT)) OVER(PARTITION BY Category) AS MaxFlag
  FROM @Test
  ) AS T
WHERE T.MaxFlag = 1;

นั่นคือผลลัพธ์:

ID Category Flag  
-- -------- ----- 
1  A        True  
2  A        False 
3  A        False 

สิ่งนี้จะพบสูงสุดFlagสำหรับแต่ละหมวดหมู่ในตารางในกรณีของคุณอาจเป็นจริง / เท็จเท่านั้นและเลือกผู้ที่มีtrue(1)เท่านั้น

ต้องการการแปลงTINYINTเนื่องจากMAXไม่ยอมรับการBITโต้แย้ง

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