SQL DISTINCT y TOP en la misma consulta

Este artículo está inspirado en una serie de preguntas que una de mis lectoras, Nan, me envió recientemente con respecto a DISTINCT, TOP y ORDER BY.

Todos los ejemplos de esta lección se basan en Microsoft SQL Server Management Studio y la base de datos AdventureWorks2012. Puede empezar a utilizar estas herramientas gratuitas utilizando mi Guía de introducción al uso de SQL Server.

¿Cómo funcionan los modificadores SQL Top y SELECT Distinct para producir resultados?

Aquí está la pregunta que me envió originalmente Nan:

Estoy un poco confundido sobre SELECT DISTINCT y SELECT. Por ejemplo,

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

¿Esto busca nombres distintos? ¿se trata de nombres y apellidos distintos y combinados? ¿Cómo distinguimos entre las columnas usadas para la evaluación distintiva y las columnas que sólo queremos mostrar en la salida?

¿Qué pasa con

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

Pensé que a todo el mundo le gustaría saber la respuesta, así que creé una entrada en el blog.

DISTINCT y TOP – ¿Qué es lo primero?

Veamos la primera sentencia cuyo propósito es devolver una lista única de nombres y apellidos.

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

TOP 10 devolverá los diez primeros elementos del conjunto ordenado, y DISTINCT eliminará cualquier duplicado. La pregunta es ¿qué sucede primero?

  • ¿Se ordena la tabla por LastName y se toman los diez primeros elementos, y luego se eliminan los duplicados?
  • ¿O se eliminan los duplicados, y luego se ordenan los elementos y se muestran los diez primeros elementos?

Antes de responder a esta pregunta ten en cuenta que DISTINCT opera sobre todas las columnas y expresiones de la cláusula SELECT. Así que, en este caso, la sentencia devolverá filas distintas para FirstName y LastName.

Desgraciadamente, no hay una forma directa de utilizar DISTINCT en un conjunto de campos y mostrar otros. Una vez que añades columnas a la sentencia SELECT pasan a estar bajo la influencia del operador DISTINCT. Digo directa, ya que podría obtener una lista distinta, y luego utilizar un INNER JOIN para tirar de otras columnas. Sin embargo, hay peligros al hacer eso, ya que la unión puede reintroducir duplicados.

Añadir una cláusula TOP a DISTINCT es interesante. No estaba seguro de lo que sucedería, pero hice algunos experimentos con la base de datos AdventureWorks y descubrí que el orden de procesamiento es algo así:

  1. Seleccione los valores DISTINCT de la tabla y ordene
  2. Seleccione las x filas superiores de los resultados del paso 1 y muéstrelas.

Si quiere probar esto usted mismo comience con

SELECT FirstName,
LastName
FROM Person.Person
ORDER BY LastName

Y observe los resultados. Sigue la pista de «Kim Ambercombie». Observa cómo hay tres entradas para su nombre.

Resultados ordenados por Apellido

Ahora ejecuta

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

Y verás que «Kim Ambercombine» aparece sólo una vez.

Lista de nombres únicos ordenados por Apellido

Entonces ejecuta

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

Y verás que devuelve los 10 primeros nombres y apellidos únicos ordenados por Apellido.

Las 10 primeras filas únicas ordenadas por LastName

Si te preguntas qué ocurre primero, las operaciones DISTINCT o TOP 10, entonces compara los resultados de las dos últimas consultas.

Nota que la consulta «DISTINCT TOP 10» incluye las 10 primeras filas de la consulta «DISTINCT».

De esto sabemos que primero se crea una lista DISTINTA y luego se devuelven los 10 primeros elementos.

Plan de consulta mostrando el orden de ejecución

También puede confirmar esto mostrando el plan de consulta. Para ello, seleccione Consulta -> Incluir plan de consulta real en el menú antes de ejecutar la consulta.

El icono «Stream Aggregate» es para la operación DISTINCT y «Top» para la TOP 10.

Puede parecer algo contraintuitivo ver que DISTINCT aparece primero dentro de la sentencia SELECT. Sólo hay que tener en cuenta que SQL no se procesa necesariamente en el orden en que un humano lo leería de izquierda a derecha.

DISTINCT y TOP con expresiones de lista SELECT

La segunda parte de la pregunta de Nan se refería a cómo se tratan las expresiones con el operador DISTINCT.

Las expresiones se tratan igual que las columnas en cuanto a DISTINCT y TOP. Empecemos con una sentencia select para obtener tanto el nombre como el completo, que creamos anexando LastName a FirstName.

Además, hay que tener en cuenta, al utilizar ORDER BY, que los elementos ORDER BY deben aparecer en la lista select al utilizar Distinct. Dado esto tengo que modificar la sentencia presentada en la pregunta original:

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

No se ejecuta ya que LastName no está en la lista SELECT. Sí, es parte de una expresión en la lista select, pero no está ahí por sí sola. Es válido ordenar por FullName.

Usaremos esta ordenación en los ejemplos siguientes.

La sentencia

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

Devuelve 19972 filas. Cuando añadimos Distinct

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

Entonces se devuelven 19516 filas. Por último añadiendo Top 10, devuelve las 10 primeras combinaciones de nombres distintos.

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

Prueba a ejecutar estas consultas en la base de datos AdventureWork y comprueba por ti mismo que el comportamiento es el mismo que encontramos cuando trabajamos exclusivamente con columnas.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.