
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 ALLsiempre 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, prefiereUNION ALL. - INTERSECT y EXCEPT internamente hacen ordenación o hash para comparar; pueden ser costosos con tablas grandes. Alternativa equivalente con
EXISTS/NOT EXISTSsuele ser competitiva si hay índice sobre la columna de enlace. - Los tipos exactos importan: combinar
VARCHAR(10)conTEXTfuerza 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
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.