UNION, UNION ALL, INTERSECT e EXCEPT
Neste documento vamos detalhar quatro operadores muito úteis quando executamos vários SELECT com o mesmo conjunto de colunas, mas que precisam retornar registros dentro de determinadas condições.
UNION e UNION ALL
Combina o resultado de duas ou mais queries em um único resultado. As queries devem ter a mesma quantidade de colunas e na mesma ordem no SELECT, e o datatype das colunas devem ser compatíveis.
Mas qual a diferença entre UNION e UNION ALL?
O operador UNION elimina as linhas duplicadas. O SQL Server executa cada um dos SELECTs da query, reúne todos os resultados e então executa um SELECT DISTINCT, mesmo que não haja registros duplicados.
O operador UNION ALL mantém as linhas duplicadas. Ou seja, o SQL Server executa cada um dos SELECTs da query, reúne todos os resultados e então os exibe.
Vamos analisar este script:
Criamos duas tabelas temporárias: #TabA e #TabB, cada uma com 6 registros. Observe que os números 5 e 6 se repetem nas duas tabelas.
Na segunda janela, executamos um SELECT com UNION entre as tabelas, e o resultado retorna 10 linhas, pois o UNION eliminou os registros 5 e 6 duplicados.
Já na terceira janela, executamos um SELECT com UNION ALL entre as tabelas, e o resultado retorna 12 linhas, e os registros 5 e 6 aparecem duplicados.
Ainda analisando o resultado das janelas 2 e 3, o nome da coluna no resultado é Col_A. O SQL Server sempre utiliza o nome das colunas da primeira query para exibir o resultado. O nome das colunas na tabela não influencia o SELECT / UNION, desde que a quantidade de colunas em cada query e o datatype de cada coluna seja compatível, como mencionado acima.
Quando utilizamos UNION e UNION ALL, é interessante lembrar alguns pontos importantes:
Utilize UNION apenas quando o resultado da query não puder ter duplicidades. Se você tem certeza que não importa se o resultado trouxer linhas repetidas, sempre utilize UNION ALL. A vantagem é que UNION ALL não executa SELECT DISTINCT, ou seja, ele utiliza menos recursos do SQL Server, tornando a query mais rápida.
Utilizar UNION com SELECT DISTINCT é desnecessário, já que o resultado final será o mesmo, pois SELECT DISTINCT está implícito em UNION.
Colunas com datatype GEOGRAPH não podem ser utilizadas com UNION, mas podem ser utilizadas com UNION ALL.
Uma query com uma ou mais cláusulas OR no WHERE pode ser reescrita utilizando UNION ALL.
Vamos focar neste último tópico. Vejam esta query:
A consulta acima possui três colunas diferentes na cláusula WHERE. A não ser que a tabela PERSON.[Address] possua um índice cuja chave tenha essas três colunas, o SQL Server fará um Table Scan em toda a tabela.
Podemos reescrever esta query, utilizando UNION:
As duas consultas exibirão o mesmo resultado. Porém, se uma das colunas tiver um índice, mas as outras duas não, a primeira query fará o Table Scan. Na segunda query, o índice será utilizado em parte da query, melhorando a performance em geral.
INTERSECT
O operador INTERSECT executa todas as queries associadas a ele, e retorna apenas as linhas com registros iguais em todas as consultas. Vamos analisar este script e seu resultado:
Agora, vamos aplicar INTERSECT:
Analisando o conteúdo das duas tabelas, apenas o registro com o número 5 e a letra A aparecem nas duas tabelas. Como utilizamos * no SELECT, o SQL Server considerou todas as colunas para encontrar os registros que são iguais nas duas tabelas.
Mas, e se eu especificar qual coluna deve ser analisada?
Desta vez, apenas o conteúdo da coluna Numero foi analisado, e por isso duas linhas foram retornadas.
EXCEPT
O operador EXCEPT retorna todas as linhas do primeiro SELECT, exceto as linhas que aparecem nos demais SELECTs da query. Vamos utilizar o script anterior para analisar o resultado de EXCEPT, acrescentando mais uma tabela:
Primeiro, vamos executar o SELECT de todas as colunas, para visualizar o conteúdo das tabelas:
O que aconteceu? A query retornou todas as linhas da tabela #TabA, exceto a linha do número 5, letra A, porque esta linha aparece na tabela #TabB.
Quando acrescentamos a tabela #TabC na query, mais uma linha deixa de aparecer:
E se tratarmos apenas a coluna Numero?
Os números 5 e 6 não são exibidos, porque eles existem na tabela #TabB, e o número 1 não é exibido, porque ele existe na tabela #TabC.
Para fechar esse post, vamos misturar tudo o que vimos até agora. Primeiro, vamos voltar ao primeiro conjunto de tabelas, e acrescentar mais duas:
E agora, utilizar os três operadores na mesma query:
Por que o resultado da query são os números 1, 2, 3, 4, 5 e 6?
O que efetivamente aconteceu aqui? Vamos analisar a query em partes, para entender o resultado exposto.
Primeiro, vamos visualizar o conteúdo de cada tabela:
Agora, vamos analisar a query em partes:
O comando INTERSECT precede UNION e EXCEPT. Então, essa parte da query equivale a:
Quando o SQL Server executa o UNION, o resultado passa a ser 1,2 3, 4, 5, 6, 9, 10. Então, o EXCEPT retira 9 e 10, deixando apenas a sequência de 1 a 6.