sélectionner les lignes dont la condition est remplie pour le groupe (sans table temporaire)

10

Avoir la table à 3 colonnes:

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

Je veux sélectionner toutes les lignes qui ont flag = 1au moins une fois par catégorie.

Résultats attendus:

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

Il peut être résolu en utilisant une table temporaire comme celle-ci:

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

Mais je préférerais une solution avec le regroupement, que j'ai du mal à trouver. Toute aide serait appréciée.

Piotr Falkowski
la source

Réponses:

16

GROUP BYne peut pas être utilisé seul car il ne renvoie qu'une ligne par groupe ( category).


  • Vous pouvez utiliser une sous-requête avec flag = 1et 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 ;
  • Vous pouvez utiliser la EXISTSclause:

    SELECT d.ID, d.category, d.flag
    FROM data d
    WHERE EXISTS (
        SELECT 1 FROM data WHERE flag = 1 AND category = d.category
    ) ;   
  • Vous pouvez utiliser la INclause (bien que ce EXISTSsoit mieux):

    SELECT d.ID, d.category, d.flag
    FROM data d
    WHERE d.category IN (SELECT category FROM data WHERE flag = 1) ;
  • Vous pouvez également utiliser CROSS APPLYune sous-requête sur 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 ;

DISTINCTne sont pas nécessaires si, pour chaque catégorie, une seule ligne peut avoir flag = 1.

Production:

ID  category    flag
1       A       1
2       A       0
3       A       0
Julien Vavasseur
la source
DISTINCT n'est pas nécessaire pour le prédicat IN. Et si une seule ligne par catégorie peut avoir le drapeau 1, DISTINCT est inutile du tout.
Andriy M du
@AndriyM corrige la INrequête. Mais l'OP a " Je veux sélectionner toutes les lignes qui ont flag = 1 au moins une fois par catégorie ", ce qui me fait penser que DISTINCTc'est nécessaire dans les autres requêtes.
ypercubeᵀᴹ
1
Et dans le CROSS APPLY, le SELECT DISTINCT categorydevrait probablement être plus efficace s'il est remplacé par SELECT TOP (1) whatever. Ce serait effectivement une autre façon d'écrire une EXISTSsous-requête.
ypercubeᵀᴹ
@Andriy C'est pourquoi j'ai ajouté une note hier sur la base de votre commentaire initial: pas nécessaire s'il n'y a qu'une seule ligne avec flag = 1.
Julien Vavasseur
4

En supposant qu'il Flags'agit d'une BITcolonne ou d'une colonne INTqui prend uniquement 0et en 1tant que valeurs, cela peut également être réalisé à l'aide de fonctions fenêtrées. Par exemple:

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;

Voilà la sortie:

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

Cela trouvera le plus élevé Flagpour chaque catégorie de votre tableau, dans votre cas, c'est probablement vrai / faux seulement et choisissez celui qui a true(1)seulement.

La conversion en TINYINTest nécessaire car MAXn'accepte pas d' BITargument.

Evaldas Buinauskas
la source