¿Equivalente a LIMIT y OFFSET para SQL Server?

172

En PostgreSQL existen las palabras clave Limity Offsetque permitirán una paginación muy fácil de los conjuntos de resultados.

¿Cuál es la sintaxis equivalente para SQL Server?

Earlz
fuente
Para el servidor SQL 2012, esta característica se implementa de manera fácil. Ver mi respuesta
Somnath Muluk
Gracias por hacer esta pregunta, nos vemos obligados a pasar de MySQL a MsSQL :(
tempcke
Puede usar offset y recuperar la siguiente declaración en el servidor SQL con orden por cláusula. Pruébalo youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Respuestas:

139

El equivalente de LIMITes SET ROWCOUNT, pero si desea 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

La ventaja aquí es la parametrización del desplazamiento y el límite en caso de que decida cambiar sus opciones de paginación (o permitir que el usuario lo haga).

Nota: el @Offsetparámetro debe usar una indexación basada en uno para esto en lugar de la indexación normal basada en cero.

Aaronaught
fuente
22
Viejo ahora. SQL Server 2012 y versiones posteriores admiten OFFSET / FETCH
Joel Coehoorn
31
@JoelCoehoorn No es viejo. Acabo de ser asignado al proyecto usando SLQ Server 2008 habiendo usado solo mysql en el pasado ...
Cthulhu
Esto es bastante bueno, pero debe ajustarse un pocoWHERE RowNum >= (@Offset + 1)
Eric Herlitz
55
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified. MSSQL2008 R2.
Paul
2
@Aaronaught Si mi Tabletiene 200k registros, primero buscará todos y luego aplicará el límite. ¿Es eficiente esta consulta?
Jigar
231

Esta característica ahora se simplifica en SQL Server 2012. Esto funciona desde SQL Server 2012 en adelante.

Límite con desplazamiento para seleccionar 11 a 20 filas en SQL Server:

SELECT email FROM emailTable 
WHERE user_id=3
ORDER BY Id
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
  • OFFSET: número de filas omitidas
  • NEXT: número requerido de filas siguientes

Referencia: https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-2017

Somnath Muluk
fuente
44
¿Hay un equivalente de SQL_CALC_FOUND_ROWScuando se usa esto?
Petah
1
@Petah @@ Rowcount te dará lo que creo
Rob Sedgwick
GOTCHA: No puedes usar esto desde un CTE. Tiene que ser usado en la consulta principal. Quería limitar la cantidad de filas devueltas (paginación) y luego realizar un cálculo costoso a las aproximadamente 10 filas devueltas, en lugar de determinar las filas, realizar el cálculo costoso y luego omitir / tomar lo que necesitaba. La respuesta de @ Aaronaught funcionará para aquellos que necesiten restringir filas dentro de un CTE.
Derreck Dean
@Somnath Muluk Este desplazamiento y recuperación lleva mucho tiempo para un mayor volumen de datos de ejemplo con un desplazamiento de 1000000. ¿Cómo puedo lidiar con esto?
Saroj Shrestha
1
@SarojShrestha: Este no es un problema de compensación y recuperación. Debería volver a visitar la arquitectura de su tabla ahora. Considere el particionamiento de tablas, su fila de datos y sus diferentes tipos de columnas y el tamaño total de la tabla, considere archivar algunas filas si no es necesario regularmente, verifique las especificaciones de su servidor.
Somnath Muluk
23
select top {LIMIT HERE} * from (
      select *, ROW_NUMBER() over (order by {ORDER FIELD}) as r_n_n 
      from {YOUR TABLES} where {OTHER OPTIONAL FILTERS}
) xx where r_n_n >={OFFSET HERE}

Una nota: esta solución solo funcionará en SQL Server 2005 o superior, ya que esto fue cuando ROW_NUMBER()se implementó.

jorgeu
fuente
He estado usando esta consulta por un tiempo y funciona muy bien, así que gracias por eso. Me pregunto qué representa el 'xx'.
Urbley
La subconsulta requiere un nombre. como no lo estoy usando solo pon xx allí
jorgeu
2
El xx es solo un alias de tabla. Podría ser un poco más claro si dijerasAS xx
Concrete Gannet
Alguien sabe cómo unirse a la izquierda en esta consulta?
Drenyl
12

Puede usar ROW_NUMBER en una expresión de tabla común para lograr esto.

;WITH My_CTE AS
(
     SELECT
          col1,
          col2,
          ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
)
SELECT
     col1,
     col2
FROM
     My_CTE
WHERE
     row_number BETWEEN @start_row AND @end_row
Tom H
fuente
4

Para mí, el uso de OFFSET y FETCH juntos fue lento, por lo que utilicé una combinación de TOP y OFFSET como esta (que fue más rápido):

SELECT TOP 20 * FROM (SELECT columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Nota: Si usa TOP y OFFSET juntos en la misma consulta como:

SELECT TOP 20 columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS

Luego obtiene un error, por lo que para usar TOP y OFFSET juntos debe separarlo con una subconsulta.

Y si necesita usar SELECT DISTINCT, la consulta es como:

SELECT TOP 20 FROM (SELECT DISTINCT columname1, columname2
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Nota: El uso de SELECT ROW_NUMBER con DISTINCT no funcionó para mí.

sebasdev
fuente
1
Obtengo "A TOP no se puede usar en la misma consulta o subconsulta que un OFFSET".
MichaelRushton
Tienes razón @MichaelRushton, no se puede usar en la misma consulta o en la misma subconsulta, entonces debes usar una subconsulta para separarla. Entonces, si tiene el SQL como SELECT TOP 20 id FROM table1 where id > 10 order by date OFFSET 20 rows, debe transformarlo como SELECT TOP 20 * FROM (SELECT id FROM table1 where id > 10 order by date OFFSET 20 ROWS) t1. Editaré mi respuesta. Gracias y disculpa mi inglés.
sebasdev
2

Otra muestra:

declare @limit int 
declare @offset int 
set @offset = 2;
set @limit = 20;
declare @count int
declare @idxini int 
declare @idxfim int 
select @idxfim = @offset * @limit
select @idxini = @idxfim - (@limit-1);
WITH paging AS
    (
        SELECT 
             ROW_NUMBER() OVER (order by object_id) AS rowid, *
        FROM 
            sys.objects 
    )
select *
    from 
        (select COUNT(1) as rowqtd from paging) qtd, 
            paging 
    where 
        rowid between @idxini and @idxfim
    order by 
        rowid;
sillyim
fuente
15
Eliminé tu discurso de odio anti-microsoft. No discutas guerras santas aquí; solo responda y haga preguntas de manera no subjetiva.
Earlz
2

Hay aquí alguien que le dice acerca de esta característica en SQL 2011, es triste que elijan un poco de la palabra llave "OFFSET / FETCH" pero no es standart que bien.

keepkeywordspleeeease
fuente
2

Agregando una ligera variación en la solución de Aaronaught, típicamente parametrizo el número de página (@PageNum) y el tamaño de la página (@PageSize). De esta manera, cada evento de clic de página solo envía el número de página solicitado junto con un tamaño de página configurable:

begin
    with My_CTE  as
    (
         SELECT col1,
              ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
    )
    select * from My_CTE
            WHERE RowNum BETWEEN (@PageNum - 1) * (@PageSize + 1) 
                              AND @PageNum * @PageSize

end
Tom
fuente
2

Lo más cerca que puedo hacer es

select * FROM( SELECT *, ROW_NUMBER() over (ORDER BY ID ) as ct from [db].[dbo].[table] ) sub where ct > fromNumber  and ct <= toNumber

Que supongo que es similar a select * from [db].[dbo].[table] LIMIT 0, 10

usuario2991730
fuente
2
-- @RowsPerPage  can be a fixed number and @PageNumber number can be passed 
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 2

SELECT *

FROM MemberEmployeeData

ORDER BY EmployeeNumber

OFFSET @PageNumber*@RowsPerPage ROWS

FETCH NEXT 10 ROWS ONLY
Shakeel
fuente
Funciona para Microsoft SQL Server 13.x Muchas gracias.
Shubham Arya
1
select top (@TakeCount) * --FETCH NEXT
from(
    Select  ROW_NUMBER() OVER (order by StartDate) AS rowid,*
    From YourTable
)A
where Rowid>@SkipCount --OFFSET
Barny
fuente
1
@nombre_row :nombre ligne par page  
@page:numero de la page

//--------------code sql---------------

declare  @page int,@nombre_row int;
    set @page='2';
    set @nombre_row=5;
    SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY etudiant_ID ) AS RowNum, *
      FROM      etudiant

    ) AS RowConstrainedResult
WHERE   RowNum >= ((@page-1)*@nombre_row)+1
    AND RowNum < ((@page)*@nombre_row)+1
ORDER BY RowNum
noureddine ahmer el kaab
fuente
1

Como nadie proporcionó este código todavía:

SELECT TOP @limit f1, f2, f3...
FROM t1
WHERE c1 = v1, c2 > v2...
AND
    t1.id NOT IN
        (SELECT TOP @offset id
         FROM t1
         WHERE c1 = v1, c2 > v2...
         ORDER BY o1, o2...)
ORDER BY o1, o2...

Puntos importantes:

  • ORDER BY debe ser idéntico
  • @limit se puede reemplazar con varios resultados para recuperar,
  • @offset es el número de resultados para omitir
  • Compare el rendimiento con las soluciones anteriores, ya que pueden ser más eficientes.
  • esta solución duplica wherey order bycláusulas, y proporcionará resultados incorrectos si no están sincronizados
  • por otro lado, ¿ order byestá ahí explícitamente si eso es lo que se necesita?
przemo_li
fuente
1

Específicamente para SQL-SERVER, puede lograrlo de muchas maneras diferentes. Para un ejemplo real, tomamos la tabla Cliente aquí.

Ejemplo 1: con "SET ROWCOUNT"

SET ROWCOUNT 10
SELECT CustomerID, CompanyName from Customers
ORDER BY CompanyName

Para devolver todas las filas, establezca ROWCOUNT en 0

SET ROWCOUNT 0  
SELECT CustomerID, CompanyName from Customers
    ORDER BY CompanyName

Ejemplo 2: con "ROW_NUMBER y OVER"

With Cust AS
( SELECT CustomerID, CompanyName,
ROW_NUMBER() OVER (order by CompanyName) as RowNumber 
FROM Customers )
select *
from Cust
Where RowNumber Between 0 and 10

Ejemplo 3: con "OFFSET y FETCH", pero con este "ORDER BY" es obligatorio

SELECT CustomerID, CompanyName FROM Customers
ORDER BY CompanyName
OFFSET 0 ROWS
FETCH NEXT 10 ROWS ONLY

Espero que esto te ayude.

Humayoun_Kabir
fuente
-1

En el servidor SQL, usaría TOP junto con ROW_NUMBER ()

SQLMenace
fuente
-1

Desde entonces, pruebo más veces este script más útil en 1 millón de registros cada página. 100 registros con paginación funcionan más rápido. Mi PC ejecuta este script 0 segundos mientras lo comparo con mysql tiene su propio límite y compensa aproximadamente 4.5 segundos para obtener el resultado.

Alguien puede perder de entender Row_Number () siempre ordenar por campo específico. En caso de que necesitemos definir solo una fila en secuencia, debemos usar:

ROW_NUMBER () OVER (ORDER BY (SELECT NULL))

SELECT TOP {LIMIT} * FROM (
      SELECT TOP {LIMIT} + {OFFSET} ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS ROW_NO,*
      FROM  {TABLE_NAME}
) XX WHERE ROW_NO > {OFFSET}

Explique:

  • {LIMIT}: número de registros para cada página
  • {OFFSET}: número de registros de omisión
Vanda Ros
fuente
2
Si bien este código puede resolver la pregunta, incluir una explicación de cómo y por qué esto resuelve el problema realmente ayudaría a mejorar la calidad de su publicación, y probablemente resultaría en más votos positivos. Recuerde que está respondiendo la pregunta para los lectores en el futuro, no solo la persona que pregunta ahora. Por favor, editar su respuesta para agregar explicaciones y dar una indicación de lo que se aplican limitaciones y supuestos.
Brian