Introdução

Continuando o post anterior, vamos tratar dos operadores que faltaram no post anterior que são: INTERSECT e EXCEPT, que são dois operadores de comparação e não de concatenação.

Conceito

Como dito, esses dois comandos realizam comparação entre as duas tabelas e suas linhas que retornam na consulta e dependendo do operador ele pode excluir ou armazenar e retornar para o resultado final.

Ambos os operadores aplicam o distinct  de forma implícita em suas operações sendo que o INTERSECT irá retornar os valores que forem comuns e não duplicados entre as duas tabelas e o EXCEPT irá retornar apenas os resultados da primeira query que não fazem partes da segunda query.

Quando o engine do banco compara as linhas para aplicar o DISTINCT, ele considera os valores NULL como iguais e assim sendo, retorna no resultado, dependendo do operador.
Veja abaixo, o resultado da operação com INTERSECT:

Podemos ver que o resultado final trouxe apenas três linhas, pois, estas são as únicas linhas que aparecem em ambas as tabelas.

Agora, vamos ver um exemplo de como funciona o EXCEPT:

Veja que, o resultado foi menor do que o obtido quando utilizamos o INTERSECT pois ele retornou apenas os valores que aparecem apenas na primeira query, uma vez que, os valores que aparecem na segunda, o operador exclui.

NOTA:  Quando utilizamos o operador EXCEPT, precisamos prestar atenção na ordem da query e como queremos o resultado final pois, como este operador retorna ‘apenas’ os valores da primeira, precisamos ficar atentos. 

Veja o exemplo da diferença da ordem das queries:

INTERSECT e EXCEPT ALL e como solucionar com o T-SQL.

Quando o T-SQL foi adaptado do padrão SQL ANSI, a Microsoft não implementou o INTERSECT E EXCEPT ALL como comandos internos da linguagem para manipular o banco de dados. Com isso, é preciso utilizar uma solução de contorno para obter o resultado desejado.

Veja que as interseções duplicadas não serão removidas do resultado final e quando este for gerado, irá retornar a menor ocorrência de uma linha na interseção, como assim?

Se um determinado resultado aparece 4 vezes na tabela A e 6 vezes na tabela B, o resultado final mostrado será de 4 linhas deste valor e não 6.

Veja abaixo o exemplo de uma query solucionando o problema do INTERSECT ALL.

Agora, vamos ver o exemplo do EXCEPT ALL e seu resultado na consulta. Irei usar uma consulta com C.T.E para excluir a coluna ROWNUM. 

A query abaixo retornou três linhas pois como o banco entende como um ALL. Com isso, mesmo um valor que esteja na query 01 mas, não possua correspondente na query 02, acaba sendo exibido no resultado final da execução da query.

NOTA: A disposição das colunas quando vamos trabalhar com operadores de conjunto podem influenciar no resultado final da query, independente do operador. 

Veja abaixo o exemplo utilizando UNION: 

Quando utilizamos o mesmo modelo de query mas, aplicando o operador INTERSECT ele nem retorna resultado pois não consegue comparar os valores, veja abaixo:

E por fim, a demonstração do resultado quando utilizamos o EXCEPT, veja abaixo:

Precedência

Agora, vamos tratar da questão de precedência entre os operadores de conjunto que temos na linguagem SQL. Essa precedência nos dá uma base para que possamos elaborar resultados condizentes com o esperado de acordo com o tipo usado.

O operador com maior precedência entre os citados é o INTERSECT. O engine do banco irá sempre avaliar este em primeiro, não importando sua posição na criação da query.

Já os outros operadores como UNION E EXCEPT, a precedência é respeitada de acordo com a ordem em que eles se encontram. Aquele que vier primeiro será priorizado pelo engine do banco.

NOTA: Se quiser alterar a prioridade de um operador como por exemplo, executar um UNION na frente do INTERSECT mesmo sabendo que este tem maior prioridade, basta colocar essa query entre parênteses.

Veja abaixo,  como funciona a precedência. A linha vermelha é quem será executada primeiro e o bloco no quadrado preto, após.

No primeiro conjunto de query, o banco irá realizar uma exceção, retornando apenas os valores da primeira query e quando ele finalizar o bloco, irá para o operador de interseção para retornar apenas os valores comuns entre as consultas.

Isso ocorre pois o primeiro bloco de consultas está entre parênteses

No primeiro conjunto de query, o banco irá realizar uma exceção, retornando apenas os valores da primeira query e quando ele finalizar o bloco, irá para o operador de interseção para retornar apenas os valores comuns entre as consultas.

Isso ocorre pois o primeiro bloco de consultas está entre parênteses.

Agora, veja abaixo a mesma query porém, sem o parênteses para reorganizar a precedência.

O primeiro bloco a ser executado foi o bloco no quadrado vermelho e o segundo, o bloco do quadrado preto. Vejam que a quantidade de linhas retornadas foi maior pois como o EXCEPT é executado por último, é o resultado proveniente deste operador que o banco retorna.

E para finalizar, irei mostrar abaixo uma forma de restringir valores no resultado das queries que usam operadores de conjunto.

Repare no primeiro exemplo que irei mostrar, retornando apenas os empregados com ID 3 e 5. Esta query retorna 169 linhas e eu apenas reduzi seu resultado. Veja o exemplo.

Agora veja por exemplo, se eu quiser apenas os maiores valores de um grupo de empregados. Irei usar novamente os empregados com ID 3 e 5.

Sim, usando a cláusula TOP, foi possível retornar apenas os dois maiores pedidos atribuídos a cada um dos empregados.

Com esse último tópico finalizo este post sobre os operadores de conjunto e sua usabilidade além de apresentar algumas soluções de contorno e os cuidados que devemos ter com a precedência para um resultado mais assertivo.

Espero que tenham gostado e deixarei o script para criação do banco usado nos exemplos aqui no github.