2008-10-09 22 views
120

¿Hay alguna forma en SQL Server para obtener los resultados comenzando en un desplazamiento dado? Por ejemplo, en otro tipo de base de datos SQL, es posible hacer:Desplazamiento de fila en SQL Server

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 interesan? ¡Gracias!

Respuesta

143

Me gustaría evitar el uso de SELECT *. Especifique las columnas que realmente quiere aunque puedan ser todas.

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

Efficiently Paging Through Large Result Sets in SQL Server 2000

A More Efficient Method for Paging Through Large Result Sets

+5

¿Por qué sugieres evitar SELECT incluso si estás seleccionando todas las columnas? –

+8

Estoy seguro de que usó "*" porque era más simple escribir y entendió mejor que "col1, col2, ... colN" – rotard

+6

En cuanto a por qué no usarlo, 'SELECT *' significa que si La estructura de la tabla cambia, su consulta aún se ejecuta, pero arroja resultados diferentes. Si se agrega una columna, esto podría ser útil (aunque todavía debe usarla por su nombre en alguna parte); si se borra o se renombra una columna, es mejor que su SQL se rompa visiblemente que el código más abajo se comporte de manera extraña porque una variable no está inicializada. – IMSoP

2

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

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

donde 'campo' es la clave.

+4

Esa solución para SQL2000 no funciona para la última página en el conjunto de resultados, a menos que el número total de filas sea un múltiplo del tamaño de la página. –

21

Ésta 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 esto es otra manera (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 
+0

Solo para aclarar en 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

+5

Esa solución para SQL2000 no funciona para la última página en el conjunto de resultados, a menos que el número total de filas sea un múltiplo del tamaño de la página. –

10

Puede utilizar ROW_NUMBER() función para obtener lo que quiere:

SELECT * 
FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t 
WHERE RowNr BETWEEN 10 AND 20 
1

En SQLServer2005 se 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) 
+0

¿No debería ser '@Offset + @Limit - 1'? Si @Limit es 10, esto devolverá 11 filas. –

+0

Estás en lo correcto. Editaré mi respuesta para reflejar tu punto. – Aheho

2

Usted debe tener cuidado cuando se utiliza el row_number() OVER declaración (ORDER BY) como performane es bastante pobre. Lo mismo ocurre con el uso de Common Table Expressions con row_number() que 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 = @[email protected] 
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 
+0

Esto devolverá 11 filas, no 10. –

88

Si va a procesar todas las páginas en orden a continuación, simplemente recordando el último valor de la clave visto en la página anterior y usando TOP (25) ... WHERE Key > @last_key ORDER BY Key puede ser el mejor método rendimiento si existen índices adecuados para permitir que esto ser buscado de manera eficiente - o an API cursor si no lo hacen.

Para seleccionar una página arbitraria la mejor solución para SQL Server 2005 a 2008 R2 es probablemente ROW_NUMBER y BETWEEN

Para SQL Server 2012+ puede utilizar 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 it remains to be seen how well performing this option will be.

+2

Ahora está disponible en SQL Server Compact 4.0 -> http://msdn.microsoft.com/en-us/library/gg699618(v=sql.110).aspx –

+12

Es sobre el tiempo lo agregaron a tSQL – JohnFx

+3

Solo para Sql Server 2012 :( –

0

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

Usando el SET ROWCOUNT [OFFSET+LIMIT] puede limitar los resultados, y con los cursores, vaya directamente a la fila que desee, luego repita el ciclo hasta el final.

Por lo que su consulta sería la siguiente:

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 
+0

Odiaría ver el rendimiento de esto cuando llegue al final de la tabla ... –

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 mucho mejor el rendimiento en tablas con datos grandes como BLOB, porque la función ROW_NUMBER solo tiene que mirar a través de una columna, y en Las filas coincidentes se devuelven con todas las columnas.

1

Utilizo esta técnica para la paginación. No busco todas las filas. Por ejemplo, si mi página necesita mostrar las 100 filas superiores, solo obtengo la cláusula 100 with where. El resultado del SQL debe tener una clave única.

La tabla tiene el siguiente:

ID, KeyId, Rank 

El mismo rango será asignado para más de un keyid.

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

Por primera vez que paso por 0 para ambos. La segunda vez que pase 1 & 14. tercio pase tiempo 2 y 6 ....

El valor del décimo registro Rango & Id se pasa a la siguiente

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

Esto tendrá el menos presión sobre el el sistema

3

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;)

5

Hay OFFSET .. FETCH en SQL Server 2012, pero tendrá que especificar una columna ORDER BY.

Si realmente no tiene ninguna columna explícita que se podía pasar por una columna de ORDER BY (como han sugerido otros), entonces usted puede utilizar 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 estamos usando en jOOQ cuando los usuarios no especifican explícitamente un pedido. Esto producirá pedidos bastante aleatorios sin ningún costo adicional.

4
SELECT TOP 75 * FROM MyTable 
EXCEPT 
SELECT TOP 50 * FROM MyTable 
+1

Esta es la respuesta más simple ... – Mai

+2

Eso es lo que estaba buscando. –

+0

El rendimiento no parece óptimo, ya que la consulta se ejecuta innecesariamente dos veces. Especialmente cuando el usuario va a las páginas superiores, la consulta para descartar filas, es decir, la parte siguiente EXCEPTO tardará más y más. – vanval

2

se mostrará los siguientes 25 registros sin incluir las primeras 50 registros funciona en SQL Server 2012.

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

puede reemplazar ID como su requisito

+0

Pls también , agregue que esto es posible en SQL SERVER 2012 –

0

mejor manera de hacerlo sin perder tiempo a la orden registros es el siguiente:

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

¡demora menos de un segundo!
la mejor solución para mesas grandes.

Cuestiones relacionadas