Database
 sql >> Base de Dados >  >> RDS >> Database

Operadores T-SQL SET Parte 2:INTERSECT e EXCEPT


Em meu artigo anterior, expliquei o básico dos operadores de conjunto, seus tipos e pré-requisitos para seu uso. Também falei sobre os operadores UNION e UNION ALL, seus usos e diferenças.

Neste artigo, vamos aprender o seguinte:
  1. Operadores EXCEPT e INTERSECT.
  2. Diferença entre INTERSECT e INNER JOIN.
  3. A explicação detalhada de INTERSECT e EXCEPT com um exemplo.

Os operadores EXCEPT e INTERSECT foram introduzidos no SQL Server 2005. Ambos são operadores de conjunto usados ​​para combinar os conjuntos de resultados gerados por duas consultas e recuperar a saída desejada.

O que é o operador INTERSECT


INTERSECT é usado para obter registros comuns a todos os conjuntos de dados recuperados de várias consultas ou tabelas. Aqui está uma visualização disso:



A sintaxe do operador INTERSECT é a seguinte:
SELECT COLUMN1, 
       COLUMN2, 
       COLUMN3, 
       COLUMN4..FROM TABLE1 
INTERSECT
SELECT COLUMN1, 
       COLUMN2, 
       COLUMN3, 
       COLUMN4..FROM TABLE2

O que é o operador EXCEPT


EXCEPT é usado para recuperar registros encontrados em uma consulta, mas não em outra consulta. Em outras palavras, ele retorna registros que são exclusivos para um conjunto de resultados. Isto é o que parece visualizado:



A sintaxe do operador EXCEPT é a seguinte:
SELECT COLUMN1, 
       COLUMN2, 
       COLUMN3, 
       COLUMN4..FROM TABLE1 
EXCEPT
SELECT COLUMN1, 
       COLUMN2, 
       COLUMN3, 
       COLUMN4..FROM TABLE2

Vamos criar uma configuração de demonstração para demonstrar como esses operadores podem ser usados.

Configuração de demonstração


Para demonstrar INTERSECT e EXCEPT, criei duas tabelas chamadas Employee e Estagiário .

Execute a seguinte consulta para criar estas tabelas:

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]

Agora, vamos inserir alguns dados fictícios no campo Funcionário tabela executando a seguinte 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')

Em seguida, faremos o mesmo para o Trainee tabela:
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

Agora, vamos usar INTERSECT para recuperar a lista de funcionários que são comuns a ambas as tabelas. Para fazer isso, execute a seguinte consulta:
SELECT NAME, 
       BUSINESSENTITYID, 
       NATIONALIDNUMBER, 
       BIRTHDATE, 
       GENDER 
FROM   EMPLOYEE 
INTERSECT 
SELECT NAME, 
       BUSINESSENTITYID, 
       NATIONALIDNUMBER, 
       BIRTHDATE, 
       GENDER 
FROM   TRAINEE

A saída desta consulta deve ser a seguinte:



Como você pode ver na captura de tela acima, a consulta retornou apenas registros comuns a ambas as tabelas.

INNER JOIN vs. INTERSECT


Na maioria dos casos, INTERSECT e INNER JOIN retornam a mesma saída, mas há algumas exceções. Um exemplo simples nos ajudará a entender isso.

Vamos adicionar alguns registros duplicados à tabela Trainee. Execute a seguinte 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

Agora, vamos tentar gerar a saída desejada usando INTERSECT.
SELECT NAME,BUSINESSENTITYID,NATIONALIDNUMBER,BIRTHDATE,GENDER FROM EMPLOYEE
INTERSECT
SELECT NAME,BUSINESSENTITYID,NATIONALIDNUMBER,BIRTHDATE,GENDER FROM TRAINEE

Esta é a saída que obtemos:



Agora, vamos tentar 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

A saída que obtemos neste caso é a seguinte:



Agora, como você pode ver na captura de tela acima, INNER JOIN recupera registros comuns a ambas as tabelas. Ele preenche todos os registros da tabela correta. Portanto, você pode ver registros duplicados.

Agora, vamos adicionar a palavra-chave DISTINCT à consulta INNER JOIN e ver o que isso faz:
SELECT DISTINCT A.NAME, 
                A.BUSINESSENTITYID, 
                A.NATIONALIDNUMBER, 
                A.BIRTHDATE, 
                A.GENDER 
FROM   EMPLOYEE A 
       INNER JOIN TRAINEE B 
               ON A.NAME = B.NAME

A saída deve ficar assim:



Como você pode ver na imagem acima, os registros duplicados foram eliminados.

INTERSECT e INNER JOIN tratam valores NULL de forma diferente. Para INNER JOIN, dois valores NULL são diferentes, portanto, há chances de ignorá-los ao unir duas tabelas.

Por outro lado, INTERSECT trata dois valores NULL como sendo iguais, portanto, os registros que possuem valores NULL não serão eliminados. Para entender melhor, vejamos um exemplo.

Primeiro, vamos adicionar alguns valores NULL ao Trainee e Funcionário tabelas executando a seguinte 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

Agora vamos tentar recuperar registros comuns às duas tabelas usando INTERSECT e INNER JOIN. Você precisará executar a seguinte 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 é a saída que devemos obter como resultado:



Como você pode ver acima, o conjunto de resultados gerado por INTERSECT contém valores NULL, enquanto INNER JOIN ignorou os registros que possuem valores NULL.

O operador EXCETO


Para demonstrar o operador EXCEPT em ação, vejamos um caso de uso. Por exemplo, quero preencher os detalhes das funcionárias da tabela Employee. A consulta a seguir nos ajudará a fazer exatamente isso:
SELECT NAME, 
       BUSINESSENTITYID, 
       NATIONALIDNUMBER, 
       BIRTHDATE, 
       GENDER 
FROM   EMPLOYEE 
WHERE  GENDER = 'F' 
EXCEPT 
SELECT NAME, 
       BUSINESSENTITYID, 
       NATIONALIDNUMBER, 
       BIRTHDATE, 
       GENDER 
FROM   EMPLOYEE 
WHERE  GENDER = 'M'

Esta é a saída que obtemos:



Como você pode ver acima, a consulta preencheu apenas os detalhes das funcionárias.

Você também pode preencher o conjunto de resultados usando uma 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')

Limitações de INTERSECT e EXCEPT

  1. Não podemos usar EXCEPT e INTERSECT em definições de exibição particionada distribuída com cláusulas COMPUTE e COMPUTE BY.
  2. EXCEPT e INTERSECT podem ser usados ​​em cursores apenas de avanço rápido e estáticos.
  3. EXCEPT e INTERSECT podem ser usados ​​em consultas distribuídas, mas só podem ser executados no servidor local. Você não pode executá-los em um servidor remoto.

Resumo


Neste artigo, abordei:
  1. Os operadores EXCEPT e INTERSECT.
  2. A diferença entre INTERSECT e INNER JOIN.
  3. Uma explicação detalhada dos operadores INTERSECT e EXCEPT com um exemplo.