Compensación de fila en SQL Server

133

¿Hay alguna manera en SQL Server para obtener los resultados a partir de un desplazamiento determinado? Por ejemplo, en otro tipo de base de datos SQL, es posible hacer:

SELECT * FROM MyTable OFFSET 50 LIMIT 25

para obtener resultados 51-75. Esta construcción no parece existir en SQL Server.

¿Cómo puedo lograr esto sin cargar todas las filas que no me importan? ¡Gracias!

Alex
fuente
Puede usar offset y recuperar la siguiente declaración. youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Respuestas:

152

Evitaría usar SELECT *. Especifique las columnas que realmente desea, aunque pueden ser todas ellas.

SQL Server 2005+

SELECT col1, col2 
FROM (
    SELECT col1, col2, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum
    FROM MyTable
) AS MyDerivedTable
WHERE MyDerivedTable.RowNum BETWEEN @startRow AND @endRow

SQL Server 2000

Paginación eficiente a través de grandes conjuntos de resultados en SQL Server 2000

Un método más eficiente para paginar a través de grandes conjuntos de resultados

Brian Kim
fuente
66
¿Por qué sugiere evitar SELECCIONAR incluso si selecciona todas las columnas?
Adam Ness
12
Estoy seguro de que usó "*" porque era más simple de escribir y entendió mejor que "col1, col2, ... colN"
gillonba
9
En cuanto a por qué no usarlo, SELECT *significa que si la estructura de la tabla cambia, su consulta aún se ejecuta, pero da resultados diferentes. Si se agrega una columna, esto podría ser útil (aunque todavía tiene que usarla por su nombre en alguna parte); Si una columna se elimina o cambia de nombre, es mejor que su SQL se rompa visiblemente que el código que se encuentra más abajo comportándose de manera extraña porque una variable no está inicializada.
IMSoP
55
seleccionar todos los datos de la tabla y cortar? si tiene 5000000000 filas? seleccione 5000000000 filas y corte para cada consulta? No es eficiente para la CPU y la memoria del servidor.
e-info128
3
Tenga en cuenta que 2012+ lo ha implementado mucho mejor. Ver respuesta de + Martin Smith
meridius
100

Si va a procesar todas las páginas en orden, simplemente recordar el último valor clave visto en la página anterior y usarlo TOP (25) ... WHERE Key > @last_key ORDER BY Keypuede ser el mejor método si existen índices adecuados para permitir que esto se busque de manera eficiente, o un cursor API si no .

Para seleccionar una página arbitraria, la mejor solución para SQL Server 2005 - 2008 R2 es probablemente ROW_NUMBER yBETWEEN

Para SQL Server 2012+, puede usar la cláusula ORDER BY mejorada para esta necesidad.

SELECT  *
FROM     MyTable 
ORDER BY OrderingColumn ASC 
OFFSET  50 ROWS 
FETCH NEXT 25 ROWS ONLY 

Aunque queda por ver qué tan bien será el desempeño de esta opción .

Martin Smith
fuente
2
Ahora está disponible en SQL Server Compact 4.0 -> msdn.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
Bart Verkoeijen
13
Ya es hora de que agreguen esto a tSQL
JohnFx
3
Solo para SQL Server 2012 :(
e-info128
22

Esta es una forma (SQL2000)

SELECT * FROM
(
    SELECT TOP (@pageSize) * FROM
    (
        SELECT TOP (@pageNumber * @pageSize) *
        FROM tableName 
        ORDER BY columnName ASC
    ) AS t1 
    ORDER BY columnName DESC
) AS t2 
ORDER BY columnName ASC

y esta es otra forma (SQL 2005)

;WITH results AS (
    SELECT 
        rowNo = ROW_NUMBER() OVER( ORDER BY columnName ASC )
        , *
    FROM tableName 
) 
SELECT * 
FROM results
WHERE rowNo between (@pageNumber-1)*@pageSize+1 and @pageNumber*@pageSize
leoinfo
fuente
Solo para aclarar sobre el primero ... (@pageSize) es un marcador de posición aquí para el valor real. Tendrás que hacer 'TOP 25' específicamente; SQL Server 2000 no admite variables en una cláusula TOP. Esto hace que sea un dolor que involucra SQL dinámico.
Cowan
55
Esa solución para SQL2000 no funciona para la última página del conjunto de resultados, a menos que el número total de filas sea un múltiplo del tamaño de la página.
Bill Karwin
10

Puede usar la ROW_NUMBER()función para obtener lo que desea:

SELECT *
FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t
WHERE RowNr BETWEEN 10 AND 20
Matthias Meid
fuente
7

Existe OFFSET .. FETCHen SQL Server 2012, pero deberá especificar una ORDER BYcolumna.

Si realmente no tiene ninguna columna explícita que pueda pasar como ORDER BYcolumna (como lo han sugerido otros), puede usar este truco:

SELECT * FROM MyTable 
ORDER BY @@VERSION 
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

... o

SELECT * FROM MyTable 
ORDER BY (SELECT 0)
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

Lo usamos en jOOQ cuando los usuarios no especifican explícitamente un pedido. Esto producirá un pedido bastante aleatorio sin ningún costo adicional.

Lukas Eder
fuente
6

Para las tablas con más y grandes columnas de datos, prefiero:

SELECT 
  tablename.col1,
  tablename.col2,
  tablename.col3,
  ...
FROM
(
  (
    SELECT
      col1
    FROM 
    (
      SELECT col1, ROW_NUMBER() OVER (ORDER BY col1 ASC) AS RowNum
      FROM tablename
      WHERE ([CONDITION])
    )
    AS T1 WHERE T1.RowNum BETWEEN [OFFSET] AND [OFFSET + LIMIT]
  )
  AS T2 INNER JOIN tablename ON T2.col1=tablename.col1
);

-

[CONDITION] can contain any WHERE clause for searching.
[OFFSET] specifies the start,
[LIMIT] the maximum results.

Tiene un rendimiento mucho mejor en tablas con datos grandes como BLOB, porque la función ROW_NUMBER solo tiene que mirar a través de una columna, y solo las filas coincidentes se devuelven con todas las columnas.

Arthur van Dijk
fuente
5

Ver mi selección para paginador

SELECT TOP @limit * FROM (
   SELECT ROW_NUMBER() OVER (ORDER BY colunx ASC) offset, * FROM (

     -- YOU SELECT HERE
     SELECT * FROM mytable


   ) myquery
) paginator
WHERE offset > @offset

Esto resuelve la paginación;)

PerfectLion
fuente
3
SELECT TOP 75 * FROM MyTable
EXCEPT 
SELECT TOP 50 * FROM MyTable
Jithin Shaji
fuente
El rendimiento inteligente no parece óptimo, ya que la consulta se ejecuta innecesariamente dos veces. Especialmente a medida que el usuario va a las páginas superiores, la consulta para descartar filas, es decir, la parte a continuación, EXCEPTO, tomará más y más tiempo.
vanval
2

Dependiendo de su versión, no puede hacerlo directamente, pero podría hacer algo hacky como

select top 25 *
from ( 
  select top 75 *
  from   table 
  order by field asc
) a 
order by field desc 

donde 'campo' es la clave.

Sin cortar
fuente
44
Esa solución para SQL2000 no funciona para la última página del conjunto de resultados, a menos que el número total de filas sea un múltiplo del tamaño de la página.
Bill Karwin
2

A continuación se mostrarán 25 registros, excluyendo los primeros 50 registros que funcionan en SQL Server 2012.

SELECT * FROM MyTable ORDER BY ID OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;

puede reemplazar la identificación como su requisito

Shb
fuente
Por favor, agregue esto es posible en SQL SERVER 2012
Usman Younas
2

Debe tener cuidado al usar la ROW_NUMBER() OVER (ORDER BY)declaración ya que el rendimiento es bastante pobre. Lo mismo ocurre con el uso de expresiones de tabla comunes con ROW_NUMBER()eso es aún peor. Estoy usando el siguiente fragmento que ha demostrado ser un poco más rápido que usar una variable de tabla con una identidad para proporcionar el número de página.

DECLARE @Offset INT = 120000
DECLARE @Limit INT = 10

DECLARE @ROWCOUNT INT = @Offset+@Limit
SET ROWCOUNT @ROWCOUNT

SELECT * FROM MyTable INTO #ResultSet
WHERE MyTable.Type = 1

SELECT * FROM
(
    SELECT *, ROW_NUMBER() OVER(ORDER BY SortConst ASC) As RowNumber FROM
    (
        SELECT *, 1 As SortConst FROM #ResultSet
    ) AS ResultSet
) AS Page
WHERE RowNumber BETWEEN @Offset AND @ROWCOUNT

DROP TABLE #ResultSet
Patrik Melander
fuente
Esto devolverá 11 filas, no 10.
Aaron Bertrand
1

Yo uso esta técnica para la paginación. No busco todas las filas. Por ejemplo, si mi página necesita mostrar las 100 filas superiores, busco solo las 100 con la cláusula where. La salida del SQL debe tener una clave única.

La tabla tiene lo siguiente:

ID, KeyId, Rank

Se asignará el mismo rango para más de un KeyId.

SQL es select top 2 * from Table1 where Rank >= @Rank and ID > @Id

Por primera vez paso 0 para ambos. La segunda vez pasa 1 y 14. 3ra vez pasa 2 y 6 ...

El valor del décimo registro Rango e Id se pasa al siguiente

11  21  1
14  22  1
7   11  1
6   19  2
12  31  2
13  18  2

Esto tendrá el menor estrés en el sistema

Ravi Ramaswamy
fuente
1

En SqlServer2005 puede hacer lo siguiente:

DECLARE @Limit INT
DECLARE @Offset INT
SET @Offset = 120000
SET @Limit = 10

SELECT 
    * 
FROM
(
   SELECT 
       row_number() 
   OVER 
      (ORDER BY column) AS rownum, column2, column3, .... columnX
   FROM   
     table
) AS A
WHERE 
 A.rownum BETWEEN (@Offset) AND (@Offset + @Limit-1) 
Aheho
fuente
¿No debería ser @Offset + @Limit - 1? Si @Limit es 10, esto devolverá 11 filas.
Aaron Bertrand
1

La mejor manera de hacerlo sin perder tiempo para ordenar registros es así:

select 0 as tmp,Column1 from Table1 Order by tmp OFFSET 5000000 ROWS FETCH NEXT 50 ROWS ONLY

¡Toma menos de un segundo!
La mejor solución para mesas grandes.

Pishgaman.org
fuente
0

He estado buscando esta respuesta durante un tiempo (para consultas genéricas) y descubrí otra forma de hacerlo en SQL Server 2000+ usando ROWCOUNT y cursores y sin TOP ni ninguna tabla temporal.

Utilizando la SET ROWCOUNT [OFFSET+LIMIT] puede limitar los resultados, y con los cursores, vaya directamente a la fila que desee, luego haga un bucle hasta el final.

Entonces su consulta sería así:

SET ROWCOUNT 75 -- (50 + 25)
DECLARE MyCursor SCROLL CURSOR FOR SELECT * FROM pessoas
OPEN MyCursor
FETCH ABSOLUTE 50 FROM MyCursor -- OFFSET
WHILE @@FETCH_STATUS = 0 BEGIN
    FETCH next FROM MyCursor
END
CLOSE MyCursor
DEALLOCATE MyCursor
SET ROWCOUNT 0
Capilé
fuente
Odiaría ver el rendimiento de esto cuando llegues al final de la mesa ...
Aaron Bertrand
0

Con SQL Server 2012 (11.x) y posterior y Azure SQL Database, también puede tener "fetch_row_count_expression", también puede tener la cláusula ORDER BY junto con esto.

USE AdventureWorks2012;  
GO  
-- Specifying variables for OFFSET and FETCH values    
DECLARE @skip int = 0  , @take int = 8;  
SELECT DepartmentID, Name, GroupName  
FROM HumanResources.Department  
ORDER BY DepartmentID ASC   
    OFFSET @skip ROWS   
    FETCH NEXT @take ROWS ONLY; 

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

Nota DESPLAZAMIENTO Especifica el número de filas a omitir antes de que comience a devolver filas desde la expresión de consulta. NO es el número de la fila inicial. Entonces, tiene que ser 0 para incluir el primer registro.

Tejasvi Hegde
fuente