En el ámbito de las bases de datos relacionales, la capacidad de combinar información de múltiples tablas es fundamental. SQL proporciona operadores JOIN para vincular filas basadas en columnas relacionadas y el operador UNION para fusionar los resultados de consultas separadas. Este artículo explora los tipos principales de JOIN y el uso de UNION, ilustrando su comportamiento con ejemplos prácticos.
Explorando los Tipos de JOIN en SQL
Las cláusulas JOIN se utilizan para combinar registros de dos o más tablas en una sola consulta. El tipo de JOIN especifica cómo se emparejan las filas y qué filas se incluyen en el conjunto de resultados cuando no hay coincidencias directas.
INNER JOIN: Coincidencias Comunes
El INNER JOIN es el tipo de unión más común. Devuelve solo las filas que tienen valores coincidentes en ambas tablas. Si una fila de la primera tabla no tiene una coincidencia en la segunda tabla, o viceversa, esa fila no se incluirá en el resultado.
SELECT sw.nombre_sitio, ra.info_contacto, ra.fecha_acceso
FROM SitiosWeb sw
INNER JOIN RegistrosAcceso ra ON sw.id_sitio = ra.id_sitio_web
ORDER BY ra.info_contacto ASC;
LEFT JOIN (o LEFT OUTER JOIN): Preservando el Lado Izquierdo
Un LEFT JOIN (también conocido como LEFT OUTER JOIN) devuelve todas las filas de la tabla izquierda y las filas coincidentes de la tabla derecha. Si no hay una coincidencia para una fila de la tabla izquierda en la tabla derecha, las columnas de la tabla derecha mostrarán NULL.
SELECT sw.nombre_sitio, ra.info_contacto, ra.fecha_acceso
FROM SitiosWeb sw
LEFT JOIN RegistrosAcceso ra ON sw.id_sitio = ra.id_sitio_web
ORDER BY ra.fecha_acceso DESC;
Registros Exclusivos del Lado Izquierdo (LEFT JOIN con Filtro)
Para obtener solo las filas de la tabla izquierda que no tienen una correspondencia en la tabla derecha, se puede usar un LEFT JOIN combinado con una condición WHERE que filtra por valores NULL en la clave de unión de la tabla derecha. Esto identifica elementos presentes en una tabla pero ausentes en la otra.
SELECT sw.nombre_sitio, ra.info_contacto, ra.fecha_acceso
FROM SitiosWeb sw
LEFT JOIN RegistrosAcceso ra ON sw.id_sitio = ra.id_sitio_web
WHERE ra.id_sitio_web IS NULL
ORDER BY sw.nombre_sitio;
RIGHT JOIN (o RIGHT OUTER JOIN): Preservando el Lado Derecho
Similar al LEFT JOIN, el RIGHT JOIN (o RIGHT OUTER JOIN) devuelve todas las filas de la tabla derecha y las filas coincidentes de la tabla izquierda. Si no hay una coincidencia para una fila de la tabla derecha en la tabla izquierda, las columnas de la tabla izquierda mostrarán NULL.
SELECT sw.nombre_sitio, ra.info_contacto, ra.fecha_acceso
FROM SitiosWeb sw
RIGHT JOIN RegistrosAcceso ra ON ra.id_sitio_web = sw.id_sitio
ORDER BY sw.nombre_sitio DESC;
Registros Exclusivos del Lado Derecho (RIGHT JOIN con Filtro)
Para aislar las filas de la tabla derecha que no tienen un valor correspondiente en la tabla izquierda, se emplea un RIGHT JOIN con una cláusula WHERE que comprueba si la clave de unión de la tabla izquierda es NULL.
SELECT sw.nombre_sitio, ra.info_contacto, ra.fecha_acceso
FROM SitiosWeb sw
RIGHT JOIN RegistrosAcceso ra ON ra.id_sitio_web = sw.id_sitio
WHERE sw.id_sitio IS NULL
ORDER BY ra.info_contacto;
FULL OUTER JOIN: La Unión Completa
El FULL OUTER JOIN combina los resultados de un LEFT JOIN y un RIGHT JOIN. Retorna todas las filas de ambas tablas. Si una fila no tiene una coincidencia en la otra tabla, las columnas de la tabla no coincidente mostrarán NULL. Este tipo de unión es útil para ver la imagen completa de los datos en ambas tablas, incluyendo los no coincidentes.
SELECT sw.nombre_sitio, ra.info_contacto, ra.fecha_acceso
FROM SitiosWeb sw
FULL OUTER JOIN RegistrosAcceso ra ON sw.id_sitio = ra.id_sitio_web
ORDER BY ra.fecha_acceso DESC;
Registros Exclusivos en Ambas Tablas (FULL OUTER JOIN - Diferencia Simétrica)
Para obtener las filas que son exclusivas de la tabla izquierda O exclusivas de la tabla derecha (es decir, todas las filas no coincidentes de ambas tablas), se utiliza un FULL OUTER JOIN y se filtra donde cualquiera de las claves de unión es NULL.
SELECT sw.nombre_sitio, ra.info_contacto, ra.fecha_acceso
FROM SitiosWeb sw
FULL OUTER JOIN RegistrosAcceso ra ON sw.id_sitio = ra.id_sitio_web
WHERE sw.id_sitio IS NULL OR ra.id_sitio_web IS NULL
ORDER BY sw.nombre_sitio DESC;
CROSS JOIN: El Producto Cartesiano
Un CROSS JOIN produce el producto cartesiano de las dos tablas, lo que significa que cada fila de la primera tabla se combina con cada fila de la segunda tabla. El número total de filas en el resultado será el producto del número de filas de ambas tablas.
SELECT sw.nombre_sitio, ra.info_contacto, ra.fecha_acceso
FROM SitiosWeb sw
CROSS JOIN RegistrosAcceso ra;
Operadores UNION en SQL
Los operadores UNION se utilizan para combinar los conjuntos de resultados de dos o más sentencias SELECT en un único conjunto de resultados. Es crucial que cada sentencia SELECT dentro de la UNION tenga el mismo número de columnas, que las columnas correspondientes tengan tipos de datos compatibles y que el orden de las columnas sea idéntico.
Sintaxis de UNION
El operador UNION, por defecto, elimina las filas duplicadas del conjunto de resultados final.
SELECT columna_nombre(s) FROM tabla1
UNION
SELECT columna_nombre(s) FROM tabla2;
Sintaxis de UNION ALL
El operador UNION ALL combina los conjuntos de resultados sin eliminar las filas duplicadas, lo que puede ser más eficiente si se sabe que no hay duplicados o si se desea conservarlos.
SELECT columna_nombre(s) FROM tabla1
UNION ALL
SELECT columna_nombre(s) FROM tabla2;
Ejempols de UNION y UNION ALL
Los nombres de las columnas en el conjunto de resultados de un UNION serán los de la primera sentencia SELECT.
-- Combinar países únicos de SitiosWeb y Aplicaciones
SELECT pais FROM SitiosWeb
UNION
SELECT pais FROM Aplicaciones
ORDER BY pais;
-- Combinar todos los países (incluyendo duplicados) de SitiosWeb y Aplicaciones
SELECT pais FROM SitiosWeb
UNION ALL
SELECT pais FROM Aplicaciones
ORDER BY pais;
-- Combinar sitios web y aplicaciones de China, incluyendo duplicados y nombres
SELECT pais, nombre_sitio FROM SitiosWeb
WHERE pais='CN'
UNION ALL
SELECT pais, nombre_app FROM Aplicaciones
WHERE pais='CN'
ORDER BY pais;
Configuración de Ejemplo: Estructura de Tablas
Para replicar los ejemplos presentados, utilice las siguientes definiciones de tablas y datos de muestra.
Tabla: RegistrosAcceso
CREATE TABLE RegistrosAcceso (
aid INT NOT NULL,
id_sitio_web INT DEFAULT 0,
info_contacto VARCHAR(255) DEFAULT 'usuario@ejemplo.com',
fecha_acceso DATE,
PRIMARY KEY (aid)
);
COMMENT ON COLUMN RegistrosAcceso.id_sitio_web IS 'Identificador del sitio web';
COMMENT ON COLUMN RegistrosAcceso.info_contacto IS 'Información de contacto del acceso';
INSERT INTO RegistrosAcceso VALUES (1, 1, 'usuario01@ejemplo.com', TO_DATE('2023-01-10', 'YYYY-MM-DD'));
INSERT INTO RegistrosAcceso VALUES (2, 3, 'usuario02@ejemplo.com', TO_DATE('2023-01-13', 'YYYY-MM-DD'));
INSERT INTO RegistrosAcceso VALUES (3, 1, 'usuario03@ejemplo.com', TO_DATE('2023-01-14', 'YYYY-MM-DD'));
INSERT INTO RegistrosAcceso VALUES (4, 5, 'usuario04@ejemplo.com', TO_DATE('2023-01-14', 'YYYY-MM-DD'));
INSERT INTO RegistrosAcceso VALUES (5, 3, 'usuario05@ejemplo.com', TO_DATE('2023-01-15', 'YYYY-MM-DD'));
INSERT INTO RegistrosAcceso VALUES (6, 5, 'usuario06@ejemplo.com', TO_DATE('2023-01-16', 'YYYY-MM-DD'));
INSERT INTO RegistrosAcceso VALUES (7, 3, 'usuario07@ejemplo.com', TO_DATE('2023-01-17', 'YYYY-MM-DD'));
INSERT INTO RegistrosAcceso VALUES (8, 6, 'usuario08@ejemplo.com', TO_DATE('2023-01-18', 'YYYY-MM-DD')); -- Sin sitio web correspondiente
Tabla: Aplicaciones
CREATE TABLE Aplicaciones (
id_app INT NOT NULL,
nombre_app CHAR(20) DEFAULT '',
url_dominio VARCHAR(255) DEFAULT '',
pais CHAR(10) DEFAULT '',
PRIMARY KEY (id_app)
);
INSERT INTO Aplicaciones VALUES (1, 'App Social', 'http://appsocial.com/', 'MX');
INSERT INTO Aplicaciones VALUES (2, 'App Noticias', 'http://appnoticias.com/', 'AR');
INSERT INTO Aplicaciones VALUES (3, 'App Compras', 'https://www.appcompras.com/', 'ES');
INSERT INTO Aplicaciones VALUES (4, 'App Finanzas', 'http://appfinanzas.com/', 'MX');
Tabla: SitiosWeb
CREATE TABLE SitiosWeb (
id_sitio INT,
nombre_sitio CHAR(20) DEFAULT '',
url VARCHAR(255) DEFAULT '',
ranking_alexa INT DEFAULT 0,
pais CHAR(10) DEFAULT '',
PRIMARY KEY (id_sitio)
);
INSERT INTO SitiosWeb VALUES (1, 'GlobalSearch', 'https://www.globalsearch.com/', 1, 'US');
INSERT INTO SitiosWeb VALUES (2, 'MegaTienda', 'https://www.megatienda.com/', 13, 'ES');
INSERT INTO SitiosWeb VALUES (3, 'PortalTech', 'http://www.portaltech.com/', 4689, 'MX');
INSERT INTO SitiosWeb VALUES (4, 'RedSocialX', 'http://redsocialx.com/', 20, 'AR');
INSERT INTO SitiosWeb VALUES (5, 'MundoFacil', 'https://www.mundofacil.com/', 3, 'US');