Desde la primera lección del módulo venimos repitiendo la misma advertencia: el conjunto de resultados no tiene orden garantizado. Ha llegado el momento de tomar el control. ORDER BY es la única forma de que una consulta devuelva las filas en un orden concreto, y aunque parezca la cláusula más sencilla de SQL, esconde media docena de detalles que separan una consulta que funciona por casualidad de una que funciona siempre: qué pasa con los empates, dónde van los nulos, por qué la Ñ puede aparecer en un sitio raro, y por qué ordenar por el número de columna es una bomba de relojería.

Contenido

  1. Sin ORDER BY no hay orden garantizado
  2. ASC y DESC
  3. Ordenar por varias columnas y resolver empates
  4. Ordenar por alias, por expresión y por posición ordinal
  5. Los nulos: NULLS FIRST y NULLS LAST
  6. Ordenar texto: colación y acentos
  7. Ordenar fechas
  8. ORDER BY en el orden lógico de ejecución
  9. Errores Comunes y Consejos
  10. Ejercicios
  11. Conclusión

  1. Sin ORDER BY no hay orden garantizado

Vamos a hacerlo explícito de una vez. Esta consulta:

SELECT id, nombre, precio FROM productos;

devuelve, hoy, en tu equipo, los productos del 1 al 20. Y eso te da una falsa sensación de seguridad, porque el estándar SQL no promete ningún orden cuando no hay ORDER BY, y PostgreSQL tampoco. Lo que ves es el orden en que el motor encontró las filas al recorrer el fichero de datos.

Circunstancias reales en las que ese orden cambia sin que tú toques la consulta:

Situación Qué ocurre
Se actualiza una fila con UPDATE PostgreSQL escribe una versión nueva de la fila al final de la tabla; esa fila pasa a devolverse la última
El optimizador decide usar un índice Las filas salen en el orden del índice, no en el físico
La tabla es grande y se activa el paralelismo Varios procesos leen trozos distintos y entregan resultados entrelazados
Se ejecuta VACUUM FULL o se reconstruye la tabla El orden físico cambia por completo
Se añade una tabla con JOIN (módulo 3) El orden depende del algoritmo de unión elegido

Puedes comprobar el primer caso tú mismo, si te atreves a modificar los datos (recuerda que el script de 01-06 es idempotente y puedes recargarlo):

UPDATE productos SET stock = stock WHERE id = 1;
SELECT id, nombre FROM productos;

El producto 1 aparecerá ahora al final de la lista. Nada ha fallado, nada avisa: simplemente el orden nunca estuvo garantizado.

Regla absoluta: si el orden de las filas importa para tu informe, tu aplicación o tu paginación, escribe ORDER BY. No hay atajos, no hay excepciones, y "es que siempre sale bien" no es un argumento.

  1. ASC y DESC

ORDER BY se escribe al final de la consulta e indica por qué columna ordenar y en qué sentido:

SELECT id, nombre, precio
FROM productos
ORDER BY precio DESC;
id nombre precio
15 Té verde matcha ceremonial 30 g 22.00
6 Crema facial de aloe vera 50 ml 18.90
20 Cápsulas de espirulina 120 uds 16.40
8 Aceite corporal de almendras 200 ml 14.25
13 Velas de cera de soja (pack 2) 13.75
1 Aceite de oliva virgen extra 500 ml 12.50
10 Detergente ecológico concentrado 1 L 11.20
12 Bolsas reutilizables de algodón (pack 5) 9.90
3 Miel de azahar cruda 500 g 9.75
7 Champú sólido de romero 80 g 8.40
19 Desodorante natural en barra 50 g 7.80
11 Estropajo vegetal de luffa (pack 3) 5.50
17 Zumo de naranja prensado en frío 1 L 5.40
16 Kombucha de jengibre 750 ml 4.95
9 Bálsamo labial de caléndula 15 ml 4.60
2 Arroz integral ecológico 1 kg 3.90
18 Cepillo de dientes de bambú 3.50
14 Infusión de manzanilla ecológica 20 uds 3.25
4 Pasta de espelta 500 g 2.80
5 Tomate triturado ecológico 400 g 1.95

El catálogo completo de más caro a más barato. Ahí tienes ya una respuesta de negocio: el matcha es el producto más caro (22.00 €) y el tomate triturado el más barato (1.95 €).

Palabra clave Significado ¿Es el valor por defecto?
ASC Ascendente: de menor a mayor, de A a Z, de fecha antigua a reciente
DESC Descendente: al revés No

Como ASC es el valor por defecto, estas dos son idénticas:

ORDER BY precio;
ORDER BY precio ASC;

Escribir ASC explícitamente no es obligatorio, pero en consultas con varias columnas y sentidos mezclados ayuda mucho a la lectura.

  1. Ordenar por varias columnas y resolver empates

Cuando dos filas tienen el mismo valor en la columna de ordenación, su orden relativo no está definido. Es el mismo problema de la sección 1, en pequeño.

SELECT id, nombre, categoria_id, precio
FROM productos
ORDER BY categoria_id;

Las cinco filas de la categoría 1 salen juntas, sí, pero ¿en qué orden entre ellas? El que el motor tenga a bien. Para fijarlo se añaden más columnas al ORDER BY, separadas por comas: la segunda desempata la primera, la tercera desempata la segunda, y así sucesivamente. Cada columna puede llevar su propio ASC o DESC.

SELECT id, nombre, categoria_id, precio
FROM productos
ORDER BY categoria_id ASC, precio DESC;
id nombre categoria_id precio
1 Aceite de oliva virgen extra 500 ml 1 12.50
3 Miel de azahar cruda 500 g 1 9.75
2 Arroz integral ecológico 1 kg 1 3.90
4 Pasta de espelta 500 g 1 2.80
5 Tomate triturado ecológico 400 g 1 1.95
6 Crema facial de aloe vera 50 ml 2 18.90
8 Aceite corporal de almendras 200 ml 2 14.25
7 Champú sólido de romero 80 g 2 8.40
9 Bálsamo labial de caléndula 15 ml 2 4.60
13 Velas de cera de soja (pack 2) 3 13.75
10 Detergente ecológico concentrado 1 L 3 11.20
12 Bolsas reutilizables de algodón (pack 5) 3 9.90
11 Estropajo vegetal de luffa (pack 3) 3 5.50
15 Té verde matcha ceremonial 30 g 4 22.00
17 Zumo de naranja prensado en frío 1 L 4 5.40
16 Kombucha de jengibre 750 ml 4 4.95
14 Infusión de manzanilla ecológica 20 uds 4 3.25
19 Desodorante natural en barra 50 g 5 7.80
18 Cepillo de dientes de bambú 5 3.50
20 Cápsulas de espirulina 120 uds 6 16.40

Un catálogo perfectamente presentable: agrupado por categoría y, dentro de cada una, del artículo más caro al más barato.

Tres columnas, con datos de clientes:

SELECT id, nombre, apellidos, ciudad, pais
FROM clientes
ORDER BY pais, ciudad, apellidos;
id nombre apellidos ciudad pais
11 Elena Navarro Puig Alicante España
5 Ana Belmonte Roca Barcelona España
13 Núria Bosch Ferrer Barcelona España
3 Marta Sanchis Gil Castellón España
4 Javier Ortega Ruiz Madrid España
12 Diego Ramos Herrera Sevilla España
15 Inés Carrasco Vega Valencia España
2 Carlos Ferrer Ibáñez Valencia España
6 Pau Llorens Vidal Valencia España
1 Lucía Martínez Soler Valencia España
14 Hugo Iglesias Pardo Zaragoza España
9 Camille Dubois Lyon Francia
10 Julien Moreau París Francia
7 Sofia Moreira Costa Lisboa Portugal
8 Tiago Almeida Nunes Oporto Portugal

Léelo de fuera adentro: primero todos los españoles, dentro de ellos las ciudades en orden alfabético, y dentro de cada ciudad los apellidos. Los dos clientes de Barcelona quedan ordenados por apellido (Belmonte antes que Bosch) y los cuatro de Valencia también (Carrasco, Ferrer, Llorens, Martínez).

Consejo profesional: termina siempre el ORDER BY con una columna única. Añadir , id como último criterio garantiza que el orden sea determinista: la misma consulta devuelve exactamente el mismo orden hoy y dentro de un año. Es imprescindible para la paginación, que verás en 02-06, y para que las pruebas automatizadas no fallen aleatoriamente.

  1. Ordenar por alias, por expresión y por posición ordinal

4.1. Por alias

Aquí se cobra la deuda de 02-02: como ORDER BY se ejecuta después de SELECT, el alias ya existe y se puede usar.

SELECT nombre,
       precio,
       coste,
       precio - coste AS margen
FROM productos
ORDER BY margen DESC, id;
nombre precio coste margen
Té verde matcha ceremonial 30 g 22.00 12.50 9.50
Crema facial de aloe vera 50 ml 18.90 9.50 9.40
Cápsulas de espirulina 120 uds 16.40 8.70 7.70
Aceite corporal de almendras 200 ml 14.25 7.10 7.15
Velas de cera de soja (pack 2) 13.75 6.90 6.85
Bolsas reutilizables de algodón (pack 5) 9.90 4.30 5.60
Detergente ecológico concentrado 1 L 11.20 6.00 5.20
Champú sólido de romero 80 g 8.40 3.60 4.80
Aceite de oliva virgen extra 500 ml 12.50 7.80 4.70
Desodorante natural en barra 50 g 7.80 3.30 4.50
Miel de azahar cruda 500 g 9.75 5.40 4.35
Estropajo vegetal de luffa (pack 3) 5.50 2.20 3.30
Bálsamo labial de caléndula 15 ml 4.60 1.80 2.80
Zumo de naranja prensado en frío 1 L 5.40 2.60 2.80
Kombucha de jengibre 750 ml 4.95 2.30 2.65
Cepillo de dientes de bambú 3.50 1.20 2.30
Infusión de manzanilla ecológica 20 uds 3.25 1.40 1.85
Arroz integral ecológico 1 kg 3.90 2.10 1.80
Pasta de espelta 500 g 2.80 1.35 1.45
Tomate triturado ecológico 400 g 1.95 0.90 1.05

Observa el empate real en la fila 13 y 14: el bálsamo labial (producto 9) y el zumo de naranja (producto 17) dejan exactamente 2.80 € de margen. El , id final es lo que hace que el 9 salga antes que el 17 de forma reproducible; sin él, el orden entre ambos sería cosa del motor.

Y fíjate en que hemos ordenado por id sin proyectarlo. Eso es perfectamente legal (a diferencia de lo que ocurría con SELECT DISTINCT en 02-04): ORDER BY puede usar cualquier columna de las tablas del FROM, aparezca o no en el resultado.

4.2. Por expresión

También puedes repetir la expresión completa en lugar de usar el alias. El resultado es idéntico:

SELECT nombre, precio, coste
FROM productos
ORDER BY (precio - coste) / precio DESC, id;
nombre precio coste
Cepillo de dientes de bambú 3.50 1.20
Bálsamo labial de caléndula 15 ml 4.60 1.80
Estropajo vegetal de luffa (pack 3) 5.50 2.20
Desodorante natural en barra 50 g 7.80 3.30
Champú sólido de romero 80 g 8.40 3.60

(5 primeras de 20 filas.)

Este es el ranking por margen porcentual, no por margen absoluto, y da un resultado muy distinto: el cepillo de bambú, que solo deja 2.30 € por unidad, es el producto que más porcentaje de margen aporta (65.7 %). Ordenar por la métrica correcta importa tanto como calcularla bien.

4.3. Por posición ordinal

SQL permite ordenar por el número de columna dentro del SELECT:

SELECT nombre, precio
FROM productos
ORDER BY 2 DESC;

Equivale a ORDER BY precio DESC, porque precio es la segunda columna de la lista.

Funciona, es estándar y está muy extendido en consultas rápidas y exploratorias. Pero es frágil y no debe llegar a código que se guarde:

Riesgo Escenario
Alguien añade una columna al principio del SELECT ORDER BY 2 pasa a ordenar por otra cosa, sin error
Alguien reordena la lista de columnas Igual
Quien lea la consulta Tiene que contar columnas para entender qué hace
Combinado con SELECT * El significado depende de la definición de la tabla, que puede cambiar

Regla del curso: ORDER BY 2 para explorar en psql; nombre de columna o alias en cualquier consulta que vayas a guardar.

Un matiz que sorprende: el ordinal solo funciona en ORDER BY (y en GROUP BY, módulo 4). En WHERE no significa nada, y ORDER BY 2 + 1 no ordena por la tercera columna: PostgreSQL entiende que ahí hay una expresión constante (el número 3) y la ignora como criterio de ordenación, porque vale lo mismo en todas las filas.

  1. Los nulos: NULLS FIRST y NULLS LAST

Un NULL no es mayor ni menor que nada: es desconocido. Pero para ordenar hay que colocarlo en algún sitio, así que cada motor toma una decisión arbitraria. PostgreSQL considera que NULL es el valor más grande.

De ahí salen sus valores por defecto:

Sentido Dónde van los nulos en PostgreSQL
ASC Al final (equivale a NULLS LAST)
DESC Al principio (equivale a NULLS FIRST)

Veámoslo con pedidos.empleado_id, que es NULL en los diez pedidos que entraron por la web:

SELECT id, cliente_id, empleado_id, estado
FROM pedidos
ORDER BY empleado_id, id;
id cliente_id empleado_id estado
2 2 4 entregado
6 5 4 cancelado
10 9 4 entregado
16 4 4 enviado
4 4 5 entregado
8 7 5 entregado
12 10 5 entregado
18 5 5 pagado
14 12 6 entregado
20 9 6 pendiente
1 1 (null) entregado
3 3 (null) entregado
5 1 (null) entregado
7 6 (null) entregado
9 8 (null) entregado
11 2 (null) entregado
13 11 (null) entregado
15 1 (null) entregado
17 7 (null) enviado
19 6 (null) entregado

Los diez pedidos gestionados por comerciales (4, 5 y 6) primero, y los diez pedidos web al final. Si quieres los nulos arriba, lo pides explícitamente:

SELECT id, cliente_id, empleado_id
FROM pedidos
ORDER BY empleado_id NULLS FIRST, id;

Ahora los pedidos 1, 3, 5, 7, 9, 11, 13, 15, 17 y 19 encabezan la lista, seguidos por los del comercial 4, luego el 5 y luego el 6.

Lo mismo con clientes.referido_por_id:

SELECT id, nombre, apellidos, referido_por_id
FROM clientes
ORDER BY referido_por_id NULLS FIRST, id;
id nombre apellidos referido_por_id
1 Lucía Martínez Soler (null)
4 Javier Ortega Ruiz (null)
6 Pau Llorens Vidal (null)
7 Sofia Moreira Costa (null)
9 Camille Dubois (null)
12 Diego Ramos Herrera (null)
14 Hugo Iglesias Pardo (null)
2 Carlos Ferrer Ibáñez 1
3 Marta Sanchis Gil 1
15 Inés Carrasco Vega 1
5 Ana Belmonte Roca 2
13 Núria Bosch Ferrer 5
11 Elena Navarro Puig 6
8 Tiago Almeida Nunes 7
10 Julien Moreau 9

Los siete clientes que llegaron por su cuenta arriba, y debajo los ocho que vinieron por recomendación, agrupados por quién los trajo: Lucía ha traído a tres (Carlos, Marta e Inés), lo que la convierte en la mejor prescriptora de TiendaVerde.

5.1. Comparativa entre motores

Esta es una de las diferencias de dialecto que más quebraderos de cabeza da al migrar consultas:

Motor NULL se considera ORDER BY col ASC ORDER BY col DESC ¿Admite NULLS FIRST/LAST?
PostgreSQL El mayor Nulos al final Nulos al principio
Oracle El mayor Nulos al final Nulos al principio
MySQL / MariaDB El menor Nulos al principio Nulos al final No
SQLite El menor Nulos al principio Nulos al final Sí (desde 3.30)
SQL Server El menor Nulos al principio Nulos al final No

Es decir: la misma consulta ORDER BY empleado_id pone los diez pedidos web al final en PostgreSQL y al principio en MySQL. Si tu informe muestra las diez primeras filas, verás datos completamente distintos.

En los motores que no admiten NULLS FIRST/LAST se emula con una columna auxiliar de ordenación, aprovechando que un booleano ordena false antes que true:

-- Portable a MySQL y SQL Server: nulos al final aunque sea ASC
ORDER BY (empleado_id IS NULL), empleado_id;

La expresión empleado_id IS NULL vale false (0) para las filas con valor y true (1) para las nulas, así que ordenar por ella empuja los nulos al final. Es un truco que conviene tener en el bolsillo.

  1. Ordenar texto: colación y acentos

Ordenar números o fechas es aritmética. Ordenar texto es cultura, y ahí las cosas se ponen interesantes.

Una colación (collation) es el conjunto de reglas que decide si 'Ñora' va antes o después de 'Oliva', si 'café' y 'CAFE' se consideran iguales al ordenar, y si 'ch' cuenta como una letra o como dos. Cada base de datos PostgreSQL se crea con una colación por defecto, que puedes consultar:

tiendaverde=> SHOW lc_collate;

Los dos escenarios habituales:

Colación Cómo ordena Efecto sobre Ñ y acentos
C o POSIX Por valor de byte en UTF-8 Las mayúsculas van antes que todas las minúsculas, y las letras acentuadas van después de toda la A-Z. Ñora acaba después de Zulema
es_ES.UTF-8, en_US.UTF-8, ICU Reglas lingüísticas Ñ va entre N y O; los acentos solo desempatan; las mayúsculas se intercalan con las minúsculas

Puedes comprobarlo en tu instalación con una comparación directa:

SELECT 'Ñora' < 'Oliva'                                 AS colacion_de_la_base,
       ('Ñora' COLLATE "C") < ('Oliva' COLLATE "C")     AS colacion_c;

En una base creada con colación lingüística (lo habitual):

colacion_de_la_base colacion_c
true false

'Ñora' < 'Oliva' es verdadero con reglas lingüísticas —la Ñ va antes que la O— y falso con la colación C, porque en UTF-8 la Ñ se codifica con dos bytes que empiezan por 0xC3, muy por encima del 0x4F de la O. La misma consulta, dos órdenes distintos, según cómo se creara la base de datos.

Para forzar una colación concreta en una consulta:

SELECT id, nombre, apellidos, ciudad
FROM clientes
ORDER BY apellidos COLLATE "es-ES-x-icu";
id nombre apellidos ciudad
8 Tiago Almeida Nunes Oporto
5 Ana Belmonte Roca Barcelona
13 Núria Bosch Ferrer Barcelona
15 Inés Carrasco Vega Valencia
9 Camille Dubois Lyon
2 Carlos Ferrer Ibáñez Valencia
14 Hugo Iglesias Pardo Zaragoza
6 Pau Llorens Vidal Valencia
1 Lucía Martínez Soler Valencia
10 Julien Moreau París
7 Sofia Moreira Costa Lisboa
11 Elena Navarro Puig Alicante
4 Javier Ortega Ruiz Madrid
12 Diego Ramos Herrera Sevilla
3 Marta Sanchis Gil Castellón

COLLATE "es-ES-x-icu" aplica las reglas del español según la biblioteca ICU, que PostgreSQL incluye desde la versión 10. Fíjate en Moreau antes que Moreira: comparten More, y luego a va antes que i.

Puedes ver las colaciones disponibles en tu servidor con:

tiendaverde=> \dOS

Notas prácticas sobre colaciones:

  • La colación afecta también a WHERE, a LIKE y a los índices. Un índice creado con una colación no sirve para ordenar con otra (módulo 8).
  • Ordenar con COLLATE explícito es más lento que usar la colación por defecto, porque impide reutilizar el índice.
  • Cambiar la colación de una base existente es una operación mayor: hay que reconstruir todos los índices de texto. Es una decisión que se toma al crear la base de datos.
  • Si necesitas un orden "sin acentos ni mayúsculas", la vía moderna en PostgreSQL son las colaciones no deterministas (CREATE COLLATION ... deterministic = false); la vía clásica es ordenar por LOWER(unaccent(columna)), con funciones del módulo 6.

  1. Ordenar fechas

Las fechas ordenan cronológicamente, sin sorpresas, porque el tipo DATE es un número por dentro:

SELECT id, cliente_id, fecha_pedido, estado, gastos_envio
FROM pedidos
ORDER BY fecha_pedido DESC;
id cliente_id fecha_pedido estado gastos_envio
20 9 2026-02-21 pendiente 12.50
19 6 2026-02-09 pagado 4.95
18 5 2026-01-27 pagado 4.95
17 7 2026-01-13 enviado 9.90
16 4 2025-12-19 enviado 4.95
15 1 2025-12-02 entregado 0.00
14 12 2025-11-14 entregado 4.95
13 11 2025-10-22 entregado 4.95
12 10 2025-10-01 entregado 12.50
11 2 2025-09-09 entregado 0.00

(10 primeras de 20 filas.)

Los pedidos del más reciente al más antiguo: el orden natural de cualquier bandeja de trabajo. En este conjunto de datos la fecha crece con el id, así que ordenar por fecha descendente coincide con ordenar por id descendente; en una base real eso no tiene por qué cumplirse (un pedido puede grabarse con fecha retroactiva) y confiar en ello sería un error.

Aviso importante: esto funciona porque las columnas son de tipo DATE. Si una fecha estuviera guardada como texto, el orden sería alfabético:

Formato guardado como texto Orden alfabético resultante
'2026-01-13' (ISO) Coincide con el cronológico, por casualidad afortunada
'13/01/2026' (europeo) Desastroso: el 13 de enero de 2026 iría junto al 13 de marzo de 1998

Es una razón más para guardar las fechas en columnas de tipo fecha, como se decidió en 01-04 y 01-06.

  1. ORDER BY en el orden lógico de ejecución

ORDER BY es el penúltimo paso: se ejecuta después de que las filas estén filtradas, proyectadas y deduplicadas, y solo LIMIT viene después.

flowchart LR
    A["1 · FROM"] --> B["2 · WHERE"]
    B --> C["3 · SELECT<br/>nacen los alias"]
    C --> D["3b · DISTINCT"]
    D --> E["4 · ORDER BY<br/>✅ puede usar alias<br/>✅ y columnas no proyectadas"]
    E --> F["5 · LIMIT<br/>(02-06)"]

Consecuencias que ya has visto en acción:

Puede ORDER BY... Respuesta Por qué
¿Usar un alias del SELECT? El alias ya existe: SELECT se ejecutó antes
¿Usar una columna que no está en el SELECT? (salvo con DISTINCT) Las columnas de la tabla siguen disponibles
¿Usar una expresión calculada? Se evalúa sobre las filas del resultado
¿Usar el ordinal de columna? Es una comodidad del estándar
¿Usar un alias con SELECT DISTINCT sobre otra columna? No Restricción vista en 02-04

Y una consideración de rendimiento que retomarás en el módulo 8: ordenar cuesta. PostgreSQL puede evitar el trabajo si existe un índice que ya devuelve las filas en el orden pedido; si no, tiene que ordenar todo el conjunto en memoria (o en disco, si no cabe). Un ORDER BY sobre una tabla de millones de filas sin índice adecuado es una de las causas más frecuentes de consultas lentas.

Un último apunte: si una consulta con ORDER BY se usa como subconsulta (módulo 7) o dentro de una vista (módulo 10), ese orden no se propaga necesariamente a la consulta exterior. El ORDER BY que manda es el del nivel más externo.

Errores Comunes y Consejos

  • Confiar en el orden sin ORDER BY. Funciona hasta el día que no. Es el error más caro de esta lección.
  • Olvidar el desempate. Dos filas con el mismo valor pueden salir en cualquier orden, y ese orden puede cambiar entre ejecuciones. Termina siempre con una columna única.
  • Poner DESC una sola vez creyendo que afecta a toda la lista. ORDER BY a, b DESC ordena a ascendente y b descendente. Si quieres las dos descendentes: ORDER BY a DESC, b DESC.
  • Ordenar por ordinal en código de producción. ORDER BY 2 se rompe en silencio cuando alguien reordena el SELECT.
  • Suponer que los nulos van donde tú crees. PostgreSQL los pone al final en ASC; MySQL, al principio. Si importa, escribe NULLS FIRST o NULLS LAST.
  • Ordenar fechas guardadas como texto. Orden alfabético, no cronológico. Usa el tipo DATE.
  • Ordenar números guardados como texto. '10' < '9' es verdadero alfabéticamente. Mismo problema.
  • Sorprenderse del sitio de la Ñ o de los acentos. Depende de la colación de la base. Compruébala con SHOW lc_collate;.
  • Consejo: usa COLLATE solo cuando de verdad haga falta, porque impide aprovechar los índices.
  • Consejo: para informes, ordena por lo que el lector busca. Un listado de productos se ordena por nombre si van a buscar uno concreto, y por precio si van a comparar.
  • Consejo: comprueba el orden con los extremos. Mira la primera y la última fila: suelen delatar de inmediato un ASC/DESC invertido o unos nulos mal colocados.

Ejercicios

Ejercicio 1

Dirección quiere el listado del catálogo tal como aparecerá en la web: solo productos activos, agrupados por categoría de menor a mayor, y dentro de cada categoría por nombre alfabético. Muestra categoria_id, id, nombre y precio. Explica por qué el resultado tiene 19 filas.

Ejercicio 2

Recursos humanos pide la plantilla ordenada por salario de mayor a menor, mostrando nombre, apellidos, puesto, salario y jefe_id. Añade lo necesario para que el orden sea determinista y responde: ¿dónde aparece Rosa Alcázar Vives y por qué su jefe_id nulo no afecta al orden?

Ejercicio 3

Sobre lineas_pedido, obtén las líneas ordenadas por importe de mayor a menor, mostrando id, pedido_id, producto_id, cantidad y el importe redondeado a dos decimales. Escribe la consulta de dos formas —usando el alias y repitiendo la expresión— y explica por qué ambas funcionan aquí pero solo una de ellas funcionaría en un WHERE.

Soluciones

Solución 1

SELECT categoria_id,
       id,
       nombre,
       precio
FROM productos
WHERE activo
ORDER BY categoria_id, nombre;
categoria_id id nombre precio
1 1 Aceite de oliva virgen extra 500 ml 12.50
1 2 Arroz integral ecológico 1 kg 3.90
1 3 Miel de azahar cruda 500 g 9.75
1 4 Pasta de espelta 500 g 2.80
1 5 Tomate triturado ecológico 400 g 1.95
2 8 Aceite corporal de almendras 200 ml 14.25
2 9 Bálsamo labial de caléndula 15 ml 4.60
2 7 Champú sólido de romero 80 g 8.40
2 6 Crema facial de aloe vera 50 ml 18.90
3 12 Bolsas reutilizables de algodón (pack 5) 9.90
3 10 Detergente ecológico concentrado 1 L 11.20
3 11 Estropajo vegetal de luffa (pack 3) 5.50
3 13 Velas de cera de soja (pack 2) 13.75
4 14 Infusión de manzanilla ecológica 20 uds 3.25
4 16 Kombucha de jengibre 750 ml 4.95
4 15 Té verde matcha ceremonial 30 g 22.00
4 17 Zumo de naranja prensado en frío 1 L 5.40
5 18 Cepillo de dientes de bambú 3.50
5 19 Desodorante natural en barra 50 g 7.80

19 filas porque WHERE activo descarta el producto 20 (Cápsulas de espirulina), el único descatalogado. Y por eso la categoría 6 no aparece en el listado: era su único producto.

Dos observaciones sobre el orden:

  • Dentro de cada categoría, el id ya no es creciente: la categoría 2 empieza por el producto 8 porque "Aceite corporal" va antes alfabéticamente que "Bálsamo", "Champú" y "Crema". Es la prueba de que el orden lo manda el ORDER BY y no la tabla.
  • En la categoría 2, "Champú sólido" va antes que "Crema facial" porque comparten la C inicial y desempata la segunda letra (h antes que r); las tildes de "Champú" y "Bálsamo" no intervienen aquí porque están más allá del punto en que ya se ha decidido el orden. Si algún producto empezara por una letra acentuada, en una base con colación C aparecería al final de todo el listado. Es el efecto de la sección 6.

Solución 2

SELECT nombre,
       apellidos,
       puesto,
       salario,
       jefe_id
FROM empleados
ORDER BY salario DESC, id;
nombre apellidos puesto salario jefe_id
Rosa Alcázar Vives Directora general 62000.00 (null)
Andrés Company Talens Responsable de ventas 41000.00 1
Beatriz Nadal Ripoll Responsable de logística 39500.00 1
Daniel Vercher Lluch Analista de datos 35000.00 1
Óscar Peris Blasco Comercial 28500.00 2
Laia Puig Sanchis Comercial 27800.00 2
Marc Estévez Roig Atención al cliente 24500.00 2
Irene Salvador Mira Operaria de almacén 22000.00 3

Rosa Alcázar Vives aparece la primera, y no por su jefe_id nulo sino porque tiene el salario más alto (62 000 €). Su jefe_id nulo no influye en absoluto en el orden, porque esa columna no participa en el ORDER BY: aparece en el resultado pero no como criterio de ordenación. Es una distinción importante: los nulos solo alteran el orden de las columnas por las que ordenas.

El , id final garantiza el determinismo. En estos datos ningún salario se repite, así que no cambia nada hoy; el día que se contrate a dos comerciales con el mismo sueldo, el listado seguirá saliendo siempre igual.

Solución 3

Versión con alias:

SELECT id,
       pedido_id,
       producto_id,
       cantidad,
       ROUND(cantidad * precio_unitario * (1 - descuento), 2) AS importe
FROM lineas_pedido
ORDER BY importe DESC, id;

Versión con la expresión repetida:

SELECT id,
       pedido_id,
       producto_id,
       cantidad,
       ROUND(cantidad * precio_unitario * (1 - descuento), 2) AS importe
FROM lineas_pedido
ORDER BY cantidad * precio_unitario * (1 - descuento) DESC, id;

Ambas devuelven lo mismo. Las diez primeras filas de las 47:

id pedido_id producto_id cantidad importe
28 12 15 2 44.00
18 8 1 3 35.63
24 10 6 2 34.02
45 19 16 6 26.73
41 17 1 2 25.00
1 1 1 2 23.90
9 4 15 1 22.00
42 17 15 1 22.00
39 16 10 2 21.28
16 7 16 4 19.80

Por qué ambas funcionan aquí. ORDER BY se ejecuta después de SELECT, así que en ese momento el alias importe ya existe y la expresión también puede reevaluarse. Las dos formas son válidas y PostgreSQL genera el mismo plan.

Por qué solo una funcionaría en WHERE. WHERE se ejecuta antes que SELECT, así que el alias todavía no ha nacido: WHERE importe > 30 daría column "importe" does not exist. Únicamente la versión con la expresión completa sirve para filtrar, como viste en 02-03. Toda esta lección se apoya en esa misma asimetría del orden lógico de ejecución.

Fíjate además en el empate de la fila 7 y 8: las líneas 9 y 42 valen exactamente 22.00 € (una unidad de matcha en ambos casos). El , id final es lo que decide que salga antes la 9. Sin él, el orden entre ambas sería impredecible, y un informe paginado podría llegar a mostrar la misma línea dos veces o ninguna.

Conclusión

Ya controlas el orden de tus resultados:

  • Sin ORDER BY no hay orden garantizado. Un UPDATE, un índice o el paralelismo pueden cambiarlo sin previo aviso.
  • ASC (por defecto) y DESC deciden el sentido, y cada columna del ORDER BY lleva el suyo.
  • Ordenar por varias columnas resuelve los empates, y terminar con una columna única hace el orden determinista: imprescindible para paginar y para que las pruebas no fallen al azar.
  • Puedes ordenar por alias, por expresión y por posición ordinal; esta última solo para explorar, porque se rompe en silencio.
  • Los nulos van al final en ASC y al principio en DESC en PostgreSQL, justo al revés que en MySQL, SQLite y SQL Server. NULLS FIRST / NULLS LAST lo hace explícito.
  • El orden del texto depende de la colación de la base: con C los acentos y la Ñ van al final de todo; con reglas lingüísticas o ICU, en su sitio. COLLATE "es-ES-x-icu" fuerza el criterio español.
  • Las fechas ordenan cronológicamente si —y solo si— están guardadas en columnas de tipo fecha.
  • ORDER BY es el paso 4 del orden lógico: ya existen los alias, aún se ven todas las columnas, y solo queda LIMIT por delante.

En la última lección del módulo, Limitando resultados con LIMIT, añadirás el paso 5 y cerrarás el ciclo completo de una consulta. Verás por qué LIMIT es lo primero que se escribe al explorar una tabla desconocida, cómo se construye una paginación con OFFSET, por qué esa paginación se degrada y se descuadra en tablas grandes, y qué se hace en su lugar en las aplicaciones que van en serio.

Curso de SQL

Módulo 1: Introducción a SQL

Módulo 2: Consultas básicas de SQL

Módulo 3: Trabajando con múltiples tablas

Módulo 4: Filtrado avanzado de datos

Módulo 5: Manipulación de datos

Módulo 6: Funciones avanzadas de SQL

Módulo 7: Subconsultas y consultas anidadas

Módulo 8: Índices y optimización de rendimiento

Módulo 9: Transacciones y concurrencia

Módulo 10: Temas avanzados

Módulo 11: SQL en la práctica

Módulo 12: Proyecto final

© Copyright 2026. Todos los derechos reservados