selecionar linhas com a condição atendida para o grupo (sem tabela temporária)

10

Tendo a tabela com 3 colunas:

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

Quero selecionar todas as linhas que tenham flag = 1pelo menos uma vez por categoria.

Resultados esperados:

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

Pode ser resolvido usando uma tabela temporária como esta:

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

Mas eu prefiro uma solução com o agrupamento, que luto para encontrar. Qualquer ajuda será apreciada.

Piotr Falkowski
fonte

Respostas:

16

GROUP BYnão pode ser usado sozinho porque retorna apenas 1 linha por grupo ( category).


  • Você pode usar uma subconsulta com 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 ;
  • Você pode usar a 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
    ) ;   
  • Você pode usar a INcláusula (embora EXISTSseja melhor):

    SELECT d.ID, d.category, d.flag
    FROM data d
    WHERE d.category IN (SELECT category FROM data WHERE flag = 1) ;
  • Você também pode usar CROSS APPLYcom uma subconsulta em 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 ;

DISTINCTnão é necessário se, para cada categoria, apenas uma linha puder ter flag = 1.

Resultado:

ID  category    flag
1       A       1
2       A       0
3       A       0
Julien Vavasseur
fonte
DISTINCT é desnecessário para o predicado IN. E se apenas uma linha por categoria pode ter o sinalizador 1, DISTINCT é desnecessário.
Andriy M
@AndriyM correto sobre a INconsulta. Mas o OP tem " Desejo selecionar todas as linhas que possuem sinalizador = 1 pelo menos uma vez por categoria ", o que me faz pensar que isso DISTINCTé necessário nas outras consultas.
ypercubeᵀᴹ
11
E no CROSS APPLY, SELECT DISTINCT categoryprovavelmente deve ser mais eficiente se substituído por SELECT TOP (1) whatever. Seria efetivamente outra maneira de escrever uma EXISTSsubconsulta.
ypercubeᵀᴹ
@Andriy É por isso que eu adicionei uma nota ontem com base no seu comentário inicial: não necessária se houver apenas 1 linha com flag = 1.
Julien Vavasseur
4

Supondo que Flagseja uma BITcoluna ou uma INTque use apenas 0e 1como valores, isso também pode ser alcançado usando funções em janelas. Por exemplo:

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;

Essa é a saída:

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

Isso será mais alto Flagpara cada categoria na sua tabela; no seu caso, provavelmente é apenas verdadeiro / falso e escolha alguém que tenha true(1)apenas.

A conversão para TINYINTé necessária porque MAXnão aceita um BITargumento.

Evaldas Buinauskas
fonte