Operações de Conjunto em SQL

Autor

Douglas Braga

SQL suporta operações de conjunto equivalentes às da álgebra relacional: união, interseção e diferença. Essas operações combinam os resultados de duas consultas e exigem que ambas retornem o mesmo número de colunas, com tipos compatíveis.

Os Três Operadores

Operador SQL Álgebra Relacional Descrição
UNION \(\cup\) (união) Linhas que aparecem em pelo menos uma das consultas
INTERSECT \(\cap\) (interseção) Linhas que aparecem nas duas consultas
EXCEPT \(-\) (diferença) Linhas da primeira consulta que não aparecem na segunda

Por padrão, todas as três operações eliminam duplicatas do resultado. Para manter duplicatas, adicione ALL: UNION ALL, INTERSECT ALL, EXCEPT ALL.

Exemplos com as Disciplinas da UnDF

Para demonstrar as operações, usamos dois conjuntos:

  • Conjunto A: disciplinas de Pedagogia (id_curso = 211) — 8 disciplinas
  • Conjunto B: disciplinas com carga horária ≥ 60 horas — 22 disciplinas (todas exceto as duas de PED com 40 h e 50 h)

UNION: Disciplinas de PED ou com carga horária ≥ 60 h

-- Conjunto A: disciplinas de Pedagogia
SELECT id_disciplina, nome_disciplina, carga_horaria
FROM   disciplina
WHERE  id_curso = 211

UNION

-- Conjunto B: disciplinas com carga horária >= 60 horas
SELECT id_disciplina, nome_disciplina, carga_horaria
FROM   disciplina
WHERE  carga_horaria >= 60

ORDER BY id_disciplina;
Displaying records 1 - 10
id_disciplina nome_disciplina carga_horaria
1110201 História Econômica Geral 75
1110202 Culturas Digitais 60
1110401 Microeconomia 2 75
1110402 Macroeconomia 2 95
1110601 Economia Brasileira 75
1110602 Orçamento e Finanças Públicas 75
1110801 Monografia 100
1110802 Desenvolvimento Econômico 90
2110201 Desenvolvimento Humano 60
2110202 Didática Geral 60
Nota

Várias disciplinas de PED pertencem aos dois conjuntos, mas aparecem apenas uma vez no resultado — a união elimina duplicatas automaticamente. As disciplinas de PED com menos de 60 h são adicionadas via conjunto A.

INTERSECT: Disciplinas de PED E com carga horária ≥ 60 h

-- Disciplinas de PED que também têm carga horária >= 60 horas
SELECT id_disciplina, nome_disciplina, carga_horaria
FROM   disciplina
WHERE  id_curso = 211

INTERSECT

SELECT id_disciplina, nome_disciplina, carga_horaria
FROM   disciplina
WHERE  carga_horaria >= 60

ORDER BY id_disciplina;
6 records
id_disciplina nome_disciplina carga_horaria
2110201 Desenvolvimento Humano 60
2110202 Didática Geral 60
2110401 Eletiva I 80
2110402 Fundamentos e Orientações Metodológicas - Ed. Infantil - BNCC 80
2110601 Eletiva II 80
2110801 Tecnologia Educacional - Design 60

As disciplinas de PED com 40 h e 50 h satisfazem apenas o primeiro lado — são excluídas do resultado.

EXCEPT: Disciplinas de PED que NÃO têm carga horária ≥ 60 h

-- Disciplinas de PED com carga horária inferior a 60 horas
SELECT id_disciplina, nome_disciplina, carga_horaria
FROM   disciplina
WHERE  id_curso = 211

EXCEPT

SELECT id_disciplina, nome_disciplina, carga_horaria
FROM   disciplina
WHERE  carga_horaria >= 60;
2 records
id_disciplina nome_disciplina carga_horaria
2110802 Estatística Aplicada 50
2110602 Educação do Campo, Indígena e Quilombola 40

Apenas as disciplinas de PED com 40 h e 50 h pertencem ao conjunto A mas não ao conjunto B.

UNION ALL: Mantendo Duplicatas

-- Todos os id_curso de disciplinas e alunos (com repetição)
SELECT id_curso FROM disciplina
UNION ALL
SELECT id_curso FROM aluno
ORDER BY id_curso;
Displaying records 1 - 10
id_curso
111
111
111
111
111
111
111
111
111
111

O resultado tem 24 + 18 = 42 linhas, com muitas repetições. Use UNION ALL quando a presença de duplicatas é intencional (por exemplo, ao contar ocorrências totais).

Compatibilidade de Esquema

As duas consultas combinadas por UNION, INTERSECT ou EXCEPT devem ter:

  1. O mesmo número de colunas no SELECT
  2. Tipos de dados compatíveis em cada posição correspondente

Os nomes das colunas no resultado final são determinados pela primeira consulta.


Para Praticar

-- EXCEPT: disciplinas que possuem pré-requisito mas nenhum aluno cursou
-- (aparece em prereq, mas não em matricula_disciplina)
SELECT d.id_disciplina, d.nome_disciplina
FROM   disciplina d
WHERE  d.id_disciplina IN (SELECT id_disciplina FROM prereq)
EXCEPT
SELECT d.id_disciplina, d.nome_disciplina
FROM   disciplina d
WHERE  d.id_disciplina IN (SELECT DISTINCT id_disciplina FROM matricula_disciplina)
ORDER BY id_disciplina;
6 records
id_disciplina nome_disciplina
1110601 Economia Brasileira
1110602 Orçamento e Finanças Públicas
1110801 Monografia
2110801 Tecnologia Educacional - Design
3110801 Bases da Eng. de Software 8
3110802 Projeto Aplicado 8 - Sistema em Tempo Real
-- INTERSECT: disciplinas que têm pré-requisito E foram cursadas por algum aluno
SELECT id_disciplina FROM prereq
INTERSECT
SELECT DISTINCT id_disciplina FROM matricula_disciplina
ORDER BY id_disciplina;
7 records
id_disciplina
1110401
2110401
2110601
3110401
3110402
3110601
3110602
-- UNION ALL vs UNION: efeito das duplicatas
-- UNION ALL conta todas as ocorrências; UNION apenas valores distintos
SELECT 'UNION (sem dup.)' AS operacao, COUNT(*) AS n_linhas
FROM (
    SELECT id_curso FROM curso
    UNION
    SELECT id_curso FROM disciplina
) t
UNION ALL
SELECT 'UNION ALL (com dup.)', COUNT(*)
FROM (
    SELECT id_curso FROM curso
    UNION ALL
    SELECT id_curso FROM disciplina
) t;
2 records
operacao n_linhas
UNION (sem dup.) 3
UNION ALL (com dup.) 27