Seleccionar filas que cumplan la condición para el grupo (sin tabla temporal)

10

Tener la mesa con 3 columnas:

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

Quiero seleccionar todas las filas que tienen flag = 1al menos una vez por categoría.

Resultados previstos:

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

Se puede resolver usando una tabla temporal como esta:

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

Pero preferiría una solución con la agrupación, que me cuesta encontrar. Cualquier ayuda será apreciada.

Piotr Falkowski
fuente

Respuestas:

16

GROUP BYno se puede usar solo porque solo devuelve 1 fila por grupo ( category).


  • Puede usar una subconsulta con flag = 1y 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 ;
  • Puedes usar la EXISTScláusula:

    SELECT d.ID, d.category, d.flag
    FROM data d
    WHERE EXISTS (
        SELECT 1 FROM data WHERE flag = 1 AND category = d.category
    ) ;   
  • Puede usar la INcláusula (aunque EXISTSes mejor):

    SELECT d.ID, d.category, d.flag
    FROM data d
    WHERE d.category IN (SELECT category FROM data WHERE flag = 1) ;
  • También puede usar CROSS APPLYcon una subconsulta en 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 ;

DISTINCTno son necesarios si, para cada categoría, solo puede tener 1 fila flag = 1.

Salida:

ID  category    flag
1       A       1
2       A       0
3       A       0
Julien Vavasseur
fuente
DISTINCT es innecesario para el predicado IN. Y si solo una fila por categoría puede tener el indicador 1, DISTINCT no es necesario en absoluto.
Andriy M
@AndriyM correcto sobre la INconsulta. Pero el OP tiene " Quiero seleccionar todas las filas que tienen flag = 1 al menos una vez por categoría ", lo que me hace pensar que DISTINCTes necesario en las otras consultas.
ypercubeᵀᴹ
1
Y en el CROSS APPLY, SELECT DISTINCT categoryprobablemente debería ser más eficiente si se reemplaza con SELECT TOP (1) whatever. Efectivamente sería otra forma de escribir una EXISTSsubconsulta.
ypercubeᵀᴹ
@Andriy Es por eso que ayer agregué una nota basada en tu comentario inicial: no es necesario si solo hay 1 fila con bandera = 1.
Julien Vavasseur
4

Suponiendo que Flages una BITcolumna o una INTque solo toma 0y 1como valores, esto también podría lograrse utilizando funciones de ventana. Por ejemplo:

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;

Esa es la salida:

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

Esto encontrará el más alto Flagpara cada categoría en su tabla, en su caso es probable que solo sea verdadero / falso y elija uno que true(1)solo tenga .

La conversión a TINYINTes necesaria porque MAXno acepta un BITargumento.

Evaldas Buinauskas
fuente