Aprende SQL · lección gratuita

Lección 29 · Operaciones de conjuntos: UNION, INTERSECT, EXCEPT

Resumen

Aunque el título menciona tres, en realidad son cuatro operadores de conjuntos: UNION (elimina duplicados), UNION ALL (los conserva), INTERSECT (intersección) y EXCEPT (diferencia). Los operadores de conjuntos combinan los resultados de dos o más SELECT en uno solo, tratándolos como conjuntos de filas. SQLite ofrece UNION (suma, sin duplicados), UNION ALL (suma, conservando duplicados), INTERSECT (filas comunes a ambos) y EXCEPT (filas del primero que no están en el segundo). A diferencia de un JOIN, que combina columnas horizontalmente, los operadores de conjuntos apilan filas verticalmente.

Sintaxis / Conceptos

La forma es SELECT ... <operador> SELECT .... Un único ORDER BY se permite al final, aplicándose al resultado combinado (no a cada SELECT por separado). No uses ORDER BY dentro de los SELECT intermedios de una unión.

Precedencia: en SQLite, INTERSECT, UNION, UNION ALL y EXCEPT se evalúan de izquierda a derecha con la misma prioridad. Si combinas varios operadores, usa subconsultas o CTEs entre paréntesis para dejar la intención explícita y evitar sorpresas.

UNION vs JOIN: si quieres más columnas (datos relacionados de otra tabla) usas JOIN; si quieres más filas (apilar resultados del mismo tipo) usas operadores de conjuntos. Errores comunes: número distinto de columnas en cada lado; esperar duplicados con UNION (los quita) o esperar que EXCEPT sea simétrico (no lo es: A EXCEPT BB EXCEPT A).

WITH dept_stats AS (SELECT departamento_id, COUNT(*) AS n, AVG(salario) AS avg_sal FROM empleados GROUP BY departamento_id) SELECT d.nombre, s.n AS empleados, ROUND(s.avg_sal, 0) AS salario_prom FROM dept_stats s JOIN departamentos d ON d.id = s.departamento_id ORDER BY avg_sal DESC

Ejemplos

-- UNION: catálogo de lugares (ciudades de deptos + países de clientes), sin duplicados
SELECT ciudad AS lugar FROM departamentos
UNION
SELECT pais FROM clientes
ORDER BY lugar;
-- UNION ALL vs UNION: conservar vs deduplicar
SELECT pais FROM clientes
UNION ALL                       -- 18 filas (con repetidos)
SELECT pais FROM clientes;
-- Etiquetar el origen de cada fila en una unión
SELECT 'caro' AS tipo, nombre FROM productos WHERE precio > 10000
UNION ALL
SELECT 'barato', nombre FROM productos WHERE precio < 700
ORDER BY tipo, nombre;
-- INTERSECT: clientes que son a la vez de Perú y del segmento Enterprise (ids)
SELECT id FROM clientes WHERE pais = 'Perú'
INTERSECT
SELECT id FROM clientes WHERE segmento = 'Enterprise';
-- EXCEPT: productos que NUNCA se han vendido (catálogo menos los vendidos)
SELECT id FROM productos
EXCEPT
SELECT producto_id FROM detalle_pedidos
ORDER BY id;
-- EXCEPT: empleados que no son vendedores (no aparecen en pedidos)
SELECT id FROM empleados
EXCEPT
SELECT empleado_id FROM pedidos
ORDER BY id;
💡 EXCEPT y NOT IN resuelven anti-joins parecidos, pero EXCEPT deduplica y compara la fila completa; además es inmune al problema de los NULL que tiene NOT IN. Para "lo que está en A pero no en B", EXCEPT suele ser lo más legible.

Cheatsheet

Cláusula / FunciónQué hace
A UNION BFilas de A y B, sin duplicados
A UNION ALL BFilas de A y B, con duplicados (más rápido)
A INTERSECT BFilas presentes en ambos
A EXCEPT BFilas de A que no están en B (no simétrico)
ORDER BY finalOrdena el resultado combinado (una sola vez, al final)
CompatibilidadMismo nº de columnas y tipos compatibles en orden

---

← CTEs recursivosFunciones de ventana: OVER y PARTITION BY →

Ver todas las lecciones de Aprende SQL →