selezionare le righe con condizione soddisfatta per il gruppo (senza tabella temporanea)


10

Avere la tabella con 3 colonne:

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

Voglio selezionare tutte le righe che hanno flag = 1almeno una volta per categoria.

Risultati aspettati:

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

Può essere risolto usando una tabella temporanea come questa:

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

Ma preferirei una soluzione con il raggruppamento, che faccio fatica a trovare. Qualsiasi aiuto sarà apprezzato.

Risposte:


16

GROUP BYnon può essere utilizzato da solo perché restituisce solo 1 riga per gruppo ( category).


  • È possibile utilizzare una query secondaria con flag = 1e 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 ;
  • Puoi usare la EXISTSclausola:

    SELECT d.ID, d.category, d.flag
    FROM data d
    WHERE EXISTS (
        SELECT 1 FROM data WHERE flag = 1 AND category = d.category
    ) ;   
  • Puoi usare la INclausola (anche se EXISTSè meglio):

    SELECT d.ID, d.category, d.flag
    FROM data d
    WHERE d.category IN (SELECT category FROM data WHERE flag = 1) ;
  • È inoltre possibile utilizzare CROSS APPLYcon una query secondaria su 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 ;

DISTINCTnon sono necessari se, per ciascuna categoria, può avere solo 1 riga flag = 1.

Produzione:

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

DISTINCT non è necessario per il predicato IN. E se solo una riga per categoria può avere il flag di 1, DISTINCT non è affatto necessario.
Andriy M,

@AndriyM è corretto sulla INquery. Ma l'OP ha " Voglio selezionare tutte le righe che hanno flag = 1 almeno una volta per categoria ", il che mi fa pensare che DISTINCTsia necessario nelle altre query.
ypercubeᵀᴹ

1
E nel CROSS APPLY, SELECT DISTINCT categoryprobabilmente dovrebbe essere più efficiente se sostituito con SELECT TOP (1) whatever. Sarebbe effettivamente un altro modo per scrivere una EXISTSsottoquery.
ypercubeᵀᴹ

@Andriy Questo è il motivo per cui ho aggiunto una nota ieri in base al tuo commento iniziale: non necessario se c'è solo 1 riga con flag = 1.
Julien Vavasseur

4

Supponendo che si Flagtratti di una BITcolonna o INTche accetta solo 0e 1come valori, ciò potrebbe essere ottenuto anche utilizzando le funzioni con finestre. Per esempio:

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;

Questo è l'output:

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

Questo troverà il massimo Flagper ogni categoria nella tua tabella, nel tuo caso è probabilmente solo vero / falso e scegli uno che ha true(1)solo.

La conversione in TINYINTè necessaria perché MAXnon accetta un BITargomento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.