¿Cuál es la mejor manera de paginar resultados en SQL Server?

475

¿Cuál es la mejor manera (en cuanto al rendimiento) para paginar resultados en SQL Server 2000, 2005, 2008, 2012 si también desea obtener el número total de resultados (antes de paginar)?

Panagiotis Korros
fuente
26
Siempre me he preguntado por qué no solo admiten la especificación de un desplazamiento como parte de TOP (como el soporte de MySQL / Posgresql con LIMIT / OFFSET). Por ejemplo, podrían tener la sintaxis "SELECCIONAR TOP x, y ...." donde x = número de filas, y = desplazamiento inicial. También sería compatible con versiones anteriores.
gregmac
3
Hey, yo también ... 2005 paginación aplicación de SQL Es realmente tan incómodo ...
opensas
66
@gregmac - Sql Server 2012 tiene límite / compensación ahora.
OO
2
La solución aceptada no muestra cómo es la mejor manera (en cuanto al rendimiento). ¿Algún dato que lo respalde en grandes conjuntos de datos?
OO
3
@OO: Puede encontrar un buen punto de referencia aquí: 4guysfromrolla.com/webtech/042606-1.shtml . Sin embargo, el método de búsqueda superará cualquier paginación basada en desplazamiento.
Lukas Eder

Respuestas:

465

Obtener el número total de resultados y paginar son dos operaciones diferentes. Por el bien de este ejemplo, supongamos que la consulta que está tratando es

SELECT * FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate

En este caso, determinaría el número total de resultados usando:

SELECT COUNT(*) FROM Orders WHERE OrderDate >= '1980-01-01'

... lo que puede parecer ineficiente, pero en realidad es bastante eficiente, suponiendo que todos los índices, etc. estén configurados correctamente.

A continuación, para obtener resultados reales de forma paginada, la siguiente consulta sería más eficiente:

SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, *
          FROM      Orders
          WHERE     OrderDate >= '1980-01-01'
        ) AS RowConstrainedResult
WHERE   RowNum >= 1
    AND RowNum < 20
ORDER BY RowNum

Esto devolverá las filas 1-19 de la consulta original. Lo bueno aquí, especialmente para las aplicaciones web, es que no tiene que mantener ningún estado, excepto los números de fila que se devolverán.

mdb
fuente
37
Solo para notar que ROW_NUMBER () no existe en SQL Server 2000
John Hunter
66
¿Esto devuelve todas las filas de la consulta interna y luego se filtra en función de la consulta externa? por ejemplo: la consulta interna devuelve 100.000 y la consulta externa devuelve solo 20.
SoftwareGeek
2
@SoftwareGeek: piense en ello como la subconsulta (consulta interna) que devuelve una secuencia, que luego se lee hasta que se satisface la cláusula WHERE externa. La cantidad de filas involucradas en eso depende completamente de la consulta, pero el optimizador generalmente hace un muy buen trabajo al minimizar ese número. El uso del visor gráfico del plan de ejecución en SQL Server Management Studio (usar Consulta / Incluir plan de ejecución real) es muy educativo en ese sentido.
mdb
2
ok, ¿qué pasa si te duplican en la selección interna (como cuando tienes una combinación interna), cómo utilizas distinta porque RowNumber es diferente y no funciona
user217648
10
Microsoft agregó una nueva característica a SQL 2012 que hace que la paginación sea similar a MySQL. Sigue este enlace para aprender cómo. Es un artículo interesante: dbadiaries.com/…
Arash
513

Finalmente, se lanzó Microsoft SQL Server 2012 , realmente me gusta su simplicidad para una paginación, no tiene que usar consultas complejas como las respondidas aquí.

Para obtener las siguientes 10 filas simplemente ejecute esta consulta:

SELECT * FROM TableName ORDER BY id OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql#using-offset-and-fetch-to-limit-the-rows- regresado

Puntos clave a considerar al usarlo:

  • ORDER BYEs obligatorio utilizar la OFFSET ... FETCHcláusula.
  • OFFSETLa cláusula es obligatoria con FETCH. No puedes usar ORDER BY ... FETCH.
  • TOPno se puede combinar con OFFSETy FETCHen la misma expresión de consulta.
Õzbek
fuente
12
Todavía esperando en LISTAGG()/ GROUP_CONCAT().
Bacon Bits
1
@BaconBits Vea esta respuesta para obtener una forma furtiva de hacerlo con FOR XML: stackoverflow.com/a/273330/429949
Richard Marskell - Drackir
1
@ RichardMarskell-Drackir Hay muchos problemas con FOR XML PATH (''). Primero, reemplaza los caracteres de control XML con códigos de entidad XML. Espero que no tiene <, >o &en sus datos! Segundo, FOR XML PATH ('')usado de esta manera es en realidad sintaxis indocumentada. Se supone que debe especificar una columna con nombre o un nombre de elemento alternativo. No hacer ninguno no está en el documento, lo que significa que el comportamiento no es confiable. En tercer lugar, cuanto más aceptamos la FOR XML PATH ('')sintaxis rota , menos probable es que la MS realmente proporcione una función real LISTAGG() [ OVER() ] como la han necesitado.
Bacon Bits
44
vergüenza la perforación es tan malo mssqlgirl.com/...
Jon
55
@ Jon, esa publicación de blog vinculada no es representativa, en el sentido de que hace comparaciones basadas en devolver el resultado de la página buscando valores de la columna de identificación.
Noel Abrahams
103

Increíblemente, ninguna otra respuesta ha mencionado la forma más rápida de hacer paginación en todas las versiones de SQL Server. Las compensaciones pueden ser terriblemente lentas para números de página grandes, como se compara aquí . Hay una forma completamente diferente y mucho más rápida de realizar paginación en SQL. Esto a menudo se llama el "método de búsqueda" o "paginación de conjunto de claves" como se describe en esta publicación de blog aquí .

SELECT TOP 10 first_name, last_name, score, COUNT(*) OVER()
FROM players
WHERE (score < @previousScore)
   OR (score = @previousScore AND player_id < @previousPlayerId)
ORDER BY score DESC, player_id DESC

El "predicado de búsqueda"

Los valores @previousScorey @previousPlayerIdson los valores respectivos del último registro de la página anterior. Esto le permite buscar la página "siguiente". Si la ORDER BYdirección es ASC, simplemente use >en su lugar.

Con el método anterior, no puede saltar inmediatamente a la página 4 sin haber obtenido primero los 40 registros anteriores. Pero a menudo, no quieres saltar tan lejos de todos modos. En cambio, obtienes una consulta mucho más rápida que podría obtener datos en tiempo constante, dependiendo de tu indexación. Además, sus páginas permanecen "estables", sin importar si los datos subyacentes cambian (por ejemplo, en la página 1, mientras está en la página 4).

Esta es la mejor manera de implementar la paginación cuando la carga diferida de más datos en aplicaciones web, por ejemplo.

Tenga en cuenta que el "método de búsqueda" también se denomina paginación de conjunto de claves .

Total de registros antes de la paginación

La COUNT(*) OVER()función de ventana lo ayudará a contar el número de registros totales "antes de la paginación". Si está utilizando SQL Server 2000, deberá recurrir a dos consultas para el COUNT(*).

Lukas Eder
fuente
2
@ user960567: en términos de rendimiento, la paginación de conjunto de claves siempre superará la paginación de desplazamiento, sin importar si implementa la paginación de desplazamiento con el estándar SQL OFFSET .. FETCHo con ROW_NUMBER()trucos anteriores .
Lukas Eder
21
Tengo tres problemas con el método de búsqueda. [1] Un usuario no puede saltar a la página. [2] asume teclas secuenciales, es decir, si alguien elimina unas 3 filas, entonces obtengo una página de 7 elementos en lugar de 10. RowNumberme da 10 elementos consistentes por página. [3] no funciona con cuadrículas existentes que suponen pagenumbery pagesize.
Rebecca
77
@Junto: la búsqueda de conjunto de teclas no es adecuada para todos los casos. Definitivamente no es para cuadrículas de datos. Pero es perfecto para escenarios como el desplazamiento infinito de la página de alimentación de Facebook. No importa si se agregan nuevas publicaciones en la parte superior, las publicaciones posteriores del feed se agregarán correctamente en la parte inferior mientras se desplaza hacia abajo. Ejemplo de uso perfecto para esto ... Tal cosa sería mucho más difícil de implementar usando el límite de compensación / recuperación usando solo números.
Robert Koritnik
44
Tengo que estar de acuerdo con Junto. Este método descarta completamente un cliente que tiene una interfaz de usuario de paginación bastante estándar de "Anterior 1 2 3 (4) 5 6 Siguiente" donde los usuarios pueden avanzar. Esto no es exactamente un caso marginal en mi experiencia ...
AaronHS
3
Artículo paginación conjunto de claves aquí
Stéphane
31

Desde SQL Server 2012, podemos usar OFFSETy FETCH NEXTCláusula para lograr la paginación.

Pruebe esto, para SQL Server:

En el SQL Server 2012, se agregó una nueva característica en la cláusula ORDER BY, para consultar la optimización de un conjunto de datos, lo que facilita el trabajo con la paginación de datos para cualquier persona que escriba en T-SQL y para todo el Plan de ejecución en SQL Server.

Debajo del script T-SQL con la misma lógica utilizada en el ejemplo anterior.

--CREATING A PAGING WITH OFFSET and FETCH clauses IN "SQL SERVER 2012"
DECLARE @PageNumber AS INT, @RowspPage AS INT
SET @PageNumber = 2
SET @RowspPage = 10 
SELECT ID_EXAMPLE, NM_EXAMPLE, DT_CREATE
FROM TB_EXAMPLE
ORDER BY ID_EXAMPLE
OFFSET ((@PageNumber - 1) * @RowspPage) ROWS
FETCH NEXT @RowspPage ROWS ONLY;

TechNet: paginación de una consulta con SQL Server

Mohan
fuente
respuesta más precisa en esta prueba
Vikrant
17

MSDN: ROW_NUMBER (Transact-SQL)

Devuelve el número secuencial de una fila dentro de una partición de un conjunto de resultados, comenzando en 1 para la primera fila de cada partición.

El siguiente ejemplo devuelve filas con números del 50 al 60 inclusive en el orden de OrderDate.

WITH OrderedOrders AS
(
    SELECT
        ROW_NUMBER() OVER(ORDER BY FirstName DESC) AS RowNumber, 
        FirstName, LastName, ROUND(SalesYTD,2,1) AS "Sales YTD"
    FROM [dbo].[vSalesPerson]
) 
SELECT RowNumber, 
    FirstName, LastName, Sales YTD 
FROM OrderedOrders 
WHERE RowNumber > 50 AND RowNumber < 60;
  RowNumber FirstName    LastName               SalesYTD
  --- -----------  ---------------------- -----------------
  1   Linda        Mitchell               4251368.54
  2   Jae          Pak                    4116871.22
  3   Michael      Blythe                 3763178.17
  4   Jillian      Carson                 3189418.36
  5   Ranjit       Varkey Chudukatil      3121616.32
  6   José         Saraiva                2604540.71
  7   Shu          Ito                    2458535.61
  8   Tsvi         Reiter                 2315185.61
  9   Rachel       Valdez                 1827066.71
  10  Tete         Mensa-Annan            1576562.19
  11  David        Campbell               1573012.93
  12  Garrett      Vargas                 1453719.46
  13  Lynn         Tsoflias               1421810.92
  14  Pamela       Ansman-Wolfe           1352577.13
Dinesh Rabara
fuente
15

Hay una buena descripción general de las diferentes técnicas de paginación en http://www.codeproject.com/KB/aspnet/PagingLarge.aspx

He usado el método ROWCOUNT con bastante frecuencia principalmente con SQL Server 2000 (también funcionará con 2005 y 2008, solo mida el rendimiento en comparación con ROW_NUMBER), es muy rápido, pero debe asegurarse de que las columnas ordenadas tengan (principalmente ) valores únicos.

liggett78
fuente
1
Curiosamente, ese artículo no menciona el método de búsqueda , que es capaz de realizar paginación en tiempo constante ... Sigue siendo un buen artículo
Lukas Eder
6

Para SQL Server 2000, puede simular ROW_NUMBER () utilizando una variable de tabla con una columna IDENTITY:

DECLARE @pageNo int -- 1 based
DECLARE @pageSize int
SET @pageNo = 51
SET @pageSize = 20

DECLARE @firstRecord int
DECLARE @lastRecord int
SET @firstRecord = (@pageNo - 1) * @pageSize + 1 -- 1001
SET @lastRecord = @firstRecord + @pageSize - 1   -- 1020

DECLARE @orderedKeys TABLE (
  rownum int IDENTITY NOT NULL PRIMARY KEY CLUSTERED,
  TableKey int NOT NULL
)

SET ROWCOUNT @lastRecord
INSERT INTO @orderedKeys (TableKey) SELECT ID FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate

SET ROWCOUNT 0

SELECT t.*
FROM Orders t
  INNER JOIN @orderedKeys o ON o.TableKey = t.ID
WHERE o.rownum >= @firstRecord
ORDER BY o.rownum

Este enfoque puede extenderse a tablas con claves de varias columnas, y no incurre en la sobrecarga de rendimiento del uso de OR (que omite el uso del índice). La desventaja es la cantidad de espacio temporal utilizado si el conjunto de datos es muy grande y uno está cerca de la última página. No probé el rendimiento del cursor en ese caso, pero podría ser mejor.

Tenga en cuenta que este enfoque podría optimizarse para la primera página de datos. Además, se usó ROWCOUNT ya que TOP no acepta una variable en SQL Server 2000.

Thomas S. Trias
fuente
3

La mejor forma de paginación en SQL Server 2012 es mediante el uso de desplazamiento y recuperación a continuación en un procedimiento almacenado. Palabra clave OFFSET : si usamos offset con la cláusula order by, la consulta omitirá el número de registros que especificamos en OFFSET n Filas.

FETCH NEXT Palabras clave : cuando usamos Fetch Next con una orden por cláusula, solo devolverá el número de filas que desea mostrar en la paginación, sin Offset, SQL generará un error. Aquí está el ejemplo dado a continuación.

create procedure sp_paging
(
 @pageno as int,
 @records as int
)
as
begin
declare @offsetcount as int
set @offsetcount=(@pageno-1)*@records
select id,bs,variable from salary order by id offset @offsetcount rows fetch Next @records rows only
end

puedes ejecutarlo de la siguiente manera.

exec sp_paging 2,3
Debendra Dash
fuente
2

Estas son mis soluciones para paginar el resultado de la consulta en el lado del servidor SQL. estos enfoques son diferentes entre SQL Server 2008 y 2012. Además, he agregado el concepto de filtrado y orden por con una columna. Es muy eficiente cuando busca, filtra y ordena en su Gridview.

Antes de realizar la prueba, debe crear una tabla de muestra e insertar alguna fila en esta tabla: (en el mundo real, debe cambiar la cláusula Where considerando los campos de su tabla y tal vez tenga alguna unión y subconsulta en la parte principal de select)

Create Table VLT
(
    ID int IDentity(1,1),
    Name nvarchar(50),
    Tel Varchar(20)
)
GO


Insert INTO VLT
VALUES
    ('NAME' + Convert(varchar(10),@@identity),'FAMIL' + Convert(varchar(10),@@identity))
GO 500000

En todos estos ejemplos, quiero consultar 200 filas por página y estoy buscando la fila para el número de página 1200.

En SQL Server 2008, puede usar el concepto CTE. Por eso, he escrito dos tipos de consulta para SQL Server 2008+

- SQL Server 2008+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM
  (  
    SELECT 
      ROW_NUMBER() 
        OVER( ORDER BY 
                CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                      THEN VLT.ID END ASC,
                CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                      THEN VLT.ID END DESC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                      THEN VLT.Tel END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                      THEN VLT.Tel END ASC
         ) AS RowNum
      ,*  
    FROM VLT 
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1   
  ) AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
  AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum

GO

Y segunda solución con CTE en SQL Server 2008+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

;WITH
  Data_CTE
  AS
  (  
    SELECT 
      ROW_NUMBER() 
        OVER( ORDER BY 
                CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                      THEN VLT.ID END ASC,
                CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                      THEN VLT.ID END DESC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                      THEN VLT.Tel END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                      THEN VLT.Tel END ASC
         ) AS RowNum
      ,*  
    FROM VLT
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1     
  )

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM Data_CTE AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
  AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum

- SQL Server 2012+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

;WITH
  Data_CTE
  AS
  (  
    SELECT 
      *  
    FROM VLT
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1         
  )

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM Data_CTE AS Data
ORDER BY 
    CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
        THEN Data.ID END ASC,
    CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
        THEN Data.ID END DESC,
    CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
        THEN Data.Name END ASC,
    CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
        THEN Data.Name END ASC,
    CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
        THEN Data.Tel END ASC,
    CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
        THEN Data.Tel END ASC
OFFSET @PageSize * (@PageNumber - 1) ROWS FETCH NEXT @PageSize ROWS ONLY;
Ardalan Shahgholi
fuente
1

Prueba este enfoque:

SELECT TOP @offset a.*
FROM (select top @limit b.*, COUNT(*) OVER() totalrows 
        from TABLENAME b order by id asc) a
ORDER BY id desc;
fatlion
fuente
1

En el caso de uso, lo siguiente parece ser fácil de usar y rápido. Simplemente configure el número de página.

use AdventureWorks
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 6;
with result as(
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
)
select SalesOrderDetailID, SalesOrderID, ProductID from result
WHERE result.RowNum BETWEEN ((@PageNumber-1)*@RowsPerPage)+1
AND @RowsPerPage*(@PageNumber)

también sin CTE

use AdventureWorks
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 6
SELECT SalesOrderDetailID, SalesOrderID, ProductID
FROM (
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
 ) AS SOD
WHERE SOD.RowNum BETWEEN ((@PageNumber-1)*@RowsPerPage)+1
AND @RowsPerPage*(@PageNumber)
Trueno
fuente
1
¿Qué significa donde 1 = 1 señor?
Errol Paleracio
0

Bueno, he usado la siguiente consulta de muestra en mi base de datos SQL 2000, también funciona bien para SQL 2005. El poder que le da está ordenado dinámicamente mediante el uso de múltiples columnas. Te digo ... esto es poderoso :)

    ALTER PROCEDURE [dbo].[RE_ListingReports_SelectSummary] 

@CompanyID  int,
@pageNumber     int,
@pageSize   int, 
@sort       varchar(200)
AS

DECLARE @sql nvarchar(4000)
DECLARE @strPageSize nvarchar(20)
DECLARE @strSkippedRows nvarchar(20)
DECLARE @strFields nvarchar(4000)
DECLARE @strFilter nvarchar(4000)
DECLARE @sortBy nvarchar(4000)
DECLARE @strFrom nvarchar(4000)
DECLARE @strID nvarchar(100)

If(@pageNumber < 0)
  SET @pageNumber = 1
SET @strPageSize = CAST(@pageSize AS varchar(20)) 
SET @strSkippedRows = CAST(((@pageNumber - 1) * @pageSize) AS varchar(20))-- For    example if pageNumber is 5  pageSize is 10, then SkippedRows = 40.
SET @strID = 'ListingDbID'
SET @strFields = 'ListingDbID,
ListingID,  
[ExtraRoom]
'
SET @strFrom = ' vwListingSummary '

SET @strFilter = ' WHERE
        CompanyID = ' + CAST(@CompanyID As varchar(20)) 
End
SET @sortBy = ''
if(len(ltrim(rtrim(@sort))) > 0)
SET @sortBy = ' Order By ' + @sort

-- Total Rows Count

SET @sql =  'SELECT Count(' + @strID + ')  FROM ' + @strFROM + @strFilter
EXEC sp_executesql @sql

--// This technique is used in a Single Table pagination
SET @sql = 'SELECT ' + @strFields + ' FROM ' + @strFROM +
    ' WHERE ' + @strID +  ' IN ' + 
   '  (SELECT TOP ' + @strPageSize + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + 
             ' AND  ' + @strID + ' NOT IN ' + '
          (SELECT TOP ' + @strSkippedRows + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + @SortBy + ') ' 
   + @SortBy + ') ' + @SortBy
Print @sql 
EXEC sp_executesql @sql

La mejor parte es que sp_executesql almacena en caché las llamadas posteriores, siempre que pase los mismos parámetros, es decir, genere el mismo texto sql.

Jalal El-Shaer
fuente
0
   CREATE view vw_sppb_part_listsource as 
    select row_number() over (partition by sppb_part.init_id order by sppb_part.sppb_part_id asc ) as idx, * from (
      select 
          part.SPPB_PART_ID
          , 0 as is_rev
          , part.part_number 
          , part.init_id 
      from t_sppb_init_part part 
      left join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
      where prev.SPPB_PART_ID is null 
      union 
      select 
          part.SPPB_PART_ID
          , 1 as is_rev
          , prev.part_number 
          , part.init_id 
      from t_sppb_init_part part 
      inner join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
    ) sppb_part

reiniciará idx cuando se trata de init_id diferente

aden
fuente
0

Para la ROW_NUMBERtécnica, si no tiene una columna de clasificación para usar, puede usar lo CURRENT_TIMESTAMPsiguiente:

SELECT TOP 20 
    col1,
    col2,
    col3,
    col4
FROM (
    SELECT 
         tbl.col1 AS col1
        ,tbl.col2 AS col2
        ,tbl.col3 AS col3
        ,tbl.col4 AS col4
        ,ROW_NUMBER() OVER (
            ORDER BY CURRENT_TIMESTAMP
            ) AS sort_row
    FROM dbo.MyTable tbl
    ) AS query
WHERE query.sort_row > 10
ORDER BY query.sort_row

Esto me ha funcionado bien para búsquedas en tamaños de tabla de hasta 700,000.

Esto obtiene registros de 11 a 30.

usuario919426
fuente
Como buena práctica, con la paginación debe intentar ordenar por un conjunto único de columnas en el conjunto de resultados, ya que el orden no debe considerarse como garantizado.
Arin Taylor
2
Esto obtiene registros del 11 al 30.
Ardalan Shahgholi
0
create PROCEDURE SP_Company_List (@pagesize int = -1 ,@pageindex int= 0   ) > AS BEGIN  SET NOCOUNT ON;


    select  Id , NameEn     from Company  ORDER by Id ASC  
OFFSET (@pageindex-1 )* @pagesize   ROWS FETCH NEXt @pagesize ROWS ONLY END  GO

DECLARE   @return_value int

EXEC  @return_value = [dbo].[SP_Company_List]         @pagesize = 1 ,         > @pageindex = 2

SELECT    'Return Value' = @return_value

GO
salem albadawi
fuente
0

Este bit le brinda la capacidad de paginar usando SQL Server y las versiones más recientes de MySQL y lleva el número total de filas en cada fila. Utiliza tu clave de pimary para contar el número de filas únicas.

WITH T AS
(  
  SELECT TABLE_ID, ROW_NUMBER() OVER (ORDER BY TABLE_ID) AS RN
  , (SELECT COUNT(TABLE_ID) FROM TABLE) AS TOTAL 
  FROM TABLE (NOLOCK)
)

SELECT T2.FIELD1, T2.FIELD2, T2.FIELD3, T.TOTAL 
FROM TABLE T2 (NOLOCK)
INNER JOIN T ON T2.TABLE_ID=T.TABLE_ID
WHERE T.RN >= 100
AND T.RN < 200
Alex M
fuente
¿Puedes dejar algún comentario que explique lo que hace tu código?
Doug F
0

Este es un duplicado de la antigua pregunta SO 2012: forma eficiente de implementar la paginación

DESDE [TableX] ORDENAR POR [FieldX] DESPLAZAMIENTO 500 FILAS FETCH SIGUIENTE 100 FILAS SOLAMENTE

Aquí el tema se discute con mayor detalle y con enfoques alternativos.

d.popov
fuente
0

A partir de 2012 podemos usar OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY

Damitha
fuente
-19

No especificó el idioma ni qué controlador está utilizando. Por lo tanto, lo estoy describiendo de manera abstracta.

  • Cree un conjunto de resultados / conjunto de datos desplazable. Esto requería un primario en la (s) tabla (s)
  • saltar al final
  • solicitar el recuento de filas
  • ir al inicio de la página
  • desplazarse por las filas hasta el final de la página
Horrocrux7
fuente