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.
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 o los índices disponibles. El plan de ejecución es la estrategia elegida por SQL Server para obtener el resultado; un índice es una estructura que organiza valores para facilitar determinados accesos.
ORDER BY Expresion1 ASC, Expresion2 DESC
ASC ordena de menor a mayor y DESC, de mayor a menor. Podemos combinar varias expresiones para resolver empates.
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
TOP limita cuántas filas entrega la consulta. La cantidad se escribe después de SELECT y no determina por sí sola cuáles filas deben considerarse primero.
SELECT TOP (Cantidad)
Columna1,
Columna2
FROM dbo.Tabla;
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
DISTINCT compara todas las expresiones seleccionadas y conserva una sola fila de cada combinación diferente.
SELECT DISTINCT
Columna1,
Columna2
FROM dbo.Tabla;
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 —cuántas filas de un lado pueden coincidir con una fila del otro— 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 de ventana: cálculos que observan filas relacionadas sin convertirlas en una sola fila. ROW_NUMBER, RANK y DENSE_RANK realizan ese trabajo mediante OVER y se explican en su referencia correspondiente.