Operaciones de conjunto UNION, INTERSECT y EXCEPT

Avanzado
SQL
SQL
Actualizado: 18/04/2026

Diagrama: Sql set operations avanzado

Fundamentos matemáticos

Las operaciones de conjunto tratan los resultados de dos consultas como conjuntos matemáticos. Las tres operaciones estándar son:

  • UNION (∪): unión de los dos conjuntos (todas las filas, sin duplicados).
  • INTERSECT (∩): intersección (solo filas presentes en ambos).
  • EXCEPT (A \ B, también llamado MINUS en Oracle): diferencia (filas del primero que no están en el segundo).

Todas requieren que las consultas tengan el mismo número de columnas y tipos compatibles columna a columna. El orden y el nombre final lo toma de la primera consulta.

UNION y UNION ALL

UNION elimina duplicados (como si aplicara SELECT DISTINCT al resultado combinado). UNION ALL conserva duplicados y es significativamente más rápido porque no hace ordenación ni hash para deduplicar.

-- Clientes activos en 2025 o 2026 (sin duplicados)
SELECT cliente_id, nombre FROM clientes_2025
UNION
SELECT cliente_id, nombre FROM clientes_2026;

-- Todos los movimientos (con duplicados si los hay)
SELECT cuenta, importe, fecha FROM movimientos_online
UNION ALL
SELECT cuenta, importe, fecha FROM movimientos_presencial;

Usa UNION ALL siempre que puedas garantizar (por la naturaleza de los datos o por un WHERE que segrega) que no habrá duplicados. Para datasets grandes la diferencia de rendimiento es muy grande.

INTERSECT

INTERSECT devuelve las filas que aparecen en ambos resultados. Ejemplo: usuarios que han comprado tanto en 2025 como en 2026:

SELECT cliente_id FROM pedidos WHERE EXTRACT(YEAR FROM fecha) = 2025
INTERSECT
SELECT cliente_id FROM pedidos WHERE EXTRACT(YEAR FROM fecha) = 2026;

Esto es equivalente (pero más legible) a:

SELECT DISTINCT p1.cliente_id
FROM pedidos p1
WHERE EXTRACT(YEAR FROM p1.fecha) = 2025
  AND EXISTS (
      SELECT 1 FROM pedidos p2
      WHERE p2.cliente_id = p1.cliente_id
        AND EXTRACT(YEAR FROM p2.fecha) = 2026
  );

También existe INTERSECT ALL (SQL estándar) que cuenta las ocurrencias: si A contiene 3 veces la fila X y B 2 veces, INTERSECT ALL devuelve X dos veces (el mínimo común).

EXCEPT

EXCEPT devuelve filas del primer conjunto que no están en el segundo. Es la forma canónica de detectar diferencias entre datasets:

-- Productos del catalogo que aun no se han vendido
SELECT producto_id FROM productos
EXCEPT
SELECT DISTINCT producto_id FROM pedidos_linea;

-- Usuarios que se registraron pero nunca han comprado
SELECT id FROM usuarios
EXCEPT
SELECT DISTINCT usuario_id FROM pedidos;

En Oracle, se usa MINUS en lugar de EXCEPT:

SELECT producto_id FROM productos
MINUS
SELECT producto_id FROM pedidos_linea;

Reglas de compatibilidad de columnas

Las columnas deben ser compatibles por tipo:

-- OK: ambas columnas 1 son INT, ambas columnas 2 son VARCHAR
SELECT id, nombre FROM tabla_a
UNION
SELECT id, nombre FROM tabla_b;

-- ERROR: los tipos no coinciden
SELECT id, nombre FROM tabla_a      -- INT, VARCHAR
UNION
SELECT nombre, id FROM tabla_b;     -- VARCHAR, INT

El nombre final de la columna lo toma de la primera consulta. Por eso conviene poner aliases descriptivos al principio:

SELECT id AS user_id, nombre AS user_name FROM clientes
UNION
SELECT id, nombre FROM empleados;
-- Las columnas del resultado se llaman user_id, user_name

Orden y paginación

ORDER BY y LIMIT se aplican al resultado completo después de la operación de conjunto, no a cada consulta:

-- Correcto: ORDER BY al final
(SELECT id, nombre FROM clientes)
UNION
(SELECT id, nombre FROM empleados)
ORDER BY nombre
LIMIT 10;

-- Tambien correcto: ORDER BY por cada consulta (con parentesis)
(SELECT * FROM ventas ORDER BY fecha DESC LIMIT 5)
UNION ALL
(SELECT * FROM devoluciones ORDER BY fecha DESC LIMIT 5);

El segundo patrón es útil para top-N por grupo combinados en una sola salida.

Caso real: reconciliación de saldos

Al migrar un sistema contable hay que asegurarse de que los saldos del sistema viejo y el nuevo coinciden. Con EXCEPT detectas discrepancias inmediatamente:

-- Cuentas que difieren entre sistema viejo y nuevo
(
    SELECT cuenta, saldo FROM contabilidad_legacy
    EXCEPT
    SELECT cuenta, saldo FROM contabilidad_nueva
)
UNION ALL
(
    SELECT cuenta, saldo FROM contabilidad_nueva
    EXCEPT
    SELECT cuenta, saldo FROM contabilidad_legacy
);

Este patrón devuelve todas las filas donde la pareja (cuenta, saldo) no es idéntica entre las dos tablas: registros que falten, sobran o tienen saldo distinto.

Auditoría de cambios

Para detectar qué registros han cambiado entre dos snapshots:

-- Registros modificados entre ayer y hoy
WITH cambios AS (
    SELECT id, estado, precio FROM productos_hoy
    EXCEPT
    SELECT id, estado, precio FROM productos_ayer
)
SELECT h.id, h.nombre, a.estado AS estado_anterior, h.estado AS estado_nuevo
FROM cambios c
JOIN productos_hoy h  ON h.id = c.id
JOIN productos_ayer a ON a.id = c.id;

Rendimiento

  • UNION ALL siempre es más rápido que UNION. Si los datos no se solapan, prefiere UNION ALL.
  • INTERSECT y EXCEPT internamente hacen ordenación o hash para comparar; pueden ser costosos con tablas grandes. Alternativa equivalente con EXISTS / NOT EXISTS suele ser competitiva si hay índice sobre la columna de enlace.
  • Los tipos exactos importan: combinar VARCHAR(10) con TEXT fuerza una conversión implícita que puede matar un índice.
-- Comprueba el plan
EXPLAIN ANALYZE
SELECT id FROM clientes
EXCEPT
SELECT id FROM empleados;

Comparación MySQL vs PostgreSQL

| Motor | UNION | UNION ALL | INTERSECT | EXCEPT | MINUS | |-------|-------|-----------|-----------|--------|-------| | PostgreSQL | ✓ | ✓ | ✓ | ✓ | ✗ | | MySQL 8.0.31+ | ✓ | ✓ | ✓ | ✓ | ✗ | | SQL Server | ✓ | ✓ | ✓ | ✓ | ✗ | | Oracle | ✓ | ✓ | ✓ | ✗ | ✓ | | SQLite | ✓ | ✓ | ✓ | ✓ | ✗ |

En versiones antiguas de MySQL (< 8.0.31) no hay INTERSECT ni EXCEPT: se emulan con INNER JOIN o NOT EXISTS respectivamente.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, SQL es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de SQL

Explora más contenido relacionado con SQL y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Diferenciar UNION vs UNION ALL en rendimiento y semantica. Usar INTERSECT para encontrar registros comunes (intersccion) entre dos consultas. Usar EXCEPT (o MINUS en Oracle) para detectar diferencias y registros faltantes. Combinar operaciones de conjunto con ORDER BY y LIMIT. Aplicar casos reales: reconciliacion de saldos, auditoria de usuarios, deteccion de cambios.