SQL UNION ALL
Na aula anterior, o UNION juntou duas listas e riscou as repetições. Às vezes, riscar é exatamente o que você não quer. Se a Ana quer saber quantos contatos a loja tem em cada cidade, cada cliente e cada fornecedor tem de contar, mesmo que vários morem em Teresina. O UNION ALL ("união de tudo") empilha as consultas sem tirar nada.
- A diferença entre
UNIONeUNION ALL, com os mesmos dados lado a lado. - Por que contar e somar em cima de um
UNIONdá números errados, sem erro nenhum. - Como pôr uma linha de TOTAL no fim de um relatório.
- Por que o
UNION ALLdá menos trabalho ao banco. - O que acontece quando você mistura
UNIONeUNION ALLno mesmo comando. - Os erros reais do SQLite com
UNION ALL.
As regras de colunas são as mesmas do UNION: o mesmo número de colunas em todas as consultas, encaixadas pela posição, com os nomes da primeira consulta e o ORDER BY só no fim. Se ainda não leu, comece pela aula UNION.
A ideia: a lista de convidados × a lista de compras
Na lista de convidados da festa, ninguém precisa aparecer duas vezes: um convite por pessoa. Isso é o UNION.
Agora pense na lista de compras. A Ana anotou "2 pacotes de café" e o irmão, na lista dele, também anotou "2 pacotes de café". Se ela juntar as duas listas riscando o repetido, vai comprar 2 pacotes em vez de 4. Na lista de compras, cada linha conta. Isso é o UNION ALL.
UNION | UNION ALL | |
|---|---|---|
| Linhas repetidas | Tira | Mantém |
| Número de linhas | Menor ou igual à soma das partes | Sempre a soma exata das partes |
| Trabalho do banco | Precisa comparar as linhas para achar as repetidas | Só empilha |
| Na vida real | Lista de convidados | Lista de compras |
1. Os mesmos dados, com e sem ALL
A consulta da aula anterior, agora com ALL:
SELECT cidade FROM clientes UNION ALL SELECT cidade FROM fornecedores;
- UNION ALL
- "Empilhe o resultado da próxima consulta embaixo deste, do jeito que vier." São duas palavras, nessa ordem: o
ALLvem logo depois doUNION.
Agora são 36 linhas: as 30 cidades dos 30 clientes, seguidas das 6 cidades dos 6 fornecedores. Teresina aparece 12 vezes. Com UNION, a mesma consulta dava 10 linhas, uma por cidade.
Repare também na ordem: o UNION ALL não mexeu em nada, e as linhas dos clientes vieram primeiro, na ordem em que estão na tabela, e as dos fornecedores depois. Mesmo assim, se a ordem importa, peça com ORDER BY no fim: nenhum banco promete ordem sem ele.
2. Contando: aqui o ALL faz falta
A Ana quer saber quantos contatos (clientes e fornecedores) a loja tem em cada cidade. A ideia é juntar as duas listas de cidades e contar com GROUP BY (aula GROUP BY). A lista unida vai entre parênteses, no lugar de uma tabela, no FROM:
SELECT cidade, COUNT(*) AS contatos FROM ( SELECT cidade FROM clientes UNION ALL SELECT cidade FROM fornecedores ) GROUP BY cidade ORDER BY contatos DESC, cidade;
- FROM ( … )
- O resultado do
UNION ALLvira uma "tabela" temporária, de onde a consulta de fora lê. Uma consulta dentro de outra se chama subconsulta e tem aula própria: Subconsultas. - GROUP BY cidade
- Agrupa as 36 linhas por cidade.
- COUNT(*) AS contatos
- Conta quantas linhas caíram em cada grupo.
Teresina tem 12 contatos (10 clientes e 2 fornecedores), Fortaleza e São Paulo têm 8 cada uma. A soma da coluna dá 36, um para cada pessoa ou empresa. Agora troque o UNION ALL por UNION e veja o estrago:
SELECT cidade, COUNT(*) AS contatos FROM ( SELECT cidade FROM clientes UNION SELECT cidade FROM fornecedores ) GROUP BY cidade ORDER BY contatos DESC, cidade;
Todas as cidades com 1 contato. O UNION deixou uma linha por cidade antes de o COUNT contar, e o banco não deu nenhum aviso. A consulta está "certa" para o SQL e errada para a Ana.
Missão: a consulta abaixo diz que Teresina tem 1 contato só, e a loja tem 12 lá. Conserte-a para que cada cliente e cada fornecedor seja contado.
SELECT cidade, COUNT(*) AS contatos FROM ( SELECT cidade FROM clientes UNION SELECT cidade FROM fornecedores ) GROUP BY cidade ORDER BY contatos DESC;
SELECT cidade, COUNT(*) AS contatos FROM ( SELECT cidade FROM clientes UNION ALL SELECT cidade FROM fornecedores ) GROUP BY cidade ORDER BY contatos DESC;
3. Somando: o frete que sumiu
O mesmo perigo vale para SUM (aula SUM()). A Ana quer o total de frete cobrado em 2024 e em 2025. Ela juntou os fretes dos dois anos e somou:
SELECT SUM(frete) AS total_frete FROM ( SELECT frete FROM pedidos WHERE data LIKE '2024%' UNION SELECT frete FROM pedidos WHERE data LIKE '2025%' );
Deu 116.25. Parece pouco para 64 pedidos, e é: a loja cobra poucos valores de frete diferentes (9.9, 15.5, 0.0…), e o UNION deixou um de cada. Sobraram só 8 linhas para somar. Com UNION ALL, cada pedido conta:
SELECT SUM(frete) AS total_frete FROM ( SELECT frete FROM pedidos WHERE data LIKE '2024%' UNION ALL SELECT frete FROM pedidos WHERE data LIKE '2025%' );
930.0, o valor certo. Para conferir, a mesma soma direto na tabela, sem UNION nenhum:
SELECT SUM(frete) AS total_frete FROM pedidos WHERE data < '2026';
Se o resultado de um UNION vai ser contado ou somado, ele quase sempre tem de ser UNION ALL. O UNION compara as linhas pelo valor; ele não sabe que dois fretes de 9.9 são de pedidos diferentes.
4. A linha de TOTAL no fim do relatório
Um uso clássico do UNION ALL: o relatório com uma linha por categoria e, embaixo, uma linha de total. A primeira consulta faz as linhas de cada categoria (com INNER JOIN e GROUP BY); a segunda faz uma linha só, com a soma de tudo:
SELECT c.nome AS categoria, SUM(p.estoque) AS estoque FROM produtos AS p INNER JOIN categorias AS c ON c.id = p.categoria_id GROUP BY c.id, c.nome UNION ALL SELECT 'TOTAL', SUM(estoque) FROM produtos;
Oito categorias e o TOTAL de 3301 unidades em estoque. As duas consultas têm 2 colunas: na segunda, o texto fixo 'TOTAL' ocupa o lugar do nome da categoria. Por que ALL aqui? Porque não existe repetição possível, e o UNION ALL poupa o banco de procurar repetições à toa.
Se você quiser ordenar as categorias, cuidado: um ORDER BY estoque DESC no fim jogaria o TOTAL lá para cima, porque ele é o maior número. O truque é uma coluna a mais só para a ordem:
SELECT 1 AS parte, c.nome AS categoria, SUM(p.estoque) AS estoque FROM produtos AS p INNER JOIN categorias AS c ON c.id = p.categoria_id GROUP BY c.id, c.nome UNION ALL SELECT 2, 'TOTAL', SUM(estoque) FROM produtos ORDER BY parte, estoque DESC;
Primeiro todas as linhas da parte 1 (as categorias, da maior para a menor), depois a parte 2 (o total).
5. UNION ALL dá menos trabalho ao banco
Para tirar repetições, o UNION precisa guardar as linhas e compará-las. O UNION ALL só empilha. Dá para ver isso no plano que o SQLite monta antes de rodar, com EXPLAIN QUERY PLAN (aula EXPLAIN QUERY PLAN, no Nível 3). Compare a penúltima linha de cada um:
EXPLAIN QUERY PLAN SELECT cidade FROM clientes UNION SELECT cidade FROM fornecedores;
EXPLAIN QUERY PLAN SELECT cidade FROM clientes UNION ALL SELECT cidade FROM fornecedores;
Com UNION, aparece UNION USING TEMP B-TREE: o SQLite monta uma estrutura temporária (uma "árvore B") para descobrir as repetidas. Com UNION ALL, só UNION ALL. No terminal do sqlite3, o mesmo plano aparece desenhado como uma árvore, em vez de tabela:
$ sqlite3 loja.db
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
sqlite> EXPLAIN QUERY PLAN SELECT cidade FROM clientes UNION SELECT cidade FROM fornecedores;
QUERY PLAN
`--COMPOUND QUERY
|--LEFT-MOST SUBQUERY
| `--SCAN clientes
`--UNION USING TEMP B-TREE
`--SCAN fornecedores
Com 36 linhas, a diferença não se sente; com milhões, sente. Por isso, muita gente experiente escreve UNION ALL por padrão e só usa UNION quando precisa mesmo tirar repetições.
6. Qual usar
| Situação | Use | Por quê |
|---|---|---|
| Uma lista de nomes ou cidades, cada um uma vez | UNION | Você quer as repetições fora |
O resultado vai ser contado (COUNT) ou somado (SUM, AVG) | UNION ALL | Cada linha tem de contar |
| Linha de TOTAL, contagem de cada tabela, partes que nunca se repetem | UNION ALL | Não há o que tirar, e ele é mais leve |
Uma lista com coluna de origem ('cliente', 'fornecedor') | Qualquer um | A coluna de origem já deixa as linhas diferentes; UNION ALL é mais leve |
Mais um exemplo do terceiro caso, um "raio X" do banco com o número de linhas de cada tabela:
SELECT 'clientes' AS tabela, COUNT(*) AS linhas FROM clientes UNION ALL SELECT 'produtos', COUNT(*) FROM produtos UNION ALL SELECT 'pedidos', COUNT(*) FROM pedidos;
Quando dá errado
UNION
É o erro das seções 2 e 3, e o mais perigoso, porque não aparece mensagem nenhuma: as cidades com 1 contato cada, o frete de 116.25 em vez de 930.0.
Como perceber: desconfie de números pequenos demais e confira por outro caminho (como o SUM direto na tabela da seção 3).
Como consertar: UNION ALL.
SELECT nome FROM clientes UNION ALL SELECT nome, cidade FROM fornecedores;
A mensagem: SELECTs to the left and right of UNION ALL do not have the same number of result columns, a mesma do UNION, agora com o nome UNION ALL: "os SELECTs à esquerda e à direita do UNION ALL não têm o mesmo número de colunas".
Como consertar: o mesmo número de colunas em todas as consultas.
ORDER BY no meio
SELECT nome FROM clientes ORDER BY nome UNION ALL SELECT nome FROM fornecedores;
A mensagem: ORDER BY clause should come after UNION ALL not before, "a cláusula ORDER BY deve vir depois do UNION ALL, não antes".
Como consertar: um ORDER BY só, depois da última consulta.
UNION ALL e UNION
A Ana começou com UNION ALL e, no fim, acrescentou mais uma parte com UNION:
SELECT cidade FROM clientes UNION ALL SELECT cidade FROM fornecedores UNION SELECT 'Teresina';
O que acontece: as 36 linhas viraram 10. O SQLite resolve os operadores da esquerda para a direita: primeiro empilhou tudo (36 linhas), depois aplicou o UNION com a última parte, e esse UNION tirou as repetições do resultado inteiro, não só da última parte. Um único UNION no fim desfaz todos os ALL antes dele.
Como consertar: não misture. Se é para manter as repetições, use UNION ALL em todas as junções.
UNION DISTINCT
Em alguns tutoriais você vai ver UNION DISTINCT, para deixar explícito que as repetições saem.
SELECT cidade FROM clientes UNION DISTINCT SELECT cidade FROM fornecedores;
A mensagem: near "DISTINCT": syntax error. O SQLite não conhece essa escrita.
Como consertar: escreva só UNION: ele já tira as repetições.
O UNION ALL é igual no MySQL, no PostgreSQL e no SQL Server: mantém as repetições, com as mesmas regras de colunas. O MySQL e o PostgreSQL aceitam também UNION DISTINCT, que é o mesmo que UNION. Sobre a mistura dos dois, o manual do MySQL diz o mesmo que você viu acima: um UNION (sem ALL) vale por cima de todos os UNION ALL à esquerda dele.
Referência rápida
| Escrita | O que faz |
|---|---|
SELECT … UNION ALL SELECT … | Empilha os resultados sem tirar nada; o total de linhas é a soma das partes |
SELECT … FROM (… UNION ALL …) GROUP BY … | Conta ou soma em cima da lista unida |
… UNION ALL SELECT 'TOTAL', SUM(…) FROM … | Linha de total no fim do relatório |
SELECT 1 AS parte, … UNION ALL SELECT 2, … ORDER BY parte | Mantém o total por último ao ordenar |
… UNION ALL … UNION … | Cuidado: o UNION do fim tira as repetições de tudo |
| Mensagem do SQLite | Quer dizer |
|---|---|
SELECTs to the left and right of UNION ALL do not have the same number of result columns | As consultas têm números de colunas diferentes |
ORDER BY clause should come after UNION ALL not before | O ORDER BY está no meio; mova para o fim |
near "DISTINCT": syntax error | UNION DISTINCT não existe no SQLite; use UNION |
Exercícios
1. Sem tirar nada
Complete para empilhar as cidades mantendo as repetições:
2. Quantas linhas?
A loja tem 30 clientes e 6 fornecedores. Quantas linhas saem de SELECT cidade FROM clientes UNION ALL SELECT cidade FROM fornecedores?
3. A Ana somou os fretes de 2024 e de 2025 com SUM em cima de um UNION e achou o valor baixo. Por quê?
4. Qual é a vantagem do UNION ALL quando as partes nunca se repetem (como uma linha de TOTAL)?
5. Quantas linhas saem de SELECT cidade FROM clientes UNION ALL SELECT cidade FROM fornecedores UNION SELECT 'Teresina'?
Missão final: o relatório com total
Missão: o relatório abaixo mostra o estoque de cada categoria. A Ana quer, embaixo, uma linha a mais com o texto TOTAL e a soma do estoque de todos os produtos. Acrescente essa linha com UNION ALL.
SELECT c.nome AS categoria, SUM(p.estoque) AS estoque FROM produtos AS p INNER JOIN categorias AS c ON c.id = p.categoria_id GROUP BY c.id, c.nome;
SELECT c.nome AS categoria, SUM(p.estoque) AS estoque FROM produtos AS p INNER JOIN categorias AS c ON c.id = p.categoria_id GROUP BY c.id, c.nome UNION ALL SELECT 'TOTAL', SUM(estoque) FROM produtos;
Livre: complete o "raio X" do banco com as tabelas que faltam (itens_pedido, funcionarios, categorias, fornecedores), uma linha para cada, com UNION ALL.
SELECT 'clientes' AS tabela, COUNT(*) AS linhas FROM clientes UNION ALL SELECT 'produtos', COUNT(*) FROM produtos UNION ALL SELECT 'pedidos', COUNT(*) FROM pedidos;
Resumo
UNION ALLempilha os resultados sem tirar as repetições: o total de linhas é sempre a soma das partes.- Use
UNION ALLsempre que o resultado for contado ou somado. ComUNION, valores iguais de linhas diferentes somem e o número sai errado, sem aviso. - Ele é ótimo para linhas de TOTAL e para juntar partes que nunca se repetem; e dá menos trabalho ao banco, porque não procura repetições.
- As regras de colunas e do
ORDER BYsão as mesmas do UNION. - Não misture: um
UNIONno fim tira as repetições do resultado inteiro e desfaz osALLanteriores.