Consulta eficiente (límite) en SQLServer 2000?
-
20-08-2019 - |
Pregunta
¿Cuál sería la forma más eficiente de hacer una consulta de paginación en SQLServer 2000?
Donde una " consulta de paginación " sería el equivalente a usar la instrucción LIMIT en MySQL.
EDITAR: ¿Podría un procedimiento almacenado ser más eficiente que cualquier consulta basada en conjunto en ese caso?
Solución
Paginación de grandes conjuntos de resultados y el ganador está utilizando RowCount. También hay una versión generalizada para consultas más complejas. Pero dale crédito a Jasmin Muharemovic :)
DECLARE @Sort /* the type of the sorting column */
SET ROWCOUNT @StartRow
SELECT @Sort = SortColumn FROM Table ORDER BY SortColumn
SET ROWCOUNT @PageSize
SELECT ... FROM Table WHERE SortColumn >= @Sort ORDER BY SortColumn
El artículo contiene el código fuente completo.
Lea la " Actualización 2004-05-05 " información. !
Otros consejos
Creo que una consulta SELECT TOP n
anidada es probablemente la forma más eficiente de lograrlo.
SELECT TOP ThisPageRecordCount *
FROM Table
WHERE ID NOT IN (SELECT TOP BeforeThisPageRecordCount ID FROM Table ORDER BY OrderingColumn)
ORDER BY OrderingColumn
Reemplace ThisPageRecordCount
con elementos por página y BeforeThisPageRecordCount
con (PageNumber - 1) * items-per-page
.
Por supuesto, la mejor manera en SQL Server 2005 es usar la función ROW_NUMBER()
en un CTE.
La eficiencia de la consulta realmente depende de cómo esté estructurada la tabla subyacente. Si, supongamos que tiene una clave principal llamada ID que es una IDENTIDAD, y es un índice agrupado, y puede asumir que nadie ha estado haciendo IDENTITY_INSERT en ella, puede hacerlo una consulta como:
SELECCIONE TOP XXX DE la tabla WHERE ID > @LastPagesID;
Eso te dará resultados lo más rápido posible. Todo lo demás que va a ser realmente eficiente es una variante de esto, tal vez no sea una identificación, tal vez lo que estás usando para la página es en realidad una fecha que sabes que es única, pero entiendes ... Las consultas basadas en IN () que se muestran aquí probablemente funcionarán, pero no afectarán el rendimiento de un escaneo de índice agrupado parcial o de cobertura.
Creo que lo que realmente tiene aquí es una razón convincente para actualizar a SQL 2005.
En SQL 2005, esto se puede hacer rápida y fácilmente con:
select ROW_NUMBER() over (order by [MyField]) as rowNum, *
from [MyTable]
where rowNum between @firstRow and @lastRow
Si realmente está atascado con SQL 2000, me preocuparía: Microsoft no lo va a soportar por mucho más tiempo dado que ahora hace dos generaciones.
No habrá una mejor manera de hacer esto, me temo: todas las soluciones son un poco piratas.
La respuesta de @Petar Petrov es probablemente la más consistente, sin embargo:
- Si está tratando con un índice agrupado en una tabla más pequeña para su ordenación, entonces el método ASC-DESC (construir dinámicamente dos clasificaciones en cada sentido usando TOP) probablemente sea más rápido.
- Si sus datos son relativamente estáticos y su ordenación es fija, puede agregar su propio campo rowNum que actualiza cuando cambia el orden de clasificación (suena horrible pero será rápido para tablas grandes).
Creo que estás viendo algunas horas ajustando con el analizador de consultas cada vez. Un proceso almacenado no hará mucha diferencia de ninguna manera: es probable que el almacenamiento en caché del plan de consulta no sea el cuello de botella.
Este es un procedimiento almacenado genérico de SQL Server 2000 que realizará la paginación en cualquier tabla. El procedimiento almacenado acepta el nombre de la tabla, las columnas a generar (por defecto todas las columnas de la tabla), una condición opcional WHERE, un orden de clasificación opcional, el número de página para recuperar y el número de filas por página.
CREATE PROCEDURE [dbo].[GetPage]
@pTableName VARCHAR(30),
@pColumns VARCHAR(200) = '*',
@pFilter VARCHAR(200) = '',
@pSort VARCHAR(200) = '',
@pPage INT = 1,
@pPageRows INT = 10
AS
SET NOCOUNT ON
DECLARE @vSQL VARCHAR(4000)
DECLARE @vTempTable VARCHAR(30)
DECLARE @vRowStart INT
DECLARE @vTotalRows INT
SET @vTempTable = '##Tmp' + CAST(DATEPART(YYYY, GETDATE()) AS VARCHAR(4)) +
CAST(DATEPART(MM, GETDATE()) AS VARCHAR(2)) +
CAST(DATEPART(DD, GETDATE()) AS VARCHAR(2)) +
CAST(DATEPART(HH, GETDATE()) AS VARCHAR(2)) +
CAST(DATEPART(MI, GETDATE()) AS VARCHAR(2)) +
CAST(DATEPART(SS, GETDATE()) AS VARCHAR(2)) +
CAST(DATEPART(MS, GETDATE()) AS VARCHAR(3))
SET @vSQL = 'SELECT ' + @pColumns + ', IDENTITY(INT, 1, 1) AS ROWID INTO ' + @vTempTable + ' FROM ' + @pTableName
IF @pFilter != '' AND @pFilter IS NOT NULL
SET @vSQL = @vSQL + ' WHERE ' + @pFilter
IF @pSort != '' AND @pSort IS NOT NULL
SET @vSQL = @vSQL + ' ORDER BY ' + @pSort
EXECUTE (@vSQL)
-- Get the total number of rows selected
SET @vTotalRows = @@ROWCOUNT
-- If page number = 0, set it to the first page
IF @pPage = 0
SET @pPage = 1
-- If page number is beyond the last page, set page to the last page
IF (@pPage * @pPageRows) > @vTotalRows
BEGIN
SET @pPage = @vTotalRows / @pPageRows
IF (@vTotalRows % @pPageRows) != 0
SET @pPage = @pPage + 1
END
SET @vRowStart = ((@pPage - 1) * @pPageRows) + 1
SET @vSQL = 'SELECT * FROM ' + @vTempTable + ' WHERE ROWID BETWEEN ' + CAST(@vRowStart AS VARCHAR(10)) +
' AND ' + CAST((@vRowStart + @pPageRows - 1) AS VARCHAR(10)) + ' ORDER BY ROWID'
EXECUTE (@vSQL)
SET @vSQL = 'DROP TABLE ' + @vTempTable
EXECUTE (@vSQL)
GO
Aquí hay algunos ejemplos sobre cómo usarlo usando la base de datos Northwing:
EXECUTE [dbo].[GetPage] 'Customers', '*', '', '', 1, 10
EXECUTE [dbo].[GetPage] 'Customers', '*', '', 'CustomerID DESC', 1, 10
Para confirmar, este no es mi trabajo pero es cortesía de http://www.eggheadcafe.com/PrintSearchContent.asp?LINKID=1055
Saludos, John