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.

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

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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.