SQL DISTINCT e TOP em Same Query

Este artigo é inspirado por uma série de perguntas que um dos meus leitores, Nan, recentemente me enviou sobre DISTINCT, TOP, e ORDER BY.

Todos os exemplos para esta lição são baseados no Microsoft SQL Server Management Studio e na base de dados AdventureWorks2012. Pode começar a utilizar estas ferramentas gratuitas utilizando o meu Guia de Introdução ao Uso do SQL Server.

Como é que os modificadores SQL Top e Distinct SELECT funcionam em conjunto para produzir resultados?

Aqui está a questão que Nan me enviou originalmente:

Estou um pouco confuso acerca de SELECT DISTINCT e SELECT. Por exemplo,

SELECT DISTINCT TOP 10 FirstName,
LastName
FROM Person.Person
ORDER BY LastName

Olhando para os primeiros nomes distintos? Primeiro e último nomes distintos combinados? Como distinguimos entre as colunas utilizadas para a avaliação distinta e as colunas que apenas queremos mostrar na saída?

E que tal

Select Distinct TOP 10 LastName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY LastName

Pensei que todos gostariam de saber a resposta, por isso criei um post no blog.

DISTINCT e TOP – Que é First?

Vejamos a primeira declaração cujo propósito é retornar uma lista única de punho e sobrenomes.

SELECT DISTINCT TOP 10 FirstName,
LastName
FROM Person.Person
ORDER BY LastName;

TOP 10 irá retornar os primeiros dez itens do conjunto ordenado, e DISTINCT irá remover quaisquer duplicatas. A questão é a que acontece primeiro?

  • É a tabela ordenada por LastName e os dez primeiros itens tomados, e depois o nome duplicado é removido?
  • Or são os duplicados removidos, e depois os itens ordenados e os dez primeiros itens exibidos?

Antes de responder a esta questão tenha em mente que DISTINCT opera em todas as colunas e expressões da cláusula SELECT. Portanto, neste caso, a declaração retornará linhas distintas para FirstName e LastName.

Felizmente não existe uma forma directa de usar DISTINCT num conjunto de campos e exibir outros. Depois de adicionar colunas à instrução SELECT elas ficam sob a influência do operador DISTINCT. Eu digo direto, pois você poderia obter uma lista distinta, e então usar um INNER JOIN para puxar em outras colunas. Há perigos de se fazer isso, pois o join pode reintroduzir duplicatas.

Adicionar uma cláusula TOP ao DISTINCT é interessante. Não tinha a certeza do que iria acontecer, mas fiz algumas experiências com a base de dados AdventureWorks e descobri que a ordem de processamento é algo como:

  1. Select DISTINCT Values from Table and order
  2. Select the TOP x lines from the results in step 1 and display.

If you want to try this yourself start with

SELECT FirstName,
LastName
FROM Person.Person
ORDER BY LastName

And notice the results. Mantenha-se a par do “Kim Ambercombie”. Repare como há três entradas para o seu nome.

Resultados ordenados por LastName

Agora correr

SELECT DISTINCT FirstName,
LastName
FROM Person.Person
ORDER BY LastName

E verá que “Kim Ambercombine” é mostrado apenas uma vez.

>
Lista de usuários ordenados por LastName

Então corra

SELECT DISTINCT TOP 10 FirstName,
LastName
FROM Person.Person
ORDER BY LastName

E você verá que retorna os 10 primeiros nomes únicos, primeiro e último nomes ordenados por LastName.

Primeira 10 linhas únicas ordenadas por LastName

Se você estiver se perguntando o que acontece primeiro, as operações DISTINCT ou TOP 10, então compare os resultados das duas últimas consultas.

Note que a consulta “DISTINCT TOP 10” inclui as 10 primeiras linhas da consulta “DISTINCT”.

A partir disto sabemos que primeiro é criada uma lista DISTINCT, e depois retornam os itens TOP 10.

Plano de consulta mostrando a ordem de execução

Pode também confirmar isto mostrando o plano de consulta. Para fazer isso, selecione Query -> Incluir plano de consulta real do menu antes de executar a consulta.

O ícone “Stream Aggregate” é para a operação DISTINCT e “Top” para o TOP 10 um.

Pode parecer um pouco contra-intuitivo ver DISTINCT listado primeiro dentro da instrução SELECT. Apenas tenha em mente que SQL não é necessariamente processado na ordem que um humano o leria da esquerda para a direita.

DISTINCT e TOP com a lista SELECT Expressions

A segunda parte da pergunta de Nan relacionada a como as expressões são tratadas com o operador DISTINCT.

Expressões são tratadas da mesma forma que a coluna referente a DISTINCT e TOP. Vamos começar com um comando select para obter o primeiro nome bem como o completo, que criamos anexando LastName ao FirstName.

Also, tenha em mente, quando usar ORDER BY, que os itens ORDER BY devem aparecer na lista select quando usar Distinct. Dado isto tenho que modificar a declaração apresentada na pergunta original:

SELECT DISTINCT FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY LastName

Não será executado uma vez que LastName não está na lista SELECT. Sim, é parte de uma expressão na lista select, mas não está lá por si só. É válido para ordenar por FullName.

Usaremos esta ordenação nos exemplos abaixo.

A afirmação

SELECT FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY FirstName + ' ' + LastName

Retorna as linhas de 19972. Quando adicionamos Distinct

SELECT DISTINCT FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY FirstName + ' ' + LastName

Então 19516 filas são devolvidas. Finalmente adicionando Top 10, retorna as primeiras 10 combinações distintas de nomes.

SELECT DISTINCT TOP 10 FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY FirstName + ' ' + LastName

Tente executar estas consultas na base de dados AdventureWork e você vê para si mesmo que o comportamento é o mesmo que encontramos quando trabalhamos exclusivamente com colunas.

Deixe uma resposta

O seu endereço de email não será publicado.