LÍMITE 10..20 en SQL Server

161

Estoy tratando de hacer algo como:

SELECT * FROM table LIMIT 10,20

o

SELECT * FROM table LIMIT 10 OFFSET 10

pero usando SQL Server

La única solución que encontré parece exagerada:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE row > 5 and row <= 10

También encontré :

SELECT TOP 10 * FROM stuff; 

... pero no es lo que quiero hacer, ya que no puedo especificar el límite inicial.

¿Hay otra forma de hacer eso?

Además, por curiosidad, ¿hay alguna razón por la que SQL Server no admite la LIMITfunción o algo similar? No quiero ser malo, pero eso realmente suena como algo que un DBMS necesita ... Si lo hace, ¡lo siento por ser tan ignorante! He estado trabajando con MySQL y SQL + durante los últimos 5 años, así que ...

marcgg
fuente
1
Lo mejor que he podido lograr es usar un CTE ROW_NUMBER()y limitar TOPel ancho del rango y una WHEREcondición para un límite del rango. También he notado un rendimiento mucho mejor si la TOPcláusula usa un literal en lugar de una variable
Jodrell el
El problema con cualquier solución que involucre ROW_NUMBER () es que si no sabe de antemano qué columnas tendrá, y tiene uniones, y las tablas unidas tienen el mismo nombre de columna, obtendrá un "La columna 'xxx' se especificó varias veces ". Esto no es tan raro como podría parecer inicialmente. Uso Dapper, y todas mis tablas tienen una columna Id. Dapper divide y asigna mapas sobre eso, así que no quiero cambiarles el nombre, pero no puedo usar el alias SELECT * FROM ([consulta original]). ¡Todavía no he encontrado una solución!
Steve Owen

Respuestas:

104

La LIMITcláusula no es parte del SQL estándar. MySQL, PostgreSQL y SQLite lo admiten como una extensión de proveedor para SQL.

Otras marcas de bases de datos pueden tener características similares (por ejemplo, TOPen Microsoft SQL Server), pero estas no siempre funcionan de manera idéntica.

Es difícil de usar TOPen Microsoft SQL Server para imitar la LIMITcláusula. Hay casos en los que simplemente no funciona.

La solución que mostró, ROW_NUMBER()está disponible en Microsoft SQL Server 2005 y versiones posteriores. Esta es la mejor solución (por ahora) que funciona únicamente como parte de la consulta.

Otra solución es usar TOPpara buscar las primeras filas de recuento + desplazamiento , y luego usar la API para buscar más allá de las primeras filas de desplazamiento .

Ver también:

Bill Karwin
fuente
135

Para SQL Server 2012 + puede usar .

SELECT  *
FROM     sys.databases
ORDER BY name 
OFFSET  5 ROWS 
FETCH NEXT 5 ROWS ONLY 
Martin Smith
fuente
10
SQl Server 2012 requiere especificar ORDER BY cuando usa OFFSET 5 ROWS FETCH NEXT 5 ROWS SOLAMENTE, mientras que MySql y SQLite no requieren ORDER BY cuando usa LIMIT 5,5
Tomas Kubes
44
@ qub1n - Sin embargo, MySQL no garantiza qué filas obtendrá en ese caso.
Martin Smith
3
¿Tiene que usar offset, o puede dejar esa línea fuera (suponiendo que no desea un desplazamiento)?
Cullub
1
@Cullub: la cláusula OFFSET es obligatoria con FETCH. Nunca se puede usar, ORDENAR POR ... FETCH. - así que lo necesitasOFFSET 0 ROWS
Martin Smith
Su consulta de ejemplo funciona bien, pero si cambio el nombre de la tabla y ordeno por col como se muestra a continuación SELECCIONE * DE DimProduct ORDENAR POR ProductKey OFFSET 5 FILAS FETCH SIGUIENTE 5 FILAS SOLAMENTE Da errorParse error at line: 4, column: 1: Incorrect syntax near 'OFFSET'
shashwat
36

como encontraste, este es el método de servidor sql preferido:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE a.row > 5 and a.row <= 10
KM.
fuente
¿Por qué adespués de la selección interna? Supongo que le estás dando un alias a la selección interna, pero parece que nunca la usas ... ¿Deberías hacerlo en a.rowlugar de solo row?
Lucas
3
@Lucas, debe poner un alias después de la ( )tabla derivada, pero lo dejará ir si luego olvida usarlo para referirse a las columnas. Aunque lo arreglé ...
KM.
gracias, lo descubrí por las malas (traté de dejar el alias).
Lucas
1
Votado +1: Sin embargo, la respuesta de @MartinSmith se vota más, después de comparar el plan de ejecución con este enfoque, descubrí que esta solución funciona mucho más rápido.
Duro
10

Si está utilizando SQL Server 2012+, vote por la respuesta de Martin Smith y use las extensiones OFFSETy FETCH NEXTpara ORDER BY,

Si tiene la mala suerte de estar atascado con una versión anterior, podría hacer algo como esto,

WITH Rows AS
(
    SELECT
              ROW_NUMBER() OVER (ORDER BY [dbo].[SomeColumn]) [Row]
            , *
        FROM
              [dbo].[SomeTable]
)
SELECT TOP 10
          *
     FROM
         Rows
    WHERE Row > 10

Creo que es funcionalmente equivalente a

SELECT * FROM SomeTable LIMIT 10 OFFSET 10 ORDER BY SomeColumn

y la mejor manera que sé de hacerlo en TSQL, antes de MS SQL 2012.


Si hay muchas filas, puede obtener un mejor rendimiento utilizando una tabla temporal en lugar de un CTE.

Jodrell
fuente
Votado por señalar la respuesta de Martin Smith (y vincularla) al tiempo que proporciona una solución anterior a 2012. También para el consejo de la tabla temporal porque tienes razón :)
fujiiface 05 de
7

Desafortunadamente, ROW_NUMBER()es lo mejor que puedes hacer. En realidad, es más correcto, porque los resultados de una limito topcláusula realmente no tienen significado sin tener en cuenta un orden específico. Pero todavía es un dolor que hacer.

Actualización: Sql Server 2012 agrega una limitcaracterística similar a través de las palabras clave OFFSET y FETCH . Este es el enfoque ansi-estándar, en oposición a LIMIT, que es una extensión MySql no estándar.

Joel Coehoorn
fuente
@Joel: ¿Puede explicar por qué ROW_NUMBER () no puede numerar las filas de la forma en que salen de ORDER BY? Siempre me he preguntado por qué el "OVER (ORDER BY name)" es obligatorio, pero creo que hay una buena razón para ello. O al menos una razón.
Tomalak
3
porque no existe el orden sin una orden por cláusula. Obtiene el orden en que los registros estaban disponibles para el servidor, y eso podría cambiar de solicitud de consulta a solicitud de consulta.
Joel Coehoorn
1
@marcgg: nunca he leído ninguna indicación de que Microsoft planea implementar LIMIT. Incluso si tienen dicho plan, los proveedores de código cerrado tienden a no anunciar previamente las características. Sin duda, sería una característica útil, pero no sabemos cuánto trabajo sería implementar, dado su código.
Bill Karwin
3
Si no desea repetirse en la cláusula ORDER BY, use el alias ROW_NUMBER () en lugar del conjunto original de columnas.
Peter Radocchia
2
@Tomalak: en lo que respecta a SQL Server, el orden utilizado para calcular ROW_NUMBER () no tiene ninguna relación con el orden del conjunto de resultados. Es por eso que debe especificarlos por separado.
LukeH
6

¿Qué tal esto?

SET ROWCOUNT 10 

SELECT TOP 20 *
FROM sys.databases
ORDER BY database_id DESC

Te da las últimas 10 filas de las primeras 20 filas. Un inconveniente es que el orden se invierte, pero, al menos, es fácil de recordar.

David Patrick
fuente
66
¿Qué pasa si solo hay 14 filas en la tabla? Obtiene las filas 14 a 5, que no es lo mismo que las filas devueltas por LIMIT 10 OFFSET 10 (deben ser las filas 14 a 11).
Bill Karwin
2
SELECT TOP 10 *
FROM TABLE
WHERE IDCOLUMN NOT IN (SELECT TOP 10 IDCOLUMN FROM TABLE)

Debe dar registros 11-20. Probablemente no sea demasiado eficiente si se incrementa para obtener más páginas, y no está seguro de cómo podría verse afectado por los pedidos. Puede que tenga que especificar esto en ambas declaraciones WHERE.

Andy
fuente
1

Una buena manera es crear un procedimiento:

create proc pagination (@startfrom int ,@endto int) as
SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name desc) as row FROM sys.databases 
 ) a WHERE a.row > @startfrom and a.row <= @endto

al igual que el límite 0,2 /////////////// ejecutar paginación 0,4

Wahaj Latif
fuente
1

Solo para la solución de registro que funciona en la mayoría de los motores de bases de datos, aunque podría no ser la más eficiente:

Select Top (ReturnCount) *
From (
    Select Top (SkipCount + ReturnCount) *
    From SourceTable
    Order By ReverseSortCondition
) ReverseSorted
Order By SortCondition

Nota de Pelase: la última página todavía contendría filas ReturnCount sin importar qué es SkipCount. Pero eso podría ser algo bueno en muchos casos.

YB
fuente
1

El equivalente de LIMIT es SET ROWCOUNT, pero si desea una paginación genérica, es mejor escribir una consulta como esta:

;WITH Results_CTE AS
(
    SELECT
        Col1, Col2, ...,
        ROW_NUMBER() OVER (ORDER BY SortCol1, SortCol2, ...) AS RowNum
    FROM Table
    WHERE <whatever>
)
SELECT *
FROM Results_CTE
WHERE RowNum >= @Offset
AND RowNum < @Offset + @Limit
Satish Kumar sonker
fuente
0
select * from (select id,name,ROW_NUMBER() OVER (ORDER BY id  asc) as row
from tableName1) tbl1
where tbl1.row>=10 and tbl1.row<=15

Imprimirá filas de 10 a 15.

sjith
fuente
0

Hasta ahora, este formato es lo que funciona para mí (aunque no es el mejor rendimiento):

SELECT TOP {desired amount of rows} * 
FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY {order columns} asc)__row__ FROM {table})tmp
WHERE __row__ > {offset row count}

Una nota al margen, paginar sobre datos dinámicos puede conducir a resultados extraños / inesperados.

Charlie Affumigato
fuente
0

De la documentación en línea de MS SQL Server ( http://technet.microsoft.com/en-us/library/ms186734.aspx ), aquí está su ejemplo que he probado y funciona, para recuperar un conjunto específico de filas. ROW_NUMBER requiere un OVER, pero puede ordenar por lo que quiera:

WITH OrderedOrders AS
(
  SELECT SalesOrderID, OrderDate,
  ROW_NUMBER() OVER (ORDER BY OrderDate) AS RowNumber
  FROM Sales.SalesOrderHeader 
) 
SELECT SalesOrderID, OrderDate, RowNumber  
FROM OrderedOrders 
WHERE RowNumber BETWEEN 50 AND 60;
Shannon WM
fuente
0

Use todos los servidores SQL:; con tbl como (SELECT ROW_NUMBER () over (ordenar por (seleccione 1)) como RowIndex, * de la tabla) seleccione top 10 * de tbl donde RowIndex> = 10

Phạm Tấn Lợi
fuente
-3
 SELECT * FROM users WHERE Id Between 15 and 25

imprimirá de 15 a 25 como límite en MYSQl

SR1
fuente
2
¿Qué pasa si el usuario eliminó un registro entre 15 y 25?
Gökçer Gökdal