Como faço (ou posso) SELECT DISTINCT em várias colunas?
-
09-06-2019 - |
Pergunta
Preciso recuperar todas as linhas de uma tabela onde 2 colunas combinadas são diferentes.Então eu quero todas as vendas que não tenham nenhuma outra venda que aconteceu no mesmo dia pelo mesmo preço.As vendas exclusivas com base no dia e no preço serão atualizadas para um status ativo.
Então estou pensando:
UPDATE sales
SET status = 'ACTIVE'
WHERE id IN (SELECT DISTINCT (saleprice, saledate), id, count(id)
FROM sales
HAVING count = 1)
Mas meu cérebro dói indo além disso.
Solução
SELECT DISTINCT a,b,c FROM t
é aproximadamente equivalente a:
SELECT a,b,c FROM t GROUP BY a,b,c
É uma boa ideia se acostumar com a sintaxe GROUP BY, pois é mais poderosa.
Para sua consulta, eu faria assim:
UPDATE sales
SET status='ACTIVE'
WHERE id IN
(
SELECT id
FROM sales S
INNER JOIN
(
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING COUNT(*) = 1
) T
ON S.saleprice=T.saleprice AND s.saledate=T.saledate
)
Outras dicas
Se você juntar as respostas até agora, limpar e melhorar, chegará a esta pergunta superior:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
Qual é muito mais rápido do que qualquer um deles.Reduz o desempenho da resposta atualmente aceita por um fator de 10 a 15 (em meus testes no PostgreSQL 8.4 e 9.1).
Mas isto ainda está longe de ser o ideal.Use um NOT EXISTS
(anti-)semi-junção para um desempenho ainda melhor. EXISTS
é o SQL padrão, existe desde sempre (pelo menos desde o PostgreSQL 7.2, muito antes de esta pergunta ser feita) e atende perfeitamente aos requisitos apresentados:
UPDATE sales s
SET status = 'ACTIVE'
WHERE NOT EXISTS (
SELECT FROM sales s1 -- SELECT list can be empty for EXISTS
WHERE s.saleprice = s1.saleprice
AND s.saledate = s1.saledate
AND s.id <> s1.id -- except for row itself
)
AND s.status IS DISTINCT FROM 'ACTIVE'; -- avoid empty updates. see below
banco de dados<> violino aqui
Velho violino SQL
Chave exclusiva para identificar linha
Se você não tiver uma chave primária ou exclusiva para a tabela (id
no exemplo), você pode substituir pela coluna do sistema ctid
para os fins desta consulta (mas não para outros fins):
AND s1.ctid <> s.ctid
Toda tabela deve ter uma chave primária.Adicione um se você ainda não tiver um.Eu sugiro um serial
ou um IDENTITY
coluna no Postgres 10+.
Relacionado:
Como isso é mais rápido?
A subconsulta no EXISTS
anti-semi-join pode parar de avaliar assim que o primeiro idiota for encontrado (não adianta procurar mais).Para uma tabela base com poucas duplicatas, isso é apenas um pouco mais eficiente.Com muitas duplicatas isso se torna caminho mais eficiente.
Excluir atualizações vazias
Para linhas que já possuem status = 'ACTIVE'
esta atualização não mudaria nada, mas ainda inseriria uma nova versão de linha com custo total (aplicam-se pequenas exceções).Normalmente, você não quer isso.Adicionar outro WHERE
condição como demonstrada acima para evitar isso e torná-lo ainda mais rápido:
Se status
é definido NOT NULL
, você pode simplificar para:
AND status <> 'ACTIVE';
Diferença sutil no tratamento de NULL
Esta consulta (diferentemente da resposta atualmente aceita por Joel) não trata valores NULL como iguais.As duas linhas seguintes para (saleprice, saledate)
seria qualificado como "distinto" (embora parecesse idêntico ao olho humano):
(123, NULL)
(123, NULL)
Também passa em um índice exclusivo e em quase qualquer outro lugar, já que os valores NULL não são comparados iguais de acordo com o padrão SQL.Ver:
OTOH, GROUP BY
, DISTINCT
ou DISTINCT ON ()
trate os valores NULL como iguais.Use um estilo de consulta apropriado dependendo do que você deseja alcançar.Você ainda pode usar esta consulta mais rápida com IS NOT DISTINCT FROM
em vez de =
para uma ou todas as comparações para tornar a comparação NULL igual.Mais:
Se todas as colunas que estão sendo comparadas estiverem definidas NOT NULL
, não há espaço para divergências.
O problema com sua consulta é que, ao usar uma cláusula GROUP BY (o que você basicamente faz usando distinto), você só pode usar colunas agrupadas ou funções agregadas.Você não pode usar o ID da coluna porque existem valores potencialmente diferentes.No seu caso, sempre há apenas um valor por causa da cláusula HAVING, mas a maioria dos RDBMS não é inteligente o suficiente para reconhecer isso.
No entanto, isso deve funcionar (e não precisa de junção):
UPDATE sales
SET status='ACTIVE'
WHERE id IN (
SELECT MIN(id) FROM sales
GROUP BY saleprice, saledate
HAVING COUNT(id) = 1
)
Você também pode usar MAX ou AVG em vez de MIN; só é importante usar uma função que retorne o valor da coluna se houver apenas uma linha correspondente.
Quero selecionar os valores distintos de uma coluna 'GrondOfLucht', mas eles devem ser classificados na ordem fornecida na coluna 'classificação'.Não consigo obter os valores distintos de apenas uma coluna usando
Select distinct GrondOfLucht,sortering
from CorWijzeVanAanleg
order by sortering
Também fornecerá a coluna 'classificação' e como 'GrondOfLucht' AND 'classificação' não é único, o resultado será TODAS as linhas.
use o GROUP para selecionar os registros de 'GrondOfLucht' na ordem dada por 'sortering
SELECT GrondOfLucht
FROM dbo.CorWijzeVanAanleg
GROUP BY GrondOfLucht, sortering
ORDER BY MIN(sortering)
Se o seu DBMS não suporta distinção com múltiplas colunas como esta:
select distinct(col1, col2) from table
A seleção múltipla em geral pode ser executada com segurança da seguinte forma:
select distinct * from (select col1, col2 from table ) as x
Como isso pode funcionar na maioria dos DBMS e espera-se que seja mais rápido do que agrupar por solução, pois você evita a funcionalidade de agrupamento.