SQL SELECT DISTINCT
A dona da Loja Aurora quer saber: "em quais cidades nós temos clientes?". A Ana pede a coluna cidade da tabela de clientes e recebe 30 linhas, com Teresina aparecendo dez vezes. A resposta está ali, mas escondida no meio das repetições. Para isso existe o DISTINCT (em inglês, "distinto", "diferente"): ele tira as linhas repetidas do resultado e deixa cada valor aparecer uma vez só.
- Por que um
SELECTcomum mostra valores repetidos. - A usar
SELECT DISTINCTpara ver cada valor uma vez só. - Como o
DISTINCTfunciona com várias colunas: ele olha a linha inteira. - O que acontece com o
NULLnumDISTINCT. - Outras formas de chegar ao mesmo resultado, e os erros mais comuns.
A ideia: a lista de cidades sem repetir
Imagine as 30 fichas de clientes da loja. Para responder "em quais cidades temos clientes?", você pegaria uma folha em branco, passaria ficha por ficha e anotaria a cidade, só se ela ainda não estiver na folha. No fim, a folha teria cada cidade uma vez.
É exatamente o que o DISTINCT faz com o resultado de um SELECT: monta a resposta normalmente e, antes de mostrar, joga fora as linhas que são iguais a uma que já apareceu.
1. O problema: valores repetidos
Primeiro, a consulta sem DISTINCT:
SELECT cidade FROM clientes;
Vieram 30 linhas, uma por cliente. Isso está certo: o SELECT mostra uma linha do resultado para cada linha da tabela. Como 10 clientes moram em Teresina, Teresina aparece 10 vezes; São Paulo e Fortaleza aparecem 7 vezes cada. Para a pergunta da dona da loja, essas repetições só atrapalham.
2. A solução: SELECT DISTINCT
Basta escrever DISTINCT logo depois do SELECT:
SELECT DISTINCT cidade FROM clientes;
- SELECT
- "Selecione", como sempre.
- DISTINCT
- "Distintos": mostre cada linha do resultado uma vez só. Vem colado no
SELECT, antes da lista de colunas. - cidade
- A coluna que você quer ver.
- FROM clientes;
- De qual tabela, e o fim do comando.
Agora são 8 linhas: a loja tem clientes em 8 cidades. A tabela clientes continua com os mesmos 30 clientes; o DISTINCT só mudou o que voltou para você.
As cidades vieram na ordem em que apareceram pela primeira vez na tabela (Teresina, do cliente 1; São Paulo, do cliente 3…). Mas o SQL não promete ordem nenhuma no resultado, com ou sem DISTINCT. Se a ordem importa (em ordem alfabética, por exemplo), peça com ORDER BY, assunto da aula ORDER BY.
3. Mais perguntas que o DISTINCT responde
Toda pergunta do tipo "quais são os … diferentes?" é um caso para o DISTINCT.
Quais cargos existem na loja?
SELECT DISTINCT cargo FROM funcionarios;
São 8 funcionários e 5 cargos. Repare que Vendedor e Vendedora aparecem separados: para o banco, são dois textos diferentes. O DISTINCT compara os valores letra por letra; ele não sabe que as duas palavras querem dizer quase a mesma coisa. Pelo mesmo motivo, 'Teresina' e 'teresina' contariam como duas cidades: no SQLite, maiúsculas e minúsculas fazem diferença nos dados (mais sobre isso em WHERE).
De quais países vêm os fornecedores?
SELECT DISTINCT pais FROM fornecedores;
Quais clientes já fizeram pelo menos um pedido?
A tabela pedidos tem 90 pedidos, e a coluna cliente_id diz de qual cliente é cada um. Quem comprou várias vezes aparece várias vezes. Com DISTINCT, cada cliente aparece uma vez:
SELECT DISTINCT cliente_id FROM pedidos;
São 26 clientes, os de número 1 a 26. Como a loja tem 30 clientes, quatro deles (27, 28, 29 e 30) ainda não fizeram nenhum pedido. Descobrir quem são eles pelo nome é trabalho para a aula LEFT JOIN.
Missão: a loja manda catálogos por estado. De quais estados são os clientes? Mostre cada estado uma vez só.
SELECT estado FROM clientes;
SELECT DISTINCT estado FROM clientes;
4. DISTINCT com várias colunas
Esta é a parte que mais confunde. Com mais de uma coluna, o DISTINCT não olha cada coluna separada: ele olha a linha inteira. Duas linhas só são "repetidas" se forem iguais em todas as colunas pedidas.
A Ana quer saber, em cada estado, se há clientes VIP (vip = 1), comuns (vip = 0) ou os dois:
SELECT DISTINCT estado, vip FROM clientes;
Vieram 7 linhas. O PI aparece duas vezes, PI|1 e PI|0, e não é repetição: as duas linhas são diferentes na coluna vip. Lendo o resultado: PI, SP e CE têm clientes VIP e comuns; PE só tem clientes comuns (por isso não há a linha PE|1, e são 7, não 8).
Linhas do resultado sem DISTINCT | São repetidas? |
|---|---|
PI|0 e PI|0 | Sim: iguais nas duas colunas. Fica uma. |
PI|0 e PI|1 | Não: o estado é igual, mas o vip é diferente. Ficam as duas. |
PI|0 e CE|0 | Não: o vip é igual, mas o estado é diferente. Ficam as duas. |
Quando o DISTINCT não tira nada
Se uma das colunas pedidas nunca se repete, nenhuma linha é igual a outra, e o DISTINCT não tem o que tirar. O id de cada cliente é único, então:
SELECT DISTINCT id, cidade FROM clientes;
Continuam as 30 linhas, com Teresina repetida, porque cada linha tem um id diferente. Para ver as cidades sem repetição, peça só a cidade.
Missão: em quais cidades ficam os fornecedores da loja? Esta consulta trouxe uma cidade repetida (Teresina aparece duas vezes) porque pede também o nome. Mostre cada cidade uma vez só.
SELECT DISTINCT nome, cidade FROM fornecedores;
SELECT DISTINCT cidade FROM fornecedores;
5. DISTINCT e o NULL
Lembra dos fornecedores estrangeiros, que não têm estado (NULL)? Veja o que o DISTINCT faz com eles:
SELECT DISTINCT estado FROM fornecedores;
Os dois NULL (de Portugal e da Argentina) viraram uma linha só. Para o DISTINCT, todos os "sem valor" contam como iguais entre si. O mesmo acontece com os 3 clientes sem email:
SELECT DISTINCT email FROM clientes;
São 28 linhas: os 27 emails diferentes e uma linha NULL no lugar dos três clientes sem email. Guarde isso: em outras partes do SQL o NULL se comporta de outro jeito, e a aula O Valor NULL explica cada caso.
Formas diferentes de fazer a mesma coisa
DISTINCT × GROUP BY
Mais adiante você vai aprender o GROUP BY, que junta as linhas em grupos. Pedir os grupos de cidade dá a mesma lista de cidades:
SELECT cidade FROM clientes GROUP BY cidade;
As mesmas 8 cidades (aqui, por acaso, em ordem alfabética; de novo, só o ORDER BY garante a ordem). O GROUP BY serve para muito mais coisas, como contar quantos clientes há em cada cidade; é a aula GROUP BY.
Contar quantos são os diferentes
E se a pergunta for "quantas cidades?", e não "quais"? Dá para contar sem listar, com a função COUNT (aula COUNT()). Uma prévia:
SELECT COUNT(cidade), COUNT(DISTINCT cidade) FROM clientes;
A primeira conta conta as cidades com repetição (30, uma por cliente); a segunda conta só as diferentes (8).
ALL: o contrário de DISTINCT
Existe também SELECT ALL ("todos"), que mantém as repetições. Como esse já é o comportamento normal, quase ninguém escreve: SELECT ALL cidade FROM clientes; é igual a SELECT cidade FROM clientes;, com as 30 linhas.
| Pergunta | Use |
|---|---|
| Quais são os valores diferentes? | SELECT DISTINCT cidade FROM clientes; |
| Quantos são os valores diferentes? | SELECT COUNT(DISTINCT cidade) FROM clientes; |
| Quantos clientes há em cada cidade? | GROUP BY (aula GROUP BY) |
| Quero todas as linhas, com repetição | SELECT cidade FROM clientes; (o ALL é o normal) |
Quando dá errado
DISTINCT no meio da lista
A Ana queria todas as cidades e, ao lado, os estados sem repetir:
SELECT cidade, DISTINCT estado FROM clientes;
near "DISTINCT": syntax error. O DISTINCT não vale para uma coluna só: ele vale para a linha inteira, e por isso tem um lugar fixo, logo depois do SELECT. No meio da lista, o banco não sabe o que fazer com ele. Como consertar: SELECT DISTINCT cidade, estado FROM clientes;, sabendo que o DISTINCT vai olhar as duas colunas juntas.
DISTINCT
Outra tentativa comum é pôr a coluna entre parênteses, como se o DISTINCT fosse valer só para ela:
SELECT DISTINCT (cidade), estado FROM clientes;
Não dá erro, mas também não faz o que parece. O parêntese em volta de cidade não muda nada (é como escrever (2) em vez de 2 numa conta), e o DISTINCT continua olhando a linha inteira, cidade e estado. O resultado é igual ao de SELECT DISTINCT cidade, estado.
DISTINCT "não funcionou"
SELECT DISTINCT nome, cidade FROM clientes;
Vieram as 30 linhas, e Teresina continua repetida. O DISTINCT funcionou, sim: não existem dois clientes com o mesmo nome e a mesma cidade, então não havia linha repetida para tirar. Como consertar: peça só as colunas que você quer sem repetição. Se o DISTINCT "não fez nada", olhe se a lista tem uma coluna que quase nunca se repete, como id, nome ou email.
SELECT DISTINCT FROM clientes;
near "FROM": syntax error, o mesmo erro de um SELECT FROM sem nada no meio: depois do DISTINCT o banco esperava a lista de colunas.
O SELECT DISTINCT desta aula funciona igual no MySQL, no PostgreSQL e no SQL Server. O PostgreSQL tem, além dele, uma extensão própria, o DISTINCT ON, que não faz parte do SQL padrão e não existe no SQLite. Ele guarda só a primeira linha de cada grupo de valores iguais nas colunas entre parênteses, e é usado junto com ORDER BY. Um exemplo, só para ilustrar (não roda no SQLite):
SELECT DISTINCT ON (cidade) cidade, nome FROM clientes ORDER BY cidade, nome;
No PostgreSQL, isso mostraria uma linha por cidade, com o primeiro nome em ordem alfabética de cada uma.
Referência rápida
| Escrita | O que faz |
|---|---|
SELECT DISTINCT coluna FROM tabela; | Cada valor da coluna uma vez só |
SELECT DISTINCT col1, col2 FROM tabela; | Cada combinação de col1 e col2 uma vez só |
SELECT ALL coluna FROM tabela; | Com repetições (o normal; o ALL é opcional) |
SELECT COUNT(DISTINCT coluna) FROM tabela; | Quantos valores diferentes existem |
NULL no DISTINCT | Todos os NULL viram uma linha só |
Exercícios
1. Sem repetir
Complete para mostrar cada cargo uma vez só:
2. Quantas linhas vêm em SELECT DISTINCT estado FROM clientes;, se os 30 clientes são dos estados PI, SP, CE e PE?
3. Em SELECT DISTINCT estado, vip FROM clientes;, as linhas PI|0 e PI|1…
4. Onde o DISTINCT deve ser escrito?
5. A tabela tem 3 clientes sem email (NULL). No resultado de SELECT DISTINCT email FROM clientes;, quantas linhas NULL aparecem?
Missão final: quem já vendeu?
Missão: a tabela pedidos tem a coluna funcionario_id, com o número do funcionário que atendeu cada pedido. A gerente quer saber quais funcionários já atenderam algum pedido. Mostre só essa coluna, cada número uma vez.
SELECT funcionario_id FROM pedidos;
SELECT DISTINCT funcionario_id FROM pedidos;
Dos 8 funcionários, 6 aparecem (os números 2 a 7). Faltam a gerente, Marta (1), e o estoquista, Davi (8), que não atendem pedidos. Confira com SELECT * FROM funcionarios;.
Resumo
- Um
SELECTcomum mostra uma linha para cada linha da tabela, com repetições. SELECT DISTINCT("distintos") tira as linhas repetidas do resultado: cada valor aparece uma vez. A tabela não muda.- O
DISTINCTvem logo depois doSELECTe vale para a linha inteira: com várias colunas, só some a linha igual em todas elas. - Com uma coluna que nunca se repete (como
id), oDISTINCTnão tem o que tirar. - Os
NULLviram uma linha só. Textos são comparados exatamente:VendedoreVendedorasão diferentes. - O SQL não promete a ordem do resultado; para ordenar, use
ORDER BY.