PostgreSQL: Operações de Conjunto e Consultas Combinadas no…

Última atualização: 2026-08-26

1. O Que Você Vai Aprender


2. A História

Bob é um analista de dados em uma plataforma de e-commerce. O CEO faz três perguntas a ele:

  1. Quais produtos foram mais vendidos no T1, T2 e T3? (UNION ALL para mesclar)
  2. Quais produtos foram mais vendidos em todos os trimestres? (INTERSECT = perenes)
  3. Quais produtos venderam bem no T1 mas não no T2? (EXCEPT = perdidos)

Bob percebe que essas perguntas se mapeiam exatamente para as três operações de conjunto do SQL: UNION, INTERSECT, EXCEPT.


3. Conceito

(1) Visão Geral das Operações de Conjunto

Operação Significado Desduplica Analogia
UNION Mesclar conjuntos de resultados Sim A ∪ B
UNION ALL Mesclar conjuntos de resultados Não A ∪ B (com duplicatas)
INTERSECT Interseção Sim A ∩ B
INTERSECT ALL Interseção Não A ∩ B (com contagens de duplicatas)
EXCEPT Em A mas não em B Sim A - B
EXCEPT ALL Em A mas não em B Não A - B (com contagens de duplicatas)
100%
flowchart TD
    subgraph Union
        U1((A)) --- U2((B))
        U1 & U2 --> U3["A ∪ B"]
    end
    subgraph Intersect
        I1((A)) --- I2((B))
        I1 ∩ I2 --> I3["A ∩ B"]
    end
    subgraph Except
        E1((A)) --- E2((B))
        E1 - E2 --> E3["A - B"]
    end

    style U3 fill:#c8e6c9
    style I3 fill:#e1f5fe
    style E3 fill:#fff9c4

(2) UNION e UNION ALL

▶ Exemplo: UNION Mescla Mais Vendidos de Três Trimestres

SQL
SELECT product_id, product_name FROM hot_products_q1
UNION
SELECT product_id, product_name FROM hot_products_q2
UNION
SELECT product_id, product_name FROM hot_products_q3
ORDER BY product_id;

Output:

TEXT 📖 Somente leitura
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

UNION desduplica automaticamente: se um produto é mais vendido tanto no T1 quanto no T2, ele aparece apenas uma vez.

▶ Exemplo: UNION ALL Preserva Duplicatas

SQL
SELECT product_id, product_name, 'Q1' AS quarter FROM hot_products_q1
UNION ALL
SELECT product_id, product_name, 'Q2' AS quarter FROM hot_products_q2
UNION ALL
SELECT product_id, product_name, 'Q3' AS quarter FROM hot_products_q3
ORDER BY quarter, product_id;
TEXT 📖 Somente leitura
 product_id | product_name | quarter
------------+--------------+---------
        101 | Widget Pro   | Q1
        102 | Gadget Mini  | Q1
        101 | Widget Pro   | Q2
        103 | Server Rack  | Q2
        101 | Widget Pro   | Q3
        104 | Cable Max    | Q3
Cenário Recomendado Motivo
Mesclar fontes diferentes sem duplicatas UNION ALL Sem necessidade de desduplicação, mais rápido
Mesclar com possíveis duplicatas que devem ser removidas UNION Desduplica automaticamente
Mesclar e identificar a fonte UNION ALL + coluna de identificação Mantém duplicatas e distingue fontes

▶ Exemplo: UNION ALL para Mesclar Estatísticas de Múltiplas Tabelas

SQL
SELECT 'NA' AS region, COUNT(*) AS order_count, SUM(amount) AS total FROM orders_na
UNION ALL
SELECT 'EU', COUNT(*), SUM(amount) FROM orders_eu
UNION ALL
SELECT 'APAC', COUNT(*), SUM(amount) FROM orders_apac;

Output:

TEXT 📖 Somente leitura
 count 
-------
     5
(1 row)

(3) INTERSECT e INTERSECT ALL

▶ Exemplo: Encontrar Produtos Perenes que Venderam Bem em Todos os Três Trimestres

SQL
SELECT product_id, product_name FROM hot_products_q1
INTERSECT
SELECT product_id, product_name FROM hot_products_q2
INTERSECT
SELECT product_id, product_name FROM hot_products_q3;
TEXT 📖 Somente leitura
 product_id | product_name
------------+--------------
        101 | Widget Pro

Widget Pro é o único produto perene que foi mais vendido em todos os trimestres.

▶ Exemplo: INTERSECT ALL Preserva Contagens de Duplicatas

Suponha que o produto 101 apareça 2 vezes na lista de mais vendidos do T1, 1 vez no T2 e 3 vezes no T3:

SQL
SELECT product_id FROM hot_products_q1_detail
INTERSECT ALL
SELECT product_id FROM hot_products_q2_detail;

Output:

TEXT 📖 Somente leitura
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

INTERSECT ALL retorna MIN(contagem de ocorrências): o produto 101 retorna min(2, 1) = 1 linha.

Operação Desduplica Tratamento de linhas duplicadas Uso típico
INTERSECT Sim Mantém apenas uma linha Encontrar itens comuns
INTERSECT ALL Não Mantém pela contagem mínima de ocorrências Corresponder frequência de duplicatas exatamente

▶ Exemplo: INTERSECT Encontra Clientes Comuns a Múltiplos Anos

SQL
SELECT customer_id FROM orders WHERE EXTRACT(YEAR FROM created_at) = 2023
INTERSECT
SELECT customer_id FROM orders WHERE EXTRACT(YEAR FROM created_at) = 2024
INTERSECT
SELECT customer_id FROM orders WHERE EXTRACT(YEAR FROM created_at) = 2025;

Output:

TEXT 📖 Somente leitura
CREATE TABLE

Clientes fiéis que fizeram pedidos em todos os três anos consecutivos.

(4) EXCEPT e EXCEPT ALL

▶ Exemplo: Mais Vendidos do T1 que Não Apareceram no T2 (Perdidos)

SQL
SELECT product_id, product_name FROM hot_products_q1
EXCEPT
SELECT product_id, product_name FROM hot_products_q2;
TEXT 📖 Somente leitura
 product_id | product_name
------------+--------------
        102 | Gadget Mini

Gadget Mini foi mais vendido no T1 mas não apareceu na lista do T2—ele foi perdido.

▶ Exemplo: Novos Mais Vendidos Promovidos no T2

SQL
SELECT product_id, product_name FROM hot_products_q2
EXCEPT
SELECT product_id, product_name FROM hot_products_q1;
TEXT 📖 Somente leitura
 product_id | product_name
------------+--------------
        103 | Server Rack

Server Rack entrou recentemente na lista de mais vendidos no T2.

▶ Exemplo: EXCEPT ALL Preserva Contagens de Duplicatas

SQL
SELECT product_id FROM order_items_2024
EXCEPT ALL
SELECT product_id FROM order_items_2025;

Output:

TEXT 📖 Somente leitura
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

Se o produto 101 aparecer 5 vezes em 2024 e 3 vezes em 2025, EXCEPT ALL retorna 5 - 3 = 2 linhas.

Operação Desduplica Tratamento de linhas duplicadas Uso típico
EXCEPT Sim Mantém apenas uma linha Encontrar diferenças
EXCEPT ALL Não Mantém pela diferença de contagem de ocorrências Calcular contagem exata de excedente

4. Pontos-Chave

(1) Regras para Operações de Conjunto

Regra um: o número de colunas deve ser o mesmo.

SQL
SELECT id, name FROM table_a
UNION
SELECT id, name, price FROM table_b;
TEXT 📖 Somente leitura
ERRO: cada consulta UNION deve ter o mesmo número de colunas

Regra dois: os tipos de coluna correspondentes devem ser compatíveis.

Regra Requisito Consequência da violação
Contagem de colunas Deve ser a mesma Erro de compilação
Tipo de coluna Deve ser compatível Conversão implícita ou erro
Nome da coluna Usa o nome da coluna da primeira consulta Cuidado com aliases

▶ Exemplo: Unificar Nomes de Coluna com Aliases

SQL
SELECT product_id, product_name AS name FROM products_active
UNION ALL
SELECT sku AS product_id, title AS name FROM products_legacy
ORDER BY product_id;

Output:

TEXT 📖 Somente leitura
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) Posição do ORDER BY em Operações de Conjunto

ORDER BY pode aparecer apenas após a última consulta e aplica-se a todo o conjunto de resultados.

▶ Exemplo: ORDER BY Correto

SQL
SELECT product_id, product_name FROM hot_products_q1
UNION ALL
SELECT product_id, product_name FROM hot_products_q2
ORDER BY product_id;

Output:

TEXT 📖 Somente leitura
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ Exemplo: Controlar Precedência com Parênteses

SQL
(SELECT product_id FROM hot_products_q1
 EXCEPT
 SELECT product_id FROM hot_products_q2)
UNION ALL
(SELECT product_id FROM hot_products_q2
 EXCEPT
 SELECT product_id FROM hot_products_q3)
ORDER BY product_id;

Output:

TEXT 📖 Somente leitura
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

Calcule a diferença primeiro, depois mescle. Sem parênteses, UNION tem precedência menor que INTERSECT/EXCEPT.

Operação Precedência Associatividade
INTERSECT Mais alta Esquerda para direita
EXCEPT Média Esquerda para direita
UNION / UNION ALL Média Esquerda para direita

(3) Tratamento de NULL em Operações de Conjunto

Operações de conjunto tratam NULL como igual (diferente de comparações comuns onde NULL <> NULL).

▶ Exemplo: Desduplicação de NULL no UNION

SQL
SELECT NULL AS val
UNION
SELECT NULL AS val;
TEXT 📖 Somente leitura
 val
-----
 
(1 row)

Os dois NULLs são tratados como iguais; UNION desduplica para uma única linha.

▶ Exemplo: Correspondência de NULL no INTERSECT

SQL
SELECT NULL AS val
INTERSECT
SELECT NULL AS val;
TEXT 📖 Somente leitura
 val
-----
 
(1 row)

NULL corresponde a NULL; INTERSECT retorna uma linha.

Cenário Comportamento do NULL Diferença da comparação comum
UNION Dois NULLs tratados como iguais, desduplicados NULL = NULL comum é UNKNOWN
INTERSECT Dois NULLs tratados como iguais, correspondidos NULL = NULL comum é UNKNOWN
EXCEPT Dois NULLs tratados como iguais, cancelados NULL <> NULL comum é UNKNOWN

(4) Operações de Conjunto vs JOIN

▶ Exemplo: INTERSECT Equivalente a INNER JOIN

SQL
SELECT a.product_id
FROM hot_products_q1 a
INNER JOIN hot_products_q2 b ON a.product_id = b.product_id;

Output:

TEXT 📖 Somente leitura
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

Equivalente a:

SQL
SELECT product_id FROM hot_products_q1
INTERSECT
SELECT product_id FROM hot_products_q2;
Dimensão Operação de conjunto JOIN
Semântica Operação de conjunto sobre linhas Combinação de colunas
Colunas de saída Usa as colunas do lado esquerdo Colunas de ambas as tabelas disponíveis
Desduplicação UNION/INTERSECT/EXCEPT desduplicam automaticamente Precisa de DISTINCT manual
Correspondência NULL NULL = NULL NULL <> NULL
Desempenho Dados grandes podem ordenar para desduplicar Hash Join indexado pode ser mais rápido
Melhor para Mesclar/interceptar/diferenciar conjuntos de resultados com mesma estrutura Unir tabelas heterogêneas para buscar colunas

5. Prática

▶ Exemplo: UNION ALL para Mesclar Pedidos e Livro de Reembolsos

SQL
SELECT
  order_id   AS transaction_id,
  amount     AS credit,
  0          AS debit,
  'order'    AS type,
  created_at
FROM orders
UNION ALL
SELECT
  refund_id,
  0,
  refund_amount,
  'refund',
  created_at
FROM refunds
ORDER BY created_at;

Output:

TEXT 📖 Somente leitura
CREATE TABLE

▶ Exemplo: EXCEPT Encontra Usuários Registrados mas Não Ativados

SQL
SELECT user_id, email FROM registered_users
EXCEPT
SELECT user_id, email FROM activated_users;

Output:

TEXT 📖 Somente leitura
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ Exemplo: INTERSECT Encontra Clientes que Compraram Ambos A e B

SQL
SELECT customer_id FROM order_items WHERE product_id = 101
INTERSECT
SELECT customer_id FROM order_items WHERE product_id = 102;

Output:

TEXT 📖 Somente leitura
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ Exemplo: Análise de Retenção de Clientes em Três Anos

SQL
SELECT 'retained' AS status, COUNT(*) AS cnt FROM (
  SELECT customer_id FROM orders WHERE EXTRACT(YEAR FROM created_at) = 2024
  INTERSECT
  SELECT customer_id FROM orders WHERE EXTRACT(YEAR FROM created_at) = 2025
) t
UNION ALL
SELECT 'churned', COUNT(*) FROM (
  SELECT customer_id FROM orders WHERE EXTRACT(YEAR FROM created_at) = 2024
  EXCEPT
  SELECT customer_id FROM orders WHERE EXTRACT(YEAR FROM created_at) = 2025
) t;

Output:

TEXT 📖 Somente leitura
 count 
-------
     5
(1 row)

▶ Exemplo: UNION ALL + GROUP BY para Resumo de Tendências

SQL
SELECT
  product_id,
  SUM(CASE WHEN quarter = 'Q1' THEN 1 ELSE 0 END) AS q1_count,
  SUM(CASE WHEN quarter = 'Q2' THEN 1 ELSE 0 END) AS q2_count,
  SUM(CASE WHEN quarter = 'Q3' THEN 1 ELSE 0 END) AS q3_count
FROM (
  SELECT product_id, 'Q1' AS quarter FROM hot_products_q1
  UNION ALL
  SELECT product_id, 'Q2' FROM hot_products_q2
  UNION ALL
  SELECT product_id, 'Q3' FROM hot_products_q3
) combined
GROUP BY product_id
ORDER BY product_id;

Output:

TEXT 📖 Somente leitura
 count 
-------
     5
(1 row)

6. Exemplo Completo

Análise de mais vendidos entre trimestres de Bob—uma saída para produtos perenes, novos e perdidos:

SQL
WITH q1 AS (
  SELECT product_id, product_name FROM hot_products WHERE quarter = 'Q1'
),
q2 AS (
  SELECT product_id, product_name FROM hot_products WHERE quarter = 'Q2'
),
q3 AS (
  SELECT product_id, product_name FROM hot_products WHERE quarter = 'Q3'
),
evergreen AS (
  SELECT product_id, product_name, 'evergreen' AS trend FROM q1
  INTERSECT
  SELECT product_id, product_name, 'evergreen' FROM q2
  INTERSECT
  SELECT product_id, product_name, 'evergreen' FROM q3
),
new_q2 AS (
  SELECT product_id, product_name, 'new_in_q2' AS trend FROM q2
  EXCEPT
  SELECT product_id, product_name, 'new_in_q2' FROM q1
),
new_q3 AS (
  SELECT product_id, product_name, 'new_in_q3' AS trend FROM q3
  EXCEPT
  SELECT product_id, product_name, 'new_in_q3' FROM q2
),
churned_q2 AS (
  SELECT product_id, product_name, 'churned_in_q2' AS trend FROM q1
  EXCEPT
  SELECT product_id, product_name, 'churned_in_q2' FROM q2
),
churned_q3 AS (
  SELECT product_id, product_name, 'churned_in_q3' AS trend FROM q2
  EXCEPT
  SELECT product_id, product_name, 'churned_in_q3' FROM q3
)
SELECT * FROM evergreen
UNION ALL
SELECT * FROM new_q2
UNION ALL
SELECT * FROM new_q3
UNION ALL
SELECT * FROM churned_q2
UNION ALL
SELECT * FROM churned_q3
ORDER BY trend, product_id;
TEXT 📖 Somente leitura
 product_id | product_name |    trend
------------+--------------+---------------
        101 | Widget Pro   | evergreen
        103 | Server Rack  | new_in_q2
        104 | Cable Max    | new_in_q3
        102 | Gadget Mini  | churned_in_q2
        103 | Server Rack  | churned_in_q3

7. Fluxo de Execução das Operações de Conjunto

100%
flowchart TD
    A["Consulta A"] --> C{Operação}
    B["Consulta B"] --> C
    C -->|UNION| D["Combinar + Desduplicar"]
    C -->|UNION ALL| E["Combinar (manter duplicatas)"]
    C -->|INTERSECT| F["Corresponder + Desduplicar"]
    C -->|EXCEPT| G["A - B + Desduplicar"]
    D --> H["ORDER BY (opcional)"]
    E --> H
    F --> H
    G --> H
    H --> I["Resultado Final"]

    style D fill:#c8e6c9
    style F fill:#e1f5fe
    style G fill:#fff9c4
Passo Operação Descrição
1 Executar cada subconsulta Executadas independentemente; conjuntos de resultados devem compartilhar estrutura
2 Operação de conjunto UNION/INTERSECT/EXCEPT
3 Desduplicar (se necessário) UNION/INTERSECT/EXCEPT desduplicam por padrão
4 ORDER BY Aplica-se ao conjunto de resultados final
5 LIMIT Limitar linhas de saída final

❓ Perguntas Frequentes

P: Qual é mais rápido, UNION ou UNION ALL? R: UNION ALL é mais rápido porque não desduplica. Se você tem certeza de que não há duplicatas, ou não precisa de desduplicação, prefira UNION ALL.

P: De quem é o nome da coluna que uma operação de conjunto usa? R: Usa o nome da coluna (ou alias) da primeira consulta. Para unificá-los, escreva o alias na primeira consulta.

P: Uma operação de conjunto pode ser usada em uma subconsulta? R: Sim. SELECT * FROM (A UNION B) AS t é válido; coloque entre parênteses e adicione um alias.

P: Qual a precedência de múltiplas operações de conjunto? R: INTERSECT > (EXCEPT = UNION). INTERSECT tem a precedência mais alta; EXCEPT e UNION estão no mesmo nível. Use parênteses para tornar a precedência explícita.

P: NULL é realmente igual a NULL em operações de conjunto? R: Sim. Em operações de conjunto, dois NULLs são tratados como iguais—este é o comportamento SQL padrão, diferente de comparações comuns onde NULL = NULL é UNKNOWN.

P: Quando usar uma operação de conjunto em vez de JOIN? R: Use uma operação de conjunto quando você só precisa testar "existe / não existe" e não precisa das colunas unidas; use JOIN quando precisa combinar colunas de ambas as tabelas. Operações de conjunto são mais intuitivas de ler; JOIN é mais flexível.


📖 Resumo


📝 Exercícios

  1. ⭐ Use UNION ALL para mesclar as tabelas de pedidos de 2024 e 2025, adicionando uma coluna de ano, ordenado por valor decrescente.
  2. ⭐ Use EXCEPT para encontrar usuários que estão na tabela customers mas não na tabela active_users.
  3. ⭐⭐ Use INTERSECT para encontrar IDs de produtos mais vendidos que aparecem em todos os três trimestres, e faça JOIN com a tabela products para exibir os nomes dos produtos.
  4. ⭐⭐ Use uma CTE + EXCEPT para análise de perda de clientes: clientes que tiveram pedidos em 2024 mas nenhum em 2025.
  5. ⭐⭐⭐ Escreva uma única instrução SQL combinando UNION ALL + INTERSECT + EXCEPT: produza um relatório cobrindo três categorias de produtos (perene / novo / perdido), cada uma com uma coluna de identificação, e finalmente use GROUP BY para contar os produtos em cada categoria.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%