виберіть рядки з умовою для групи (без тимчасової таблиці)


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 взагалі непотрібний.
Андрій М

@AndriyM правильний INзапит. Але в ОП " Я хочу вибрати всі рядки, які мають прапор = 1 принаймні один раз на категорію ", що змушує мене думати, що DISTINCTв інших запитах це необхідно.
ypercubeᵀᴹ

1
І в цьому CROSS APPLY, SELECT DISTINCT categoryмабуть, має бути ефективнішим, якщо його замінити на SELECT TOP (1) whatever. Це фактично був би іншим способом написати EXISTSпідзапит.
ypercubeᵀᴹ

@Andriy Ось чому я вчора додав замітку на основі вашого первинного коментаря: не потрібен, якщо є лише 1 рядок з прапором = 1.
Жульєн Вавассер

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аргумент.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.