DISTINCT, TOP y ORDER BY: reducir y ordenar un resultado

Una consulta puede devolver todas las filas que cumplen una condición y aun así no responder exactamente lo que buscamos. Quizá necesitamos conocer los estados existentes sin repetirlos, revisar solo los pedidos más recientes o presentar los precios de menor a mayor.

La idea

Estas tres construcciones afectan el resultado de formas distintas: DISTINCT elimina repeticiones, TOP limita la cantidad de filas y ORDER BY establece su orden.

Sintaxis

SELECT [DISTINCT] [TOP (cantidad)]
    Columna1,
    Columna2
FROM dbo.Tabla
ORDER BY
    Columna1 [ASC | DESC];

Los elementos entre corchetes son opcionales en esta representación. Los corchetes ayudan a leer la forma general; no deben copiarse literalmente al escribir la consulta.

ORDER BY: declarar el orden

Las tablas no tienen un orden de lectura que debamos asumir. Aunque una consulta parezca devolver siempre las filas de la misma manera, ese comportamiento puede cambiar con el plan, los índices o la ejecución.

SELECT
    p.IdProducto,
    p.Nombre,
    p.Precio
FROM dbo.Producto AS p
ORDER BY
    p.Precio DESC,
    p.IdProducto ASC;

DESC ordena el precio de mayor a menor. Cuando dos productos tienen el mismo precio, IdProducto ASC proporciona un segundo criterio estable.

TOP: limitar la cantidad de filas

Para obtener los tres productos de mayor precio:

SELECT TOP (3)
    p.IdProducto,
    p.Nombre,
    p.Precio
FROM dbo.Producto AS p
ORDER BY
    p.Precio DESC,
    p.IdProducto ASC;
IdProducto Nombre Precio
6 Disco externo 1 TB 1399.00
3 Soporte para monitor 799.00
4 Teclado compacto 629.90

TOP sin ORDER BY limita filas, pero no define cuáles son las primeras desde el punto de vista del negocio. Si pedimos “los tres más caros”, el orden forma parte de la pregunta.

También puede expresarse como porcentaje:

SELECT TOP (10) PERCENT
    p.IdProducto,
    p.Nombre,
    p.Precio
FROM dbo.Producto AS p
ORDER BY p.Precio DESC;

En resultados pequeños, el redondeo puede producir una cantidad que no parece intuitiva. Para procesos que requieren un número exacto suele ser más claro utilizar una cantidad de filas.

DISTINCT: eliminar filas repetidas

Si solo queremos conocer los estados que aparecen en los pedidos:

SELECT DISTINCT
    p.Estado
FROM dbo.Pedido AS p
ORDER BY p.Estado;
Estado
Cancelado
Confirmado
Enviado
Pendiente

DISTINCT considera juntas todas las expresiones seleccionadas. Si agregamos IdPedido, cada combinación de identificador y estado será diferente, aunque el estado se repita:

SELECT DISTINCT
    p.IdPedido,
    p.Estado
FROM dbo.Pedido AS p;

En ese caso, DISTINCT no reduce las filas porque IdPedido ya identifica cada pedido.

Qué conviene comprobar

DISTINCT puede ocultar una unión mal construida. Si aparecen duplicados después de agregar un JOIN, conviene revisar primero la cardinalidad de la relación y la condición escrita en ON.

Para resultados paginados, un orden estable necesita una columna, o combinación de columnas, que permita desempatar. Ordenar solo por fecha puede producir páginas inconsistentes cuando varias filas comparten el mismo valor.

TOP limita el resultado completo. Cuando necesitamos cierta cantidad de filas dentro de cada grupo, utilizamos funciones como ROW_NUMBER, RANK o DENSE_RANK con OVER.

Relacionado con

  • WHERE: limita las filas antes de ordenar el resultado.
  • OFFSET y FETCH: permiten paginación.
  • ROW_NUMBER, RANK y DENSE_RANK: clasifican dentro de una ventana.
  • INNER JOIN: puede multiplicar filas cuando existen varias coincidencias.