¿Cómo seleccionar la enésima fila en una tabla de base de datos SQL?

399

Estoy interesado en aprender algunos (idealmente) de base de datos agnóstico formas de seleccionar el n º fila de una tabla de base de datos. También sería interesante ver cómo se puede lograr esto utilizando la funcionalidad nativa de las siguientes bases de datos:

  • servidor SQL
  • MySQL
  • PostgreSQL
  • SQLite
  • Oráculo

Actualmente estoy haciendo algo como lo siguiente en SQL Server 2005, pero estaría interesado en ver otros enfoques más agnósticos:

WITH Ordered AS (
SELECT ROW_NUMBER() OVER (ORDER BY OrderID) AS RowNumber, OrderID, OrderDate
FROM Orders)
SELECT *
FROM Ordered
WHERE RowNumber = 1000000

Crédito por el SQL anterior: el blog de Firoz Ansari

Actualización: Vea la respuesta de Troels Arvin con respecto al estándar SQL. Troels, ¿tienes algún enlace que podamos citar?

Charles Roper
fuente
3
Si. Aquí hay un enlace a información sobre el estándar ISO SQL: troels.arvin.dk/db/rdbms/links/#standards
Troels Arvin
13
Solo para señalar que, por la definición de una relación, las filas de una tabla no tienen orden, por lo que no se puede seleccionar la enésima fila de una tabla. Lo que se puede seleccionar es la enésima fila en un conjunto de filas devuelto por (el resto de) una consulta, que es lo que logran su ejemplo y todas las demás respuestas. Para la mayoría, esto puede ser una semántica, pero señala el problema subyacente de la pregunta. Si necesita regresar OrderNo N, introduzca una columna OrderSequenceNo en la tabla y generarla desde un generador de secuencia independiente al crear un nuevo orden.
Damir Sudarevic
2
El estándar SQL define la opción offset x fetch first y rows only. Actualmente soportado por (al menos) Postgres, Oracle12, DB2.
a_horse_with_no_name

Respuestas:

349

Hay formas de hacerlo en partes opcionales del estándar, pero muchas bases de datos admiten su propia forma de hacerlo.

Un sitio realmente bueno que habla sobre esto y otras cosas es http://troels.arvin.dk/db/rdbms/#select-limit .

Básicamente, PostgreSQL y MySQL son compatibles con el no estándar:

SELECT...
LIMIT y OFFSET x 

Oracle, DB2 y MSSQL son compatibles con las funciones estándar de ventanas:

SELECT * FROM (
  SELECT
    ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
    columns
  FROM tablename
) AS foo
WHERE rownumber <= n

(que acabo de copiar del sitio vinculado anteriormente ya que nunca uso esos DB)

Actualización: a partir de PostgreSQL 8.4, se admiten las funciones estándar de ventanas, por lo que se espera que el segundo ejemplo funcione también para PostgreSQL.

Actualización: SQLite agregó funciones de ventana compatibles con la versión 3.25.0 en 2018-09-15, por lo que ambos formularios también funcionan en SQLite.

Henrik Gustafsson
fuente
3
MySQL usa la sintaxis OFFSET y LIMIT también. Firebird utiliza las palabras clave FIRST y SKIP, pero se colocan justo después de SELECT.
Doug
77
¿No debería ser eso solo WHERE rownumber = npara obtener la enésima fila?
Steve Bennett
MySQL admite funciones de ventana desde la versión 8. MariaDB desde la versión 10.2
Paul Spiegel
102

PostgreSQL admite funciones de ventanas definidas por el estándar SQL, pero son incómodas, por lo que la mayoría de la gente usa (el no estándar) LIMIT/OFFSET :

SELECT
    *
FROM
    mytable
ORDER BY
    somefield
LIMIT 1 OFFSET 20;

Este ejemplo selecciona la fila 21. OFFSET 20le dice a Postgres que omita los primeros 20 registros. Si no especifica una ORDER BYcláusula, no hay garantía de qué registro obtendrá, lo que rara vez es útil.

Neall
fuente
31

No estoy seguro del resto, pero sé que SQLite y MySQL no tienen ningún orden de fila "predeterminado". En esos dos dialectos, al menos, el siguiente fragmento toma la decimoquinta entrada de the_table, ordenando por la fecha / hora en que se agregó:

SELECT * FROM the_table ORDER BY added DESC LIMIT 1,15

(por supuesto, necesitaría tener un campo DATETIME agregado y establecerlo en la fecha / hora en que se agregó esa entrada ...)

Tetera Ellen
fuente
Esta parece ser la mejor manera de limitar la consulta con un valor de desplazamiento en línea. ¿Pero no deberíamos usar 0,14 aquí? 1,15 saldrá de la primera fila.
Gladiator
¿Pero qué significa el 15? Sé que el 1 dice para obtener un registro. La coma no se usa en el ejemplo que verifiqué 1keydata.com/sql/sql-limit.html
committedandroider
1
En realidad, desde aquí php.about.com/od/mysqlcommands/g/Limit_sql.htm , si quisieras obtener la 15ª entrada, ¿no harías LIMIT 14, 1 (0 es el primer elemento, 1 de longitud
committedandroider
debe ser SELECT * FROM the_table ORDER BY agregado DESC LIMIT 15,1
JerryGoyal
25

SQL 2005 y superior tiene esta característica incorporada. Use la función ROW_NUMBER (). Es excelente para páginas web con un estilo de navegación << Anterior y Siguiente >>:

Sintaxis:

SELECT
    *
FROM
    (
        SELECT
            ROW_NUMBER () OVER (ORDER BY MyColumnToOrderBy) AS RowNum,
            *
        FROM
            Table_1
    ) sub
WHERE
    RowNum = 23
Ben Breen
fuente
Prefiero esta solución, ya que se siente más directa.
FoxArc
18

Sospecho que esto es muy ineficiente, pero es un enfoque bastante simple, que funcionó en un pequeño conjunto de datos en el que lo probé.

select top 1 field
from table
where field in (select top 5 field from table order by field asc)
order by field desc

Esto obtendría el quinto elemento, cambiar el segundo número superior para obtener un enésimo elemento diferente

Solo servidor SQL (creo), pero debería funcionar en versiones anteriores que no admiten ROW_NUMBER ().

Tim Saunders
fuente
Voy a usar esto ya que ROW_NUMBER () no funciona en SQL 2000 (sí, todavía tenemos un cliente en SQL 2000). Específicamente, voy a reemplazar el '5' con una variable iteradora de un bucle y usar que copiar y modificar cada fila de una tabla a su vez. Tal vez alguien vea este comentario y lo encuentre útil
Inversus
15

1 cambio pequeño: n-1 en lugar de n.

select *
from thetable
limit n-1, 1
Nick Berardi
fuente
cual tecnologia
user230910
14

Verifíquelo en SQL Server:

Select top 10 * From emp 
EXCEPT
Select top 9 * From emp

¡Esto te dará la décima fila de la tabla emp!

Rameshwar Pawale
fuente
Ya ha proporcionado una respuesta a esta pregunta aquí. Elimine la respuesta que cree que no es correcta. Si cree que ambas respuestas son correctas, publique ambas en un solo lugar
SpringLearner
11

Al contrario de lo que afirman algunas de las respuestas, el estándar SQL no guarda silencio sobre este tema.

Desde SQL: 2003, ha podido usar "funciones de ventana" para omitir filas y limitar conjuntos de resultados.

Y en SQL: 2008, se agregó un enfoque un poco más simple, utilizando
OFFSET skip ROWS FETCH FIRST n ROWS ONLY

Personalmente, no creo que la adición de SQL: 2008 fuera realmente necesaria, por lo que si fuera ISO, lo habría mantenido fuera de un estándar ya bastante grande.

Troels Arvin
fuente
Sin embargo, es bueno que haya un estándar, hace que la vida de las personas como yo sea más fácil, y que Microsoft sea tan amable de hacer cosas de manera estándar :)
user230910
7

Cuando solíamos trabajar en MSSQL 2000, hicimos lo que llamamos el "triple flip":

EDITADO

DECLARE @InnerPageSize int
DECLARE @OuterPageSize int
DECLARE @Count int

SELECT @Count = COUNT(<column>) FROM <TABLE>
SET @InnerPageSize = @PageNum * @PageSize
SET @OuterPageSize = @Count - ((@PageNum - 1) * @PageSize)

IF (@OuterPageSize < 0)
    SET @OuterPageSize = 0
ELSE IF (@OuterPageSize > @PageSize)
    SET @OuterPageSize = @PageSize

DECLARE @sql NVARCHAR(8000)

SET @sql = 'SELECT * FROM
(
    SELECT TOP ' + CAST(@OuterPageSize AS nvarchar(5)) + ' * FROM
    (
        SELECT TOP ' + CAST(@InnerPageSize AS nvarchar(5)) + ' * FROM <TABLE> ORDER BY <column> ASC
    ) AS t1 ORDER BY <column> DESC
) AS t2 ORDER BY <column> ASC'

PRINT @sql
EXECUTE sp_executesql @sql

No era elegante ni rápido, pero funcionó.

Adam V
fuente
Digamos que tiene 25 filas y desea la tercera página de tamaño de página de 10 filas, es decir, las filas 21-25. La consulta más interna obtiene las 30 filas superiores (filas 1-25). La consulta intermedia obtiene las últimas 10 filas (filas 25-16). La consulta externa los reordena y devuelve las filas 16-25. Esto es claramente incorrecto si desea las filas 21-25.
Bill Karwin
Ahora no funciona si queremos una página central. Digamos que tenemos 25 filas y queremos la segunda página, es decir, las filas 11-20. La consulta interna obtiene las 2 * 10 = 20 filas superiores, o las filas 1-20. La consulta intermedia obtiene las últimas 15 filas: 25 - ((2-1) * 10) = 15, lo que da como resultado las filas 20-6. La última consulta invierte el orden y devuelve las filas 6-20. Esta técnica no funciona, a menos que el número total de filas sea un múltiplo del tamaño de página deseado.
Bill Karwin
Quizás la mejor conclusión es que deberíamos actualizar cualquier instancia restante de MS SQL Server 2000. :-) Es casi 2012, y este problema se ha resuelto de mejor manera durante muchos años.
Bill Karwin
@Bill Karwin: tenga en cuenta los IF / ELSE IFbloques debajo del OuterPageSizecálculo: en las páginas 1 y 2, volverán a colocar el OuterPageSizevalor en 10. En la página 3 (filas 21-25) el cálculo devolverá correctamente 5, y en todas las páginas 4 y superiores, el resultado negativo del cálculo se reemplazará por 0 (aunque probablemente sería más rápido devolver una fila de datos vacía inmediatamente en ese punto).
Adam V
Oh ya veo ahora. Bueno, mantengo mi opinión de que usar MS SQL Server 2000 hoy no merece la pena.
Bill Karwin el
6

SERVIDOR SQL


Seleccione el registro n 'th desde arriba

SELECT * FROM (
SELECT 
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID) AS ROW
FROM TABLE 
) AS TMP 
WHERE ROW = n

seleccione el registro n desde el fondo

SELECT * FROM (
SELECT 
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID DESC) AS ROW
FROM TABLE 
) AS TMP 
WHERE ROW = n
Aditya
fuente
5

Oráculo:

select * from (select foo from bar order by foo) where ROWNUM = x
Mark Harrison
fuente
1
where ROWNUM = xsolo funcionará para x = 1 en Oracle DB. es decir where ROWNUM = 2, no devolverá ninguna fila.
aff
5

En Oracle 12c, puede usar la OFFSET..FETCH..ROWS opción conORDER BY

Por ejemplo, para obtener el 3er registro desde arriba:

SELECT * 
FROM   sometable
ORDER BY column_name
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY;
Kaushik Nayak
fuente
4

Aquí hay una solución rápida de su confusión.

SELECT * FROM table ORDER BY `id` DESC LIMIT N, 1

Aquí puede obtener la última fila completando N = 0, la segunda última mediante N = 1, la cuarta última completando N = 3 y así sucesivamente.

Esta es una pregunta muy común sobre la entrevista y es muy simple.

Además, si desea Cantidad, ID o algún Orden de clasificación numérico, puede utilizar la función CAST en MySQL.

SELECT DISTINCT (`amount`) FROM cart ORDER BY CAST( `amount` AS SIGNED ) DESC LIMIT 4 , 1

Aquí Al completar N = 4, podrá obtener el quinto último registro de la cantidad más alta de la tabla CART. Puede ajustar su nombre de campo y tabla y encontrar una solución.

Amit Shah
fuente
3

AÑADIR:

LIMIT n,1

Eso limitará los resultados a un resultado que comience en el resultado n.

Andrew G. Johnson
fuente
3

Por ejemplo, si desea seleccionar cada décima fila en MSSQL, puede usar;

SELECT * FROM (
  SELECT
    ROW_NUMBER() OVER (ORDER BY ColumnName1 ASC) AS rownumber, ColumnName1, ColumnName2
  FROM TableName
) AS foo
WHERE rownumber % 10 = 0

Simplemente tome el MOD y cambie el número 10 aquí cualquier número que desee.

EA
fuente
3

Para SQL Server, una forma genérica de ir por número de fila es la siguiente:

SET ROWCOUNT @row --@row = the row number you wish to work on.

Por ejemplo:

set rowcount 20   --sets row to 20th row

select meat, cheese from dbo.sandwich --select columns from table at 20th row

set rowcount 0   --sets rowcount back to all rows

Esto devolverá la información de la fila 20. Asegúrese de poner el recuento de filas 0 después.

Zoe
fuente
2

LIMIT n, 1 no funciona en MS SQL Server. Creo que es la única base de datos importante que no admite esa sintaxis. Para ser justos, no es parte del estándar SQL, aunque es tan ampliamente compatible que debería serlo. En todo excepto el servidor SQL LIMIT funciona muy bien. Para el servidor SQL, no he podido encontrar una solución elegante.

Kibbee
fuente
1
Excepto Oracle, DB2, bueno, casi todas las bases de datos de nivel empresarial en todo el mundo. PostgreSQL es la única base de datos con capacidad empresarial que admite la palabra clave LIMIT, y eso se debe principalmente a que, al ser de código abierto, debe ser accesible por la multitud ignorante de MySQL de ACID.
David
3
@AlexD Esta "respuesta" se publicó en los viejos tiempos de Stackoverflow antes de que se implementaran los comentarios. Hubiera publicado esto como un comentario a otra respuesta, pero a la vez, los comentarios no existían.
Kibbee
2

Aquí hay una versión genérica de un sproc que escribí recientemente para Oracle que permite la paginación / clasificación dinámica: HTH

-- p_LowerBound = first row # in the returned set; if second page of 10 rows,
--                this would be 11 (-1 for unbounded/not set)
-- p_UpperBound = last row # in the returned set; if second page of 10 rows,
--                this would be 20 (-1 for unbounded/not set)

OPEN o_Cursor FOR
SELECT * FROM (
SELECT
    Column1,
    Column2
    rownum AS rn
FROM
(
    SELECT
        tbl.Column1,
        tbl.column2
    FROM MyTable tbl
    WHERE
        tbl.Column1 = p_PKParam OR
        tbl.Column1 = -1
    ORDER BY
        DECODE(p_sortOrder, 'A', DECODE(p_sortColumn, 1, Column1, 'X'),'X'),
        DECODE(p_sortOrder, 'D', DECODE(p_sortColumn, 1, Column1, 'X'),'X') DESC,
        DECODE(p_sortOrder, 'A', DECODE(p_sortColumn, 2, Column2, sysdate),sysdate),
        DECODE(p_sortOrder, 'D', DECODE(p_sortColumn, 2, Column2, sysdate),sysdate) DESC
))
WHERE
    (rn >= p_lowerBound OR p_lowerBound = -1) AND
    (rn <= p_upperBound OR p_upperBound = -1);
Greg Hurlman
fuente
2

Pero realmente, ¿no es todo esto realmente solo trucos de salón para un buen diseño de base de datos en primer lugar? Las pocas veces que necesitaba una funcionalidad como esta era para una simple consulta para hacer un informe rápido. Para cualquier trabajo real, el uso de trucos como estos es un problema atractivo. Si se necesita seleccionar una fila en particular, simplemente tenga una columna con un valor secuencial y termine con ella.


fuente
2

Para el servidor SQL, lo siguiente devolverá la primera fila de la tabla.

declare @rowNumber int = 1;
    select TOP(@rowNumber) * from [dbo].[someTable];
EXCEPT
    select TOP(@rowNumber - 1) * from [dbo].[someTable];

Puede recorrer los valores con algo como esto:

WHILE @constVar > 0
BEGIN
    declare @rowNumber int = @consVar;
       select TOP(@rowNumber) * from [dbo].[someTable];
    EXCEPT
       select TOP(@rowNumber - 1) * from [dbo].[someTable];  

       SET @constVar = @constVar - 1;    
END;
nPcomp
fuente
1

En Sybase SQL Anywhere:

SELECT TOP 1 START AT n * from table ORDER BY whatever

No olvides ORDER BY o no tiene sentido.

Graeme Perrow
fuente
1

T-SQL: selección del N ° de registro de una tabla

select * from
 (select row_number() over (order by Rand() desc) as Rno,* from TableName) T where T.Rno = RecordNumber

Where  RecordNumber --> Record Number to Select
       TableName --> To be Replaced with your Table Name

Por ejemplo, para seleccionar el 5º registro de una tabla Empleado, su consulta debe ser

select * from
 (select row_number() over (order by Rand() desc) as Rno,* from Employee) T where T.Rno = 5
Sangeeth Krishna
fuente
1
SELECT * FROM emp a
WHERE  n = (SELECT COUNT( _rowid)
              FROM emp b
             WHERE a. _rowid >= b. _rowid);
Rahul Sharma
fuente
1
SELECT
    top 1 *
FROM
    table_name
WHERE
    column_name IN (
        SELECT
            top N column_name
        FROM
            TABLE
        ORDER BY
            column_name
    )
ORDER BY
    column_name DESC

He escrito esta consulta para encontrar la enésima fila. Ejemplo con esta consulta sería

SELECT
    top 1 *
FROM
    Employee
WHERE
    emp_id IN (
        SELECT
            top 7 emp_id
        FROM
            Employee
        ORDER BY
            emp_id
    )
ORDER BY
    emp_id DESC
Arjun Chiddarwar
fuente
0

increíble que puedas encontrar un motor SQL ejecutando este ...

WITH sentence AS
(SELECT 
    stuff,
    row = ROW_NUMBER() OVER (ORDER BY Id)
FROM 
    SentenceType
    )
SELECT
    sen.stuff
FROM sentence sen
WHERE sen.row = (ABS(CHECKSUM(NEWID())) % 100) + 1
jrEving
fuente
0

Nada sofisticado, sin funciones especiales, en caso de que uses Caché como yo ...

SELECT TOP 1 * FROM (
  SELECT TOP n * FROM <table>
  ORDER BY ID Desc
)
ORDER BY ID ASC

Dado que tiene una columna de ID o una columna de sello de fecha en la que puede confiar.

Abogado del diablo
fuente
0

Así es como lo haría dentro de DB2 SQL, creo que el RRN (número de registro relativo) está almacenado en la tabla por el O / S;

SELECT * FROM (                        
   SELECT RRN(FOO) AS RRN, FOO.*
   FROM FOO                         
   ORDER BY RRN(FOO)) BAR             
 WHERE BAR.RRN = recordnumber

fuente
0
select * from 
(select * from ordered order by order_id limit 100) x order by 
x.order_id desc limit 1;

Primero seleccione las 100 filas superiores ordenando en forma ascendente y luego seleccione la última fila ordenando en forma descendente y limite a 1. Sin embargo, esta es una declaración muy costosa ya que accede a los datos dos veces.

Dwipam Katariya
fuente
0

Me parece que, para ser eficiente, necesita 1) generar un número aleatorio entre 0 y uno menos que el número de registros de la base de datos, y 2) poder seleccionar la fila en esa posición. Desafortunadamente, diferentes bases de datos tienen diferentes generadores de números aleatorios y diferentes formas de seleccionar una fila en una posición en un conjunto de resultados; por lo general, especifica cuántas filas omitir y cuántas filas desea, pero se hace de manera diferente para diferentes bases de datos. Aquí hay algo que me funciona en SQLite:

select * 
from Table 
limit abs(random()) % (select count(*) from Words), 1;

Depende de poder usar una subconsulta en la cláusula límite (que en SQLite es LIMIT <recs to skip>, <recs to take>) Seleccionar el número de registros en una tabla debería ser particularmente eficiente, ya que forma parte de la base de datos metadatos, pero eso depende de la implementación de la base de datos. Además, no sé si la consulta realmente generará el conjunto de resultados antes de recuperar el enésimo registro, pero espero que no sea necesario. Tenga en cuenta que no estoy especificando una cláusula "ordenar por". Podría ser mejor "ordenar por" algo como la clave primaria, que tendrá un índice: obtener el enésimo registro de un índice podría ser más rápido si la base de datos no puede obtener el enésimo registro de la base de datos sin construir el conjunto de resultados .

John Deighan
fuente
0

La respuesta más adecuada que he visto en este artículo para el servidor SQL

WITH myTableWithRows AS (
    SELECT (ROW_NUMBER() OVER (ORDER BY myTable.SomeField)) as row,*
    FROM myTable)
SELECT * FROM myTableWithRows WHERE row = 3
Knight Fighter
fuente