SQL TOP, FETCH FIRST y WITH TIES
Lección 2.9 · Tiempo de lectura: ~9 min · Tareas de práctica ↓
SQL TOP y FETCH FIRST n ROWS ONLY son las alternativas a LIMIT: TOP lo usa SQL Server, y FETCH FIRST es el estándar SQL que admiten PostgreSQL, Oracle, MariaDB y otros. En esta lección aprenderás la sintaxis estándar, la paginación con OFFSET ... FETCH, la opción WITH TIES, que conserva las filas con valores iguales, PERCENT y las formas antiguas de Oracle y Firebird.
En la lección 2.7 limitaste resultados con LIMIT y OFFSET. Esa sintaxis funciona en MySQL, MariaDB, PostgreSQL y SQLite, pero SQL Server y Oracle no la aceptan. Cuando cambias de base de datos, lees consultas ajenas o preparas una entrevista, encontrarás varias formas más de escribir la misma idea.
Por qué limitar filas tiene varias sintaxis
Durante mucho tiempo, limitar filas no formaba parte del estándar SQL, así que cada base de datos inventó su propia sintaxis:
- MySQL y PostgreSQL añadieron
LIMIT, y SQLite lo copió. - SQL Server añadió
TOP. - Oracle tenía la pseudocolumna
ROWNUM. - Firebird añadió
FIRSTySKIP.
La cláusula estándar FETCH FIRST apareció recién en SQL:2008, cuando estas costumbres ya estaban establecidas. Hoy la mayoría de las bases de datos admiten la forma estándar además de la propia, pero MySQL y SQLite todavía no.
FETCH FIRST n ROWS ONLY: la sintaxis estándar
FETCH FIRST va después de ORDER BY, donde iría LIMIT:
SELECT title, length
FROM film
ORDER BY length
FETCH FIRST 3 ROWS ONLY;
Resultado: tres de las películas más cortas, de 46 minutos. La consulta funciona en MariaDB 10.6+, PostgreSQL y Oracle; MySQL da un error de sintaxis.
Las palabras FIRST y NEXT, ROW y ROWS son intercambiables: FETCH NEXT 1 ROW ONLY significa lo mismo que FETCH FIRST 1 ROWS ONLY. Elige la que se lea mejor. Si omites el número, FETCH FIRST ROW ONLY devuelve una fila.
Paginación con OFFSET ... FETCH NEXT
La forma estándar de OFFSET se escribe con la palabra ROWS y va antes de FETCH:
SELECT title, length
FROM film
ORDER BY length, title
OFFSET 10 ROWS
FETCH NEXT 5 ROWS ONLY;
Resultado: las películas 11 a 15 por duración, igual que LIMIT 5 OFFSET 10.
Es la única forma de paginar en SQL Server, y allí exige ORDER BY: OFFSET sin ordenación es un error de sintaxis.
WITH TIES: conservar las filas que comparten el último valor
Mira otra vez las películas más cortas. Cinco películas duran 46 minutos, pero FETCH FIRST 3 ROWS ONLY devuelve solo tres, y cuáles tres lo decide la base de datos.
WITH TIES lo resuelve: devuelve las primeras N filas más todas las filas siguientes que tienen los mismos valores de ordenación que la fila N.
SELECT title, length
FROM film
ORDER BY length
FETCH FIRST 3 ROWS WITH TIES;
Resultado: 5 filas. Se devuelven las cinco películas de 46 minutos, porque la tercera comparte su duración con otras dos.
| Opción | Filas | Qué obtienes |
|---|---|---|
FETCH FIRST 3 ROWS ONLY | 3 | tres cualesquiera de las cinco películas de 46 minutos |
FETCH FIRST 3 ROWS WITH TIES | 5 | las cinco películas de 46 minutos |
WITH TIES responde con honestidad a preguntas como «las tres películas más cortas»: ninguna película queda fuera solo porque comparte un valor con la última.
Reglas de WITH TIES
- Exige
ORDER BY: los empates se definen por las columnas de ordenación. Sin ordenación, PostgreSQL y MariaDB dan un error. - Una columna de desempate elimina los empates. Con
ORDER BY length, titleno hay dos filas iguales, yWITH TIESdevuelve exactamente 3 filas. - El resultado puede ser mucho mayor que N. Las películas más largas de Sakila duran 185 minutos, y
ORDER BY length DESC FETCH FIRST 3 ROWS WITH TIESdevuelve 10 películas.
La misma consulta en PostgreSQL sobre la base de datos Bookings encuentra las rutas más largas:
SELECT flight_no, departure_city, arrival_city, duration
FROM routes
ORDER BY duration DESC
FETCH FIRST 3 ROWS WITH TIES;
Resultado: 4 rutas de 8 horas y 50 minutos: vuelos entre Moscú y Yuzhno-Sajalinsk o Petropávlovsk.
WITH TIES en FETCH lo admiten PostgreSQL 13+, MariaDB 10.6+ y Oracle 12c+.
SQL Server: TOP
SQL Server coloca TOP justo después de SELECT:
SELECT TOP (3) Name, ListPrice
FROM Product
ORDER BY ListPrice DESC;
Resultado: tres de los productos más caros de AdventureWorks.
Los paréntesis son opcionales para un número (TOP 3 también funciona), pero se recomiendan: con ellos TOP acepta además una variable o una expresión, como TOP (@n).
TOP tiene su propio WITH TIES, y allí solo funciona con TOP: OFFSET ... FETCH en SQL Server no admite WITH TIES.
SELECT TOP (3) WITH TIES Name, ListPrice
FROM Product
ORDER BY ListPrice DESC;
Resultado: 5 productos. Cinco tallas de la bicicleta Road-150 Red cuestan 3578.27.
Trampas de TOP
TOPsinORDER BYestá permitido, pero devuelve filas arbitrarias, igual queLIMITsin ordenación.TOP ... WITH TIESsinORDER BYes un error.- SQL Server no acepta
FETCH FIRSTpor sí solo:FETCHsolo se permite después deOFFSET. Para las primeras filas escribeOFFSET 0 ROWS FETCH NEXT 3 ROWS ONLYo usaTOP.
Devolver un porcentaje de filas
A veces no necesitas 10 filas sino el 10% de ellas. SQL Server y Oracle pueden hacerlo directamente:
SELECT TOP (1) PERCENT Name, ListPrice
FROM Product
ORDER BY ListPrice DESC;
Resultado: 3 filas. El 1% de 295 productos es 2,95, y SQL Server redondea hacia arriba.
En Oracle se usa la forma estándar:
SELECT first_name, last_name, salary
FROM hr.employees
ORDER BY salary DESC
FETCH FIRST 5 PERCENT ROWS ONLY;
Resultado: los 6 empleados con los salarios más altos: el 5% de 107 es 5,35, redondeado hacia arriba.
DuckDB escribe el porcentaje en LIMIT: LIMIT 10%. PostgreSQL, MariaDB y MySQL no admiten porcentajes: allí primero cuentas las filas y calculas el número tú mismo.
Oracle: ROWNUM y FETCH FIRST
Antes de la versión 12c Oracle no tenía FETCH FIRST, y el código antiguo limita filas con la pseudocolumna ROWNUM. Numera las filas a medida que se leen, antes de aplicar ORDER BY. Por eso esta consulta es incorrecta:
SELECT first_name, last_name, salary
FROM hr.employees
WHERE ROWNUM <= 3
ORDER BY salary;
Resultado: no los tres salarios más bajos, sino las tres primeras filas que leyó la base de datos (17000, 17000 y 24000), ordenadas después.
Para ordenar primero, el código antiguo envuelve la consulta ordenada en una subconsulta y aplica ROWNUM a su resultado:
SELECT first_name, last_name, salary
FROM (
SELECT first_name, last_name, salary
FROM hr.employees
ORDER BY salary
)
WHERE ROWNUM <= 3;
Resultado: los tres salarios más bajos: 2100, 2200 y 2200.
Otra trampa: WHERE ROWNUM > 1 no devuelve ninguna fila. La primera fila recibe el número 1, no cumple la condición y se descarta; la siguiente vuelve a ser la número 1, y así sucesivamente.
En Oracle 12c y posteriores usa simplemente FETCH FIRST, incluido WITH TIES:
SELECT first_name, last_name, salary
FROM hr.employees
ORDER BY salary DESC
FETCH FIRST 7 ROWS WITH TIES;
Resultado: 8 empleados: el 7.º y el 8.º ganan 12008.
Oracle sigue sin aceptar LIMIT, incluso en la versión 26ai.
Firebird: FIRST, SKIP y ROWS
Firebird tiene dos sintaxis propias. FIRST y SKIP van justo después de SELECT, como TOP:
SELECT FIRST 3 SKIP 2 first_name, last_name, salary
FROM employee
ORDER BY salary DESC;
Resultado: los empleados 3 a 5 por salario: se omiten dos filas y se devuelven tres.
ROWS m TO n va al final de la consulta y recibe números de fila en lugar de una cantidad:
SELECT first_name, last_name, salary
FROM employee
ORDER BY salary DESC
ROWS 3 TO 5;
Resultado: los mismos empleados 3 a 5. ROWS 3 solo significa las tres primeras filas.
Desde Firebird 3.0 también funciona el estándar OFFSET ... FETCH. WITH TIES no se admite, ni siquiera en Firebird 5.
WITH TIES en MySQL y SQLite
MySQL y SQLite no tienen ni FETCH ni WITH TIES, pero el mismo resultado se puede construir con herramientas que aprenderás más adelante en el curso. Aquí tienes un adelanto.
Una subconsulta encuentra el valor de la tercera fila, y la consulta externa toma todas las películas que no son más largas:
SELECT title, length
FROM film
WHERE length <= (
SELECT length
FROM film
ORDER BY length
LIMIT 1 OFFSET 2
)
ORDER BY length, title;
Resultado: las mismas 5 películas de 46 minutos que con FETCH FIRST 3 ROWS WITH TIES. Las subconsultas se ven en la lección 6.2.
La función de ventana RANK() da el mismo rango a valores iguales, así que rnk <= 3 conserva todos los empates:
SELECT title, length
FROM (
SELECT title, length, RANK() OVER (ORDER BY length) AS rnk
FROM film
) AS ranked
WHERE rnk <= 3
ORDER BY length, title;
Resultado: las mismas 5 películas. RANK() se ve en la lección 7.2.
Sintaxis para limitar filas por base de datos
| Base de datos | Primeras N filas | Omitir filas | WITH TIES | Porcentaje |
|---|---|---|---|---|
| MySQL | LIMIT n | LIMIT n OFFSET m | no | no |
| MariaDB 10.6+ | LIMIT n, FETCH FIRST n ROWS ONLY | LIMIT n OFFSET m, OFFSET m ROWS FETCH NEXT n ROWS ONLY | FETCH ... WITH TIES | no |
| PostgreSQL 13+ | LIMIT n, FETCH FIRST n ROWS ONLY | LIMIT n OFFSET m, OFFSET m ROWS FETCH NEXT n ROWS ONLY | FETCH ... WITH TIES | no |
| SQL Server | TOP (n) | OFFSET m ROWS FETCH NEXT n ROWS ONLY | TOP (n) WITH TIES | TOP (n) PERCENT |
| Oracle 12c+ | FETCH FIRST n ROWS ONLY (ROWNUM antes de 12c) | OFFSET m ROWS FETCH NEXT n ROWS ONLY | FETCH ... WITH TIES | FETCH FIRST n PERCENT ROWS ONLY |
| Firebird | FIRST n, ROWS n, FETCH FIRST (3.0+) | SKIP m, ROWS m TO n, OFFSET ... FETCH (3.0+) | no | no |
| SQLite | LIMIT n | LIMIT n OFFSET m | no | no |
| DuckDB | LIMIT n, FETCH FIRST n ROWS ONLY | LIMIT n OFFSET m, OFFSET ... FETCH | no | LIMIT n% |
Si una consulta debe funcionar en varias bases de datos, el estándar OFFSET ... FETCH con ORDER BY cubre la mayoría: todas salvo MySQL y SQLite. Con esta lección terminas los fundamentos de la recuperación de filas; el siguiente módulo muestra cómo transformar valores con funciones SQL.
Conclusiones clave:
FETCH FIRST n ROWS ONLYes la alternativa estándar aLIMIT; MySQL y SQLite no la admiten.OFFSET m ROWS FETCH NEXT n ROWS ONLYpagina los resultados; en SQL Server exigeORDER BY.WITH TIESañade las filas que comparten los valores de ordenación de la última fila, así que el resultado puede superar N.- SQL Server usa
TOP (n),TOP (n) WITH TIESyTOP (n) PERCENT. ROWNUMde Oracle se asigna antes deORDER BY: ordena primero en una subconsulta o usaFETCH FIRSTen 12c+.- En MySQL y SQLite, construye
WITH TIEScon una subconsulta o conRANK().
Preguntas frecuentes
¿Cuál es el equivalente de LIMIT en SQL Server?
Para las primeras filas usa SELECT TOP (n) ...; para paginar, ORDER BY ... OFFSET m ROWS FETCH NEXT n ROWS ONLY. SQL Server no acepta LIMIT ni permite FETCH sin OFFSET.
¿Qué hace WITH TIES en SQL?
Devuelve las primeras N filas más cada fila siguiente con los mismos valores en las columnas de ORDER BY que la fila N. FETCH FIRST 3 ROWS WITH TIES puede devolver 3, 5 o 10 filas, según cuántas compartan el tercer valor.
¿MySQL admite FETCH FIRST o WITH TIES?
No. MySQL 8 y 9 solo admiten LIMIT. MariaDB admite FETCH FIRST ... WITH TIES desde la versión 10.6. En MySQL, obtén el mismo resultado con una subconsulta o con RANK().
¿Cómo limito filas en Oracle?
En Oracle 12c y posteriores usa FETCH FIRST n ROWS ONLY después de ORDER BY. En versiones antiguas ordena en una subconsulta y filtra su resultado con WHERE ROWNUM <= n. Oracle no admite LIMIT.
¿Qué diferencia hay entre FETCH FIRST y FETCH NEXT?
Ninguna: FIRST y NEXT son sinónimos, igual que ROW y ROWS. Por convención, FETCH FIRST se usa sin OFFSET y FETCH NEXT después de él.