En mi artículo anterior, expliqué los conceptos básicos de los operadores de conjuntos, sus tipos y los requisitos previos para su uso. También hablé sobre los operadores UNION y UNION ALL, su uso y diferencias.
En este artículo, vamos a aprender lo siguiente:
- Operadores EXCEPT e INTERSECT.
- Diferencia entre INTERSECT e INNER JOIN.
- La explicación detallada de INTERSECT y EXCEPT con un ejemplo.
Los operadores EXCEPT e INTERSECT se introdujeron en SQL Server 2005. Ambos son operadores establecidos que se utilizan para combinar los conjuntos de resultados generados por dos consultas y recuperar el resultado deseado.
¿Qué es el operador INTERSECT
INTERSECT se utiliza para obtener registros comunes a todos los conjuntos de datos recuperados de varias consultas o tablas. Aquí hay una visualización de esto:
La sintaxis del operador INTERSECT es la siguiente:
SELECT COLUMN1, COLUMN2, COLUMN3, COLUMN4..FROM TABLE1 INTERSECT SELECT COLUMN1, COLUMN2, COLUMN3, COLUMN4..FROM TABLE2
¿Qué es el operador EXCEPTO
EXCEPT se utiliza para recuperar registros que se encuentran en una consulta pero no en otra consulta. En otras palabras, devuelve registros que son exclusivos de un conjunto de resultados. Así es como se ve visualizado:
La sintaxis del operador EXCEPT es la siguiente:
SELECT COLUMN1, COLUMN2, COLUMN3, COLUMN4..FROM TABLE1 EXCEPT SELECT COLUMN1, COLUMN2, COLUMN3, COLUMN4..FROM TABLE2
Vamos a crear una configuración de demostración para demostrar cómo se pueden usar estos operadores.
Configuración de demostración
Para demostrar INTERSECT y EXCEPT, creé dos tablas llamadas Employee y aprendiz .
Ejecute la siguiente consulta para crear estas tablas:
CREATE TABLE [DBO].[EMPLOYEE] ( [NAME] [NVARCHAR](250) NOT NULL, [BUSINESSENTITYID] [INT] NOT NULL, [NATIONALIDNUMBER] [NVARCHAR](15) NOT NULL, [LOGINID] [NVARCHAR](256) NOT NULL, [BIRTHDATE] [DATE] NOT NULL, [MARITALSTATUS] [NCHAR](1) NOT NULL, [GENDER] [NCHAR](1) NOT NULL ) ON [PRIMARY] CREATE TABLE [DBO].[TRAINEE] ( [NAME] [NVARCHAR](250) NOT NULL, [BUSINESSENTITYID] [INT] NOT NULL, [NATIONALIDNUMBER] [NVARCHAR](15) NOT NULL, [BIRTHDATE] [DATE] NOT NULL, [GENDER] [NCHAR](1) NOT NULL ) ON [PRIMARY]
Ahora, insertemos algunos datos ficticios en el Empleado table ejecutando la siguiente consulta:
INSERT [DBO].[EMPLOYEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [LOGINID], [BIRTHDATE], [MARITALSTATUS], [GENDER]) VALUES (N'KEN SÁNCHEZ', 1, N'295847284', N'ADVENTURE-WORKS\KEN0', CAST(N'1969-01-29' AS DATE), N'S', N'M') GO INSERT [DBO].[EMPLOYEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [LOGINID], [BIRTHDATE], [MARITALSTATUS], [GENDER]) VALUES (N'TERRI DUFFY', 2, N'245797967', N'ADVENTURE-WORKS\TERRI0', CAST(N'1971-08-01' AS DATE), N'S', N'F') GO INSERT [DBO].[EMPLOYEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [LOGINID], [BIRTHDATE], [MARITALSTATUS], [GENDER]) VALUES (N'ROBERTO TAMBURELLO', 3, N'509647174', N'ADVENTURE-WORKS\ROBERTO0', CAST(N'1974-11-12' AS DATE), N'M', N'M') GO INSERT [DBO].[EMPLOYEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [LOGINID], [BIRTHDATE], [MARITALSTATUS], [GENDER]) VALUES (N'ROB WALTERS', 4, N'112457891', N'ADVENTURE-WORKS\ROB0', CAST(N'1974-12-23' AS DATE), N'S', N'M') GO INSERT [DBO].[EMPLOYEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [LOGINID], [BIRTHDATE], [MARITALSTATUS], [GENDER]) VALUES (N'GAIL ERICKSON', 5, N'695256908', N'ADVENTURE-WORKS\GAIL0', CAST(N'1952-09-27' AS DATE), N'M', N'F') GO INSERT [DBO].[EMPLOYEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [LOGINID], [BIRTHDATE], [MARITALSTATUS], [GENDER]) VALUES (N'JOSSEF GOLDBERG', 6, N'998320692', N'ADVENTURE-WORKS\JOSSEF0', CAST(N'1959-03-11' AS DATE), N'M', N'M')
A continuación, haremos lo mismo con el aprendiz tabla:
INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'JOHN WOOD', 18, N'222969461', CAST(N'1978-03-06' AS DATE), N'M') GO INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'MARY DEMPSEY', 19, N'52541318', CAST(N'1978-01-29' AS DATE), N'F') GO INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'WANIDA BENSHOOF', 20, N'323403273', CAST(N'1975-03-17' AS DATE), N'F') GO INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'KEN SÁNCHEZ', 1, N'295847284', CAST(N'1969-01-29' AS DATE), N'M') GO INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'TERRI DUFFY', 2, N'245797967', CAST(N'1971-08-01' AS DATE), N'F') GO INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'ROBERTO TAMBURELLO', 3, N'509647174', CAST(N'1974-11-12' AS DATE), N'M') GO
Ahora, usemos INTERSECT para recuperar la lista de empleados que son comunes a ambas tablas. Para hacerlo, ejecute la siguiente consulta:
SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM EMPLOYEE INTERSECT SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM TRAINEE
El resultado de esta consulta debe ser el siguiente:
Como puede ver en la captura de pantalla anterior, la consulta solo ha devuelto registros que son comunes a ambas tablas.
UNIÓN INTERNA frente a INTERSECCIÓN
En la mayoría de los casos, INTERSECT e INNER JOIN devuelven el mismo resultado, pero hay algunas excepciones. Un ejemplo simple nos ayudará a entender esto.
Agreguemos algunos registros duplicados a la tabla Trainee. Ejecute la siguiente consulta:
INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'TERRI DUFFY', 2, N'245797967', CAST(N'1971-08-01' AS DATE), N'F') GO INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'ROBERTO TAMBURELLO', 3, N'509647174', CAST(N'1974-11-12' AS DATE), N'M') GO
Ahora, intentaremos generar el resultado deseado usando INTERSECT.
SELECT NAME,BUSINESSENTITYID,NATIONALIDNUMBER,BIRTHDATE,GENDER FROM EMPLOYEE INTERSECT SELECT NAME,BUSINESSENTITYID,NATIONALIDNUMBER,BIRTHDATE,GENDER FROM TRAINEE
Este es el resultado que obtenemos:
Ahora, intentemos usar INNER JOIN.
SELECT A.NAME, A.BUSINESSENTITYID, A.NATIONALIDNUMBER, A.BIRTHDATE, A.GENDER FROM EMPLOYEE A INNER JOIN TRAINEE B ON A.NAME = B.NAME
El resultado que obtenemos en este caso es el siguiente:
Ahora, como puede ver en la captura de pantalla anterior, INNER JOIN recupera registros que son comunes a ambas tablas. Rellena todos los registros de la tabla de la derecha. Por lo tanto, puede ver registros duplicados.
Ahora, agreguemos la palabra clave DISTINCT a la consulta INNER JOIN y veamos lo que hace:
SELECT DISTINCT A.NAME, A.BUSINESSENTITYID, A.NATIONALIDNUMBER, A.BIRTHDATE, A.GENDER FROM EMPLOYEE A INNER JOIN TRAINEE B ON A.NAME = B.NAME
La salida debería verse así:
Como puede ver en la captura de pantalla anterior, se han eliminado los registros duplicados.
INTERSECT e INNER JOIN tratan los valores NULL de manera diferente. Para INNER JOIN, dos valores NULL son diferentes, por lo que hay posibilidades de que los omita al unir dos tablas.
Por otro lado, INTERSECT trata dos valores NULL como si fueran iguales, por lo que los registros que tienen valores NULL no serán eliminados. Para entenderlo mejor, veamos un ejemplo.
Primero, agreguemos algunos valores NULL al Aprendiz y empleado tablas ejecutando la siguiente consulta:
INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (NULL, 3, N'509647174', CAST(N'1974-11-12' AS DATE), N'M') GO INSERT [DBO].[Employee] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER],[LOGINID], [BIRTHDATE],[MARITALSTATUS], [GENDER]) VALUES (NULL, 3, N'509647174','ADVENTURE-WORKS\TERRI0', CAST(N'1974-11-12' AS DATE), N'M',N'M') GO
Ahora intentemos recuperar registros comunes a las dos tablas usando INTERSECT e INNER JOIN. Deberá ejecutar la siguiente consulta:
/*QUERY WITH INTERSECT*/ SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM EMPLOYEE INTERSECT SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM TRAINEE /*QUERY WITH INNER JOIN*/ SELECT A.NAME, A.BUSINESSENTITYID, A.NATIONALIDNUMBER, A.BIRTHDATE, A.GENDER FROM EMPLOYEE A INNER JOIN TRAINEE B ON A.NAME = B.NAME
Esta es la salida que deberíamos obtener como resultado:
Como puede ver arriba, el conjunto de resultados generado por INTERSECT contiene valores NULL, mientras que INNER JOIN omitió los registros que tienen valores NULL.
El operador EXCEPTO
Para demostrar el operador EXCEPT en acción, veamos un caso de uso. Por ejemplo, quiero completar los detalles de las empleadas de la tabla Empleado. La siguiente consulta nos ayudará a hacer precisamente eso:
SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM EMPLOYEE WHERE GENDER = 'F' EXCEPT SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM EMPLOYEE WHERE GENDER = 'M'
Este es el resultado que obtenemos:
Como puede ver arriba, la consulta llenó solo los detalles de las empleadas.
También puede completar el conjunto de resultados mediante una subconsulta:
SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM EMPLOYEE AS M WHERE GENDER = 'F' AND GENDER NOT IN (SELECT GENDER FROM EMPLOYEE AS F WHERE GENDER = 'M')
Limitaciones de INTERSECT y EXCEPT
- No podemos usar EXCEPT e INTERSECT en definiciones de vistas particionadas distribuidas con cláusulas COMPUTE y COMPUTE BY.
- EXCEPT e INTERSECT se pueden usar en cursores estáticos y de solo avance rápido.
- EXCEPT e INTERSECT se pueden usar en consultas distribuidas, pero solo se pueden ejecutar en el servidor local. No puede ejecutarlos en un servidor remoto.
Resumen
En este artículo, he cubierto:
- Los operadores EXCEPT e INTERSECT.
- La diferencia entre INTERSECT e INNER JOIN.
- Una explicación detallada de los operadores INTERSECT y EXCEPT con un ejemplo.