Tienes ya todas las piezas de una consulta menos una: decidir cuántas filas quieres recibir. Con las 20 filas de productos parece irrelevante, pero el día que te conectes a una tabla de producción con cincuenta millones de registros, un SELECT * FROM pedidos; sin más puede colapsar tu terminal, saturar la red y hacer que un administrador de sistemas te busque con cara de pocos amigos. LIMIT es la primera línea de defensa. Y es también la base de la paginación, uno de esos problemas que parecen resueltos en dos minutos y que en tablas grandes se convierten en un dolor de cabeza clásico. En esta lección aprenderás ambas cosas: el uso cotidiano y el uso serio.

Contenido

  1. LIMIT: acotar el resultado
  2. Por qué LIMIT sin ORDER BY no es determinista
  3. OFFSET y la paginación clásica
  4. Los dos problemas de OFFSET en tablas grandes
  5. Paginación por cursor o keyset
  6. FETCH FIRST n ROWS ONLY: la forma estándar
  7. Casos de uso típicos
  8. LIMIT en el orden lógico de ejecución
  9. Errores Comunes y Consejos
  10. Ejercicios
  11. Conclusión

  1. LIMIT: acotar el resultado

LIMIT n indica cuántas filas, como máximo, quieres recibir. Se escribe al final de la consulta, después de ORDER BY:

SELECT id, nombre, precio
FROM productos
ORDER BY precio DESC
LIMIT 5;
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

Los cinco productos más caros del catálogo. Este patrón —ordenar y recortar— es la forma canónica de responder a cualquier pregunta de tipo "top N".

Comportamiento de LIMIT en los casos límite:

Escritura Qué devuelve
LIMIT 5 Como mucho 5 filas
LIMIT 100 sobre productos Las 20 que hay. LIMIT es un máximo, no una exigencia
LIMIT 0 Ninguna fila, pero sí las cabeceras. Útil para inspeccionar los tipos de columna de una consulta sin ejecutarla del todo
LIMIT ALL Todas. Equivale a no poner LIMIT; sirve para construir consultas por programa
LIMIT NULL Todas, igual que LIMIT ALL
LIMIT -1 ERROR: LIMIT must not be negative

El uso más frecuente en el día a día ni siquiera lleva ORDER BY: es echar un vistazo a una tabla que no conoces.

SELECT * FROM lineas_pedido LIMIT 5;
id pedido_id producto_id cantidad precio_unitario descuento
1 1 1 2 11.95 0.00
2 1 2 3 3.90 0.00
3 1 14 2 3.25 0.00
4 2 6 1 17.50 0.00
5 2 9 2 4.60 0.00

Aquí SELECT * y la ausencia de ORDER BY son perfectamente legítimos: no quieres unas filas concretas, quieres ver qué pinta tienen los datos. Es exploración, no un informe.

Costumbre profesional: cuando te conectes a una base de datos que no conoces, escribe LIMIT 10 antes de escribir el resto de la consulta. Cuesta ocho caracteres y evita el bochorno de bloquear una sesión en producción.

  1. Por qué LIMIT sin ORDER BY no es determinista

Retomamos la advertencia de 02-05, porque con LIMIT se vuelve mucho más peligrosa.

SELECT id, nombre, precio FROM productos LIMIT 3;
id nombre precio
1 Aceite de oliva virgen extra 500 ml 12.50
2 Arroz integral ecológico 1 kg 3.90
3 Miel de azahar cruda 500 g 9.75

¿Qué has pedido exactamente? Tres filas cualesquiera. No las tres primeras por id, no las tres más baratas: tres filas arbitrarias, las que el motor tuviera más a mano. Que hoy salgan la 1, la 2 y la 3 es casualidad del orden físico.

La diferencia con una consulta sin LIMIT es sutil pero crucial:

Consulta Sin ORDER BY
SELECT ... FROM productos Devuelve las 20 filas, en orden impredecible. El conjunto es correcto; solo el orden es incierto
SELECT ... FROM productos LIMIT 3 Devuelve tres filas impredecibles. El conjunto mismo es incierto

En el primer caso, ordenar el resultado en tu aplicación arregla el problema. En el segundo no hay arreglo posible: has perdido 17 filas y no sabes cuáles.

Regla: LIMIT sin ORDER BY solo es aceptable para explorar. En cuanto el resultado se use para algo, el ORDER BY es obligatorio, y debe ser determinista (terminado en una columna única, como viste en 02-05).

  1. OFFSET y la paginación clásica

OFFSET m descarta las primeras m filas del resultado antes de aplicar LIMIT. Combinados, permiten recorrer un resultado grande en trozos:

Página N  →  LIMIT tamaño OFFSET (N - 1) * tamaño

Veámoslo con el catálogo de TiendaVerde ordenado por precio descendente y páginas de cinco productos.

Página 1LIMIT 5 OFFSET 0:

SELECT id, nombre, precio
FROM productos
ORDER BY precio DESC, id
LIMIT 5 OFFSET 0;
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

Página 2LIMIT 5 OFFSET 5:

id nombre precio
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

Página 3LIMIT 5 OFFSET 10:

id nombre precio
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

Y la cuarta y última cerraría con los productos 2, 18, 14, 4 y 5.

Resumen del cálculo:

Página OFFSET LIMIT Productos devueltos
1 0 5 15, 6, 20, 8, 13
2 5 5 1, 10, 12, 3, 7
3 10 5 19, 11, 17, 16, 9
4 15 5 2, 18, 14, 4, 5

Detalles importantes:

  • El ORDER BY es obligatorio. Sin él, cada página se calcula sobre un orden distinto y podrías ver el mismo producto en dos páginas y otro en ninguna.
  • El ORDER BY debe ser determinista. Por eso hemos escrito ORDER BY precio DESC, id: si dos productos costaran lo mismo, el desempate por id garantiza que siempre caigan en la misma página.
  • OFFSET sin LIMIT es válido: OFFSET 15 devuelve las filas de la 16 en adelante.
  • OFFSET mayor que el total devuelve cero filas, sin error. OFFSET 100 sobre productos no da nada.

  1. Los dos problemas de OFFSET en tablas grandes

Esta paginación funciona, es la que aprende todo el mundo, y tiene dos defectos serios que solo se manifiestan cuando los datos crecen.

4.1. El coste crece con el número de página

OFFSET m no salta las primeras m filas: las calcula y las descarta. El motor no tiene forma de adivinar cuál es la fila número 500 001 sin haber producido las 500 000 anteriores.

Página Filas que el motor produce Filas que te entrega
1 20 20
50 1 000 20
500 10 000 20
50 000 1 000 000 20

La última página de un listado grande puede tardar cientos de veces más que la primera. Es un patrón conocido: los usuarios se quejan de que "la web va lenta al final del catálogo", y la causa está aquí. Con EXPLAIN ANALYZE (módulo 8) se ve con toda claridad en el nodo Limit, que informa de cuántas filas ha descartado.

4.2. Los datos se mueven entre página y página

Este es peor, porque no es lento: es incorrecto.

Imagina que un usuario está viendo la página 1 del catálogo ordenado por precio descendente y, en ese instante, alguien da de alta un producto de 25 € (más caro que el matcha). Cuando el usuario pulse "siguiente":

Momento Qué pasa
Página 1 (antes) 15, 6, 20, 8, 13
Se inserta un producto nuevo de 25.00 € Todo el listado se desplaza una posición
Página 2 (OFFSET 5) Ahora la posición 6 la ocupa el producto 13, que ya estaba en la página 1

El usuario ve el producto 13 dos veces. Y con un borrado ocurre lo contrario: una fila desaparece sin haberse mostrado nunca.

Cambio en los datos Síntoma para el usuario
Se inserta una fila que va antes de la página actual Ve una fila repetida
Se borra una fila anterior a la página actual Una fila se salta y no la ve nunca

Cada página es una consulta independiente, ejecutada en un momento distinto y sobre un estado distinto de la tabla. OFFSET no tiene memoria de lo que ya te enseñó.

¿Cuándo se puede vivir con OFFSET?

Escenario ¿OFFSET es aceptable?
Panel interno con pocos miles de filas Sí, sin problema
Datos que no cambian durante la sesión (un informe histórico)
Necesitas "ir a la página 47" directamente Sí: es lo único que lo permite
Catálogo público con millones de filas y escrituras constantes No
Un scroll infinito en una aplicación móvil No
Exportar una tabla entera en trozos No: usa keyset

  1. Paginación por cursor o keyset

La alternativa consiste en dejar de decir "sáltate 500 000 filas" y empezar a decir "dame lo que viene después de esto". En lugar de una posición, se recuerda el último valor visto.

Volvamos a nuestro catálogo. La página 1 terminaba en el producto 13, con precio 13.75 €. La página 2 se pide así:

SELECT id, nombre, precio
FROM productos
WHERE precio < 13.75
ORDER BY precio DESC, id
LIMIT 5;
id nombre precio
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

Idéntica a la página 2 de la sección 3, pero obtenida sin descartar nada: el motor va directo a las filas que cumplen precio < 13.75.

Qué pasa si hay empates. Si dos productos costaran exactamente 13.75 €, WHERE precio < 13.75 se saltaría el segundo. La solución es comparar la tupla completa de las columnas de ordenación, algo que SQL permite:

SELECT id, nombre, precio
FROM productos
WHERE (precio, id) < (13.75, 13)
ORDER BY precio DESC, id DESC
LIMIT 5;

La comparación (precio, id) < (13.75, 13) es lexicográfica: se cumple si precio < 13.75, o si precio = 13.75 y además id < 13. Es exactamente la condición "estrictamente después del último elemento visto" para ese orden. Ojo con un detalle: para que la comparación de tuplas encaje, todas las columnas del ORDER BY deben ir en el mismo sentido, por eso aquí aparece id DESC.

Comparativa de los dos enfoques:

Aspecto OFFSET Keyset
Coste de la página N Crece linealmente con N Constante
¿Aprovecha un índice? Solo para ordenar Sí, para posicionarse directamente
Filas repetidas o saltadas si los datos cambian No
¿Permite saltar a la página 47? No: solo "siguiente" y "anterior"
¿Permite mostrar "página 3 de 120"? No sin una consulta adicional
Complejidad de implementación Trivial Media: hay que arrastrar el cursor

En la práctica: si tu interfaz es un scroll infinito o un botón "cargar más", el keyset es siempre la respuesta correcta. Si necesitas numeritos de página clicables, OFFSET es lo único viable y hay que asumir sus límites (o limitar el número de páginas navegables, que es lo que hacen casi todos los buscadores).

Para que el keyset rinda hace falta un índice sobre las columnas de ordenación, en este caso (precio, id). Sin él, PostgreSQL tiene que ordenar toda la tabla igualmente y no ganas nada. Los índices son el módulo 8.

  1. FETCH FIRST n ROWS ONLY: la forma estándar

LIMIT es cómodo, universalmente conocido... y no es SQL estándar. Lo introdujo MySQL, lo adoptó PostgreSQL y hoy lo entienden casi todos los motores, pero la sintaxis oficial del estándar SQL:2008 es otra:

SELECT id, nombre, precio
FROM productos
ORDER BY precio DESC, id
OFFSET 0 ROWS
FETCH FIRST 5 ROWS ONLY;
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

Exactamente el mismo resultado. PostgreSQL admite ambas formas y las trata igual.

PostgreSQL añade además WITH TIES, que amplía el resultado para incluir todas las filas empatadas con la última:

SELECT id, nombre, precio
FROM productos
ORDER BY precio DESC
FETCH FIRST 5 ROWS WITH TIES;

Sobre estos datos devuelve las mismas 5 filas, porque no hay dos productos con el mismo precio. Pero si hubiera tres productos a 13.75 €, WITH TIES devolvería 7 filas en lugar de 5: es la forma correcta de hacer un "top 5" honesto en una competición. WITH TIES exige ORDER BY y no funciona con LIMIT, solo con FETCH.

Tabla comparativa entre motores:

Motor Sintaxis principal Alternativas
Estándar SQL:2008 OFFSET m ROWS FETCH FIRST n ROWS ONLY
PostgreSQL LIMIT n OFFSET m FETCH FIRST n ROWS ONLY, WITH TIES
MySQL / MariaDB LIMIT n OFFSET m LIMIT m, n (¡los argumentos al revés!)
SQLite LIMIT n OFFSET m LIMIT m, n
SQL Server 2012+ OFFSET m ROWS FETCH NEXT n ROWS ONLY SELECT TOP (n) ... (sin offset)
Oracle 12c+ OFFSET m ROWS FETCH FIRST n ROWS ONLY
Oracle 11g y anteriores WHERE ROWNUM <= n sobre una subconsulta ordenada

Dos trampas de esta tabla que conviene subrayar:

  1. LIMIT m, n de MySQL invierte el significado: el primer número es el desplazamiento y el segundo la cantidad. LIMIT 5, 10 en MySQL es LIMIT 10 OFFSET 5 en PostgreSQL. Una consulta copiada sin leerla devuelve datos equivocados.
  2. ROWNUM de Oracle se asigna antes del ORDER BY. WHERE ROWNUM <= 5 ORDER BY precio DESC devuelve cinco filas cualesquiera y luego las ordena: no es el top 5. Hay que ordenar en una subconsulta y aplicar ROWNUM fuera.

¿Cuál usar? En este curso, LIMIT: es lo que verás en el 99 % del código PostgreSQL. Si escribes SQL que deba ejecutarse en varios motores, OFFSET ... FETCH FIRST ... ROWS ONLY es la apuesta segura.

  1. Casos de uso típicos

7.1. Top N

Ya lo has visto con los productos más caros. Otra variante, los cinco de mayor margen:

SELECT nombre,
       precio,
       coste,
       precio - coste AS margen
FROM productos
WHERE activo
ORDER BY margen DESC, id
LIMIT 5;
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
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

Fíjate en que las Cápsulas de espirulina (margen 7.70 €, tercera del catálogo) no aparecen: el WHERE activo las descarta antes de ordenar. El orden lógico manda: primero se filtra, después se ordena, y solo al final se recorta.

7.2. Los últimos registros

SELECT id, cliente_id, fecha_pedido, estado, metodo_pago
FROM pedidos
ORDER BY fecha_pedido DESC, id DESC
LIMIT 3;
id cliente_id fecha_pedido estado metodo_pago
20 9 2026-02-21 pendiente contrareembolso
19 6 2026-02-09 pagado tarjeta
18 5 2026-01-27 pagado transferencia

Los tres últimos pedidos recibidos: el contenido natural de un panel de "actividad reciente".

7.3. El registro extremo

SELECT id, nombre, apellidos, fecha_registro
FROM clientes
ORDER BY fecha_registro
LIMIT 1;
id nombre apellidos fecha_registro
1 Lucía Martínez Soler 2025-01-10

La primera clienta de TiendaVerde. Este patrón —ORDER BY ... LIMIT 1— es equivalente a usar MIN/MAX, pero con una ventaja: te devuelve la fila entera, no solo el valor mínimo. Con MIN(fecha_registro) sabrías la fecha, pero no quién es. Las funciones de agregación llegan en la lección 04-04.

7.4. Muestreo rápido para exploración

SELECT * FROM resenas LIMIT 3;
id producto_id cliente_id puntuacion comentario fecha
1 1 1 5 Aceite excelente, sabor intenso y envase muy cuidado. 2025-03-15
2 2 1 4 Buen arroz, aunque tarda algo más en cocer de lo habitual. 2025-03-16
3 6 2 5 La crema deja la piel muy suave. Repetiré sin duda. 2025-03-25

  1. LIMIT en el orden lógico de ejecución

Con LIMIT se completa el ciclo que empezamos en 02-01. Este es el diagrama definitivo del módulo:

flowchart LR
    A["1 · FROM<br/>origen de las filas"] --> B["2 · WHERE<br/>filtra filas"]
    B --> C["3 · SELECT<br/>proyecta y calcula<br/>nacen los alias"]
    C --> D["3b · DISTINCT<br/>elimina duplicados"]
    D --> E["4 · ORDER BY<br/>ordena el resultado"]
    E --> F["5 · LIMIT / OFFSET<br/>recorta"]

LIMIT es siempre lo último. De ahí salen consecuencias que conviene tener claras:

Pregunta Respuesta
¿LIMIT 5 hace que WHERE examine solo 5 filas? No. WHERE se aplica a todas las filas de la tabla
¿LIMIT 5 con ORDER BY ordena solo 5 filas? No. Lógicamente se ordena todo y luego se recorta
Entonces, ¿LIMIT no ahorra trabajo? Sí que lo ahorra, pero en el plan físico, no en el lógico

Ese último punto merece un matiz. Aunque lógicamente se ordene todo, PostgreSQL es más listo que eso: cuando ve un ORDER BY ... LIMIT n pequeño, usa un algoritmo llamado top-N heapsort, que mantiene en memoria solo las n mejores filas vistas hasta el momento en lugar de ordenar el conjunto entero. Y si existe un índice que ya devuelve las filas en el orden pedido, puede leer las n primeras y parar, sin tocar el resto de la tabla.

Es el ejemplo perfecto de la distinción de 02-01: el orden lógico define el significado de la consulta, y el plan físico que elige el optimizador puede ser radicalmente más eficiente mientras produzca el mismo resultado. Lo verás con tus propios ojos en el módulo 8 con EXPLAIN.

Errores Comunes y Consejos

  • LIMIT sin ORDER BY en cualquier consulta que no sea exploratoria. Devuelve filas arbitrarias y el problema no se puede arreglar después.
  • ORDER BY no determinista al paginar. Si hay empates y no los desempatas con una columna única, una misma fila puede aparecer en dos páginas y otra en ninguna.
  • Copiar LIMIT 10, 20 de un ejemplo de MySQL. En MySQL significa "20 filas desde la 11"; en PostgreSQL ni siquiera es sintaxis válida.
  • Creer que OFFSET salta filas sin coste. Las produce y las tira. En la página 5 000 lo notarás.
  • Paginar con OFFSET sobre datos que cambian. Filas repetidas y filas saltadas, sin ningún error que lo delate.
  • Aplicar el keyset con un ORDER BY que no coincide con la condición del WHERE. Las columnas deben ser las mismas, en el mismo orden y en el mismo sentido.
  • Usar LIMIT para "arreglar" una consulta lenta. Si la consulta es lenta por falta de índice, LIMIT puede seguir siendo lento: el motor tiene que encontrar las filas antes de recortarlas.
  • Esperar que LIMIT 1 sustituya a un WHERE bien escrito. Devolver una fila cualquiera de las que cumplen la condición rara vez es lo que se quiere.
  • Consejo: LIMIT primero, consulta después. Al explorar una base desconocida, escríbelo antes de nada.
  • Consejo: LIMIT 0 es útil. Te da las columnas y sus tipos sin traer datos, ideal para comprobar la forma de una consulta compleja.
  • Consejo: para un "top N" honesto, plantéate FETCH FIRST n ROWS WITH TIES. Cortar por el número 5 exacto cuando el 5 y el 6 empatan es engañoso.

Ejercicios

Ejercicio 1

Atención al cliente necesita un panel con los cinco pedidos más antiguos que todavía no están entregados ni cancelados, mostrando id, cliente_id, fecha_pedido, estado y gastos_envio. Escribe la consulta y explica cuántas filas devuelve realmente y por qué.

Ejercicio 2

Construye la segunda página de un listado de clientes ordenado por país y, dentro de cada país, por apellidos, con páginas de cuatro clientes. Escríbela primero con OFFSET y después con paginación keyset. Explica qué información necesita la aplicación en cada caso para pedir la página siguiente.

Ejercicio 3

El equipo de compras quiere las tres líneas de pedido de mayor importe. Escribe la consulta usando la sintaxis estándar FETCH FIRST, e indica qué cambiaría si usaras WITH TIES. Después responde: ¿por qué esta consulta no puede decirte de qué producto se trata?

Soluciones

Solución 1

SELECT id,
       cliente_id,
       fecha_pedido,
       estado,
       gastos_envio
FROM pedidos
WHERE estado <> 'entregado'
  AND estado <> 'cancelado'
ORDER BY fecha_pedido, id
LIMIT 5;
id cliente_id fecha_pedido estado gastos_envio
16 4 2025-12-19 enviado 4.95
17 7 2026-01-13 enviado 9.90
18 5 2026-01-27 pagado 4.95
19 6 2026-02-09 pagado 4.95
20 9 2026-02-21 pendiente 12.50

5 filas, que resultan ser todas las que cumplen la condición: de los 20 pedidos, 14 están entregados y 1 cancelado, así que solo quedan 5 en curso. El LIMIT 5 no ha recortado nada, y ese es un comportamiento normal: LIMIT es un máximo, no una promesa.

El razonamiento por el orden lógico: FROM trae 20 filas → WHERE deja 5 → SELECT proyecta → ORDER BY fecha_pedido, id las ordena de más antigua a más reciente → LIMIT 5 no descarta ninguna. Si mañana entran diez pedidos nuevos, la consulta seguirá devolviendo los cinco más antiguos sin tocar una coma.

En el módulo 4 escribirás esa condición de forma más limpia como WHERE estado NOT IN ('entregado', 'cancelado').

Solución 2

Con OFFSET, la página 2 (clientes 5 a 8 del listado):

SELECT id, nombre, apellidos, ciudad, pais
FROM clientes
ORDER BY pais, apellidos, id
LIMIT 4 OFFSET 4;

El listado completo ordenado por pais, apellidos empieza así: Belmonte Roca (5), Bosch Ferrer (13), Carrasco Vega (15), Ferrer Ibáñez (2) —página 1—, y continúa:

id nombre apellidos ciudad pais
14 Hugo Iglesias Pardo Zaragoza España
6 Pau Llorens Vidal Valencia España
1 Lucía Martínez Soler Valencia España
11 Elena Navarro Puig Alicante España

Con paginación keyset, partiendo del último elemento de la página 1 —Carlos Ferrer Ibáñez, país España, apellidos Ferrer Ibáñez, id 2—:

SELECT id, nombre, apellidos, ciudad, pais
FROM clientes
WHERE (pais, apellidos, id) > ('España', 'Ferrer Ibáñez', 2)
ORDER BY pais, apellidos, id
LIMIT 4;

Devuelve exactamente las mismas cuatro filas.

Qué necesita la aplicación en cada caso:

Enfoque Qué arrastra entre peticiones
OFFSET Solo un número: la página actual. Fácil de poner en una URL (?page=2) y permite saltar a cualquier página
Keyset Los valores de ordenación de la última fila mostrada (aquí pais, apellidos e id). Se guardan en un "cursor" que la aplicación devuelve al pedir la siguiente página

Fíjate en dos detalles del keyset: la tupla del WHERE contiene exactamente las mismas columnas del ORDER BY y en el mismo orden, y se incluye id como última columna precisamente para desempatar dos clientes que compartieran país y apellidos. Sin ese id, un apellido repetido haría que se perdiera un cliente entre página y página.

Solución 3

SELECT id,
       pedido_id,
       cantidad,
       precio_unitario,
       descuento,
       ROUND(cantidad * precio_unitario * (1 - descuento), 2) AS importe
FROM lineas_pedido
ORDER BY cantidad * precio_unitario * (1 - descuento) DESC, id
FETCH FIRST 3 ROWS ONLY;
id pedido_id cantidad precio_unitario descuento importe
28 12 2 22.00 0.00 44.00
18 8 3 12.50 0.05 35.63
24 10 2 18.90 0.10 34.02

Qué cambiaría con WITH TIES. Nada, en este caso: la cuarta línea vale 26.73 €, muy lejos de los 34.02 € de la tercera, así que no hay empate que arrastrar. Pero conviene notar que WITH TIES no puede llevar un desempate artificial en el ORDER BY: si dejas el , id final, cada fila tiene un valor de ordenación único y nunca habrá empates que incluir. Para que WITH TIES sirva de algo hay que ordenar solo por el criterio real:

ORDER BY cantidad * precio_unitario * (1 - descuento) DESC
FETCH FIRST 3 ROWS WITH TIES;

Con estos datos devuelve las mismas 3 filas. Si hubiera dos líneas empatadas en 34.02 €, devolvería 4.

Por qué no puedes saber de qué producto se trata. Porque lineas_pedido guarda producto_id, un número, y el nombre del producto vive en la tabla productos. Con las herramientas de este módulo solo puedes consultar una tabla a la vez: lo máximo que puedes decir es que la línea 28 corresponde al producto 15. Para escribir "Té verde matcha ceremonial 30 g" en el informe hace falta combinar ambas tablas, y eso es precisamente lo que empieza en la lección siguiente.

Conclusión

Has cerrado el ciclo completo de una consulta:

  • LIMIT n acota el resultado a un máximo de n filas y es la primera línea de defensa al explorar tablas grandes o desconocidas.
  • LIMIT sin ORDER BY no es determinista: devuelve filas arbitrarias, y ese daño no se puede reparar después.
  • OFFSET m descarta las primeras m filas y permite la paginación clásica LIMIT tamaño OFFSET (N-1)*tamaño, que exige un ORDER BY determinista.
  • Esa paginación tiene dos defectos en tablas grandes: el coste crece con el número de página, y las inserciones o borrados hacen que se repitan o se salten filas.
  • La paginación por cursor o keysetWHERE (columnas) < (últimos valores vistos)— tiene coste constante y no se descuadra, a cambio de no poder saltar a una página arbitraria.
  • FETCH FIRST n ROWS ONLY es la forma estándar, y WITH TIES amplía el resultado con las filas empatadas. Cada motor tiene su dialecto, y LIMIT m, n de MySQL invierte los argumentos.
  • LIMIT es el paso 5 y último del orden lógico; el optimizador, sin embargo, sí aprovecha su presencia con estrategias como el top-N heapsort.

Y con esto cierras el módulo 2 completo. Sabes construir una consulta de principio a fin: elegir su origen con FROM, filtrar filas con WHERE, proyectar y calcular columnas con SELECT y sus alias, eliminar repeticiones con DISTINCT, imponer un orden con ORDER BY y recortar el resultado con LIMIT. Y sabes en qué orden lógico ocurre todo eso, que es lo que te ha permitido entender por qué un alias funciona en un sitio y no en otro.

Pero fíjate en el techo con el que has chocado una y otra vez: pedidos te dice cliente_id = 9 y no "Camille Dubois"; lineas_pedido te dice producto_id = 15 y no "Té verde matcha ceremonial"; productos te dice categoria_id = 2 y no "Cosmética natural". Todas tus consultas han mirado una sola tabla, y las preguntas que de verdad le interesan a TiendaVerde —qué compró cada cliente, qué categoría factura más, qué comercial cierra más pedidos, qué productos no ha comprado nadie— viven precisamente en las relaciones entre tablas que dibujaste en el diagrama de 01-06. En el módulo 3, Consultas con múltiples tablas, aprenderás a recorrer esas relaciones con JOIN: el INNER JOIN para lo que casa a ambos lados, el LEFT y el RIGHT para conservar lo que no casa —ahí aparecerán por fin los tres clientes sin pedidos y los tres productos nunca vendidos—, el FULL OUTER para ambas cosas a la vez, el SELF JOIN para las relaciones reflexivas de empleados y clientes, y UNION, INTERSECT y EXCEPT para combinar resultados enteros. Las nueve tablas de TiendaVerde dejan de ser nueve islas.

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