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
- Sin
ORDER BYno hay orden garantizado ASCyDESC- Ordenar por varias columnas y resolver empates
- Ordenar por alias, por expresión y por posición ordinal
- Los nulos:
NULLS FIRSTyNULLS LAST - Ordenar texto: colación y acentos
- Ordenar fechas
ORDER BYen el orden lógico de ejecución- Errores Comunes y Consejos
- Ejercicios
- Conclusión
- Sin
ORDER BY no hay orden garantizado
ORDER BY no hay orden garantizadoVamos a hacerlo explícito de una vez. Esta consulta:
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):
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.
ASC y DESC
ASC y DESCORDER BY se escribe al final de la consulta e indica por qué columna ordenar y en qué sentido:
| 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 | Sí |
DESC |
Descendente: al revés | No |
Como ASC es el valor por defecto, estas dos son idénticas:
Escribir ASC explícitamente no es obligatorio, pero en consultas con varias columnas y sentidos mezclados ayuda mucho a la lectura.
- 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.
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.
| 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:
| 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 BYcon una columna única. Añadir, idcomo ú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.
- 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.
| 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:
| 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:
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 2para explorar enpsql; 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.
- Los nulos:
NULLS FIRST y NULLS LAST
NULLS FIRST y NULLS LASTUn 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:
| 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:
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 | Sí |
| Oracle | El mayor | Nulos al final | Nulos al principio | Sí |
| 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.
- 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:
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:
| 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:
Notas prácticas sobre colaciones:
- La colación afecta también a
WHERE, aLIKEy a los índices. Un índice creado con una colación no sirve para ordenar con otra (módulo 8). - Ordenar con
COLLATEexplí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 porLOWER(unaccent(columna)), con funciones del módulo 6.
- Ordenar fechas
Las fechas ordenan cronológicamente, sin sorpresas, porque el tipo DATE es un número por dentro:
| 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.
ORDER BY en el orden lógico de ejecución
ORDER BY en el orden lógico de ejecuciónORDER 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? |
Sí | El alias ya existe: SELECT se ejecutó antes |
¿Usar una columna que no está en el SELECT? |
Sí (salvo con DISTINCT) |
Las columnas de la tabla siguen disponibles |
| ¿Usar una expresión calculada? | Sí | Se evalúa sobre las filas del resultado |
| ¿Usar el ordinal de columna? | Sí | 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
DESCuna sola vez creyendo que afecta a toda la lista.ORDER BY a, b DESCordenaaascendente ybdescendente. Si quieres las dos descendentes:ORDER BY a DESC, b DESC. - Ordenar por ordinal en código de producción.
ORDER BY 2se rompe en silencio cuando alguien reordena elSELECT. - Suponer que los nulos van donde tú crees. PostgreSQL los pone al final en
ASC; MySQL, al principio. Si importa, escribeNULLS FIRSToNULLS 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 conSHOW lc_collate;. - Consejo: usa
COLLATEsolo 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/DESCinvertido 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
| 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
idya 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 elORDER BYy no la tabla. - En la categoría 2, "Champú sólido" va antes que "Crema facial" porque comparten la
Cinicial y desempata la segunda letra (hantes quer); 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ónCaparecería al final de todo el listado. Es el efecto de la sección 6.
Solución 2
| 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 BYno hay orden garantizado. UnUPDATE, un índice o el paralelismo pueden cambiarlo sin previo aviso. ASC(por defecto) yDESCdeciden el sentido, y cada columna delORDER BYlleva 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
ASCy al principio enDESCen PostgreSQL, justo al revés que en MySQL, SQLite y SQL Server.NULLS FIRST/NULLS LASTlo hace explícito. - El orden del texto depende de la colación de la base: con
Clos 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 BYes el paso 4 del orden lógico: ya existen los alias, aún se ven todas las columnas, y solo quedaLIMITpor 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
- ¿Qué es SQL?
- Configurando tu entorno SQL
- Sintaxis básica de SQL
- Entendiendo bases de datos y tablas
- El modelo relacional: claves primarias y foráneas
- La base de datos del curso: TiendaVerde
Módulo 2: Consultas básicas de SQL
- Instrucción SELECT
- Alias, expresiones y columnas calculadas
- Filtrando datos con WHERE
- DISTINCT y eliminación de duplicados
- Ordenando datos con ORDER BY
- Limitando resultados con LIMIT
Módulo 3: Trabajando con múltiples tablas
- Operaciones JOIN
- INNER JOIN
- LEFT JOIN
- RIGHT JOIN
- FULL OUTER JOIN
- SELF JOIN y CROSS JOIN
- Uniones de conjuntos: UNION, INTERSECT y EXCEPT
Módulo 4: Filtrado avanzado de datos
- Usando LIKE para coincidencia de patrones
- Operadores IN y BETWEEN
- Valores NULL y IS NULL
- Funciones de agregación: COUNT, SUM, AVG, MIN y MAX
- Agregando datos con GROUP BY
- Cláusula HAVING
Módulo 5: Manipulación de datos
- Creando tablas y restricciones con CREATE TABLE
- Instrucción INSERT
- Instrucción UPDATE
- Instrucción DELETE
- Instrucción UPSERT (MERGE)
- Modificando el esquema: ALTER TABLE y migraciones seguras
Módulo 6: Funciones avanzadas de SQL
- Funciones de cadena
- Funciones numéricas
- Funciones de fecha y hora
- Conversión de tipos y manejo de NULL: CAST y COALESCE
- Expresiones condicionales
Módulo 7: Subconsultas y consultas anidadas
- Introducción a subconsultas
- Subconsultas correlacionadas
- EXISTS y NOT EXISTS
- Usando subconsultas en cláusulas SELECT, FROM y WHERE
- Subconsultas o JOIN: cuál elegir
Módulo 8: Índices y optimización de rendimiento
- Entendiendo los índices
- Creación y gestión de índices
- Tipos de índice y cuándo no indexar
- Técnicas de optimización de consultas
- Análisis del rendimiento de consultas
Módulo 9: Transacciones y concurrencia
- Introducción a las transacciones
- Propiedades ACID
- Instrucciones de control de transacciones
- Niveles de aislamiento y anomalías de concurrencia
- Manejo de concurrencia: bloqueos e interbloqueos
Módulo 10: Temas avanzados
- Vistas
- Expresiones de tabla comunes (CTE)
- Funciones de ventana
- Procedimientos almacenados
- Triggers
- JSON y datos semiestructurados
Módulo 11: SQL en la práctica
- Casos de uso en el mundo real
- Mejores prácticas
- Seguridad: inyección SQL, permisos y roles
- SQL para análisis de datos
- SQL en desarrollo web
