выбрать строки с условием для группы (без временной таблицы)

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запросе. Но у 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 / false и выберите только одну из них true(1).

Преобразование в TINYINTнеобходимо, потому MAXчто не принимает BITаргумент.

Эвалдас Буйнаускас
источник