sql - ejemplos - forma eficiente de implementar paginación
paginacion sql server 2016 (9)
En 2008 no podemos usar Skip (). Take ()
El camino es:
var MinPageRank = (PageNumber - 1) * NumInPage + 1
var MaxPageRank = PageNumber * NumInPage
var visit = Visita.FromSql($"SELECT * FROM (SELECT [RANK] = ROW_NUMBER() OVER (ORDER BY Hora DESC),* FROM Visita WHERE ) A WHERE A.[RANK] BETWEEN {MinPageRank} AND {MaxPageRank}").ToList();
¿Debo usar los métodos Skip()
y Take()
LINQ para paginación, o implementar mi propia paginación con una consulta SQL?
¿Cuál es más eficiente? ¿Por qué elegiría uno sobre el otro?
Estoy usando SQL Server 2008, ASP.NET MVC y LINQ.
En SQL Server 2008:
DECLARE @PAGE INTEGER = 2
DECLARE @TAKE INTEGER = 50
SELECT [t1].*
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [t0].[COLUMNORDER] DESC) AS [ROW_NUMBER], [t0].*
FROM [dbo].[TABLA] AS [t0]
WHERE ([t0].[COLUMNS_CONDITIONS] = 1)
) AS [t1]
WHERE [t1].[ROW_NUMBER] BETWEEN ((@PAGE*@TAKE) - (@TAKE-1)) AND (@PAGE*@TAKE)
ORDER BY [t1].[ROW_NUMBER]
En t0, todos los registros En t1 son solo los que corresponden a esa página
Intenta usar
FROM [TableX]
ORDER BY [FieldX]
OFFSET 500 ROWS
FETCH NEXT 100 ROWS ONLY
para obtener las filas de 501 a 600 en el servidor SQL, sin cargarlas en la memoria. Tenga en cuenta que esta sintaxis está disponible solo con SQL Server 2012
LinqToSql convertirá automáticamente un .Skip (N1) .Take (N2) en la sintaxis de TSQL por usted. De hecho, cada "consulta" que haces en Linq, en realidad solo está creando una consulta SQL para ti en el fondo. Para probar esto, simplemente ejecute el Analizador de SQL mientras se ejecuta su aplicación.
La metodología de omisión / recepción me ha funcionado muy bien, y otras de lo que he leído.
Por curiosidad, ¿qué tipo de consulta de autopaginación tienes, que crees que es más eficiente que la omisión / toma de Linq?
Mientras que LINQ-to-SQL generará una cláusula OFFSET
(posiblemente emulada usando ROW_NUMBER() OVER()
como otros mencionaron ), existe una forma completamente diferente, mucho más rápida de realizar paginación en SQL. Esto a menudo se llama el "método de búsqueda" como se describe en esta publicación del blog aquí .
SELECT TOP 10 first_name, last_name, score
FROM players
WHERE (score < @previousScore)
OR (score = @previousScore AND player_id < @previousPlayerId)
ORDER BY score DESC, player_id DESC
Los valores @previousScore
y @previousPlayerId
son los valores respectivos del último registro de la página anterior. Esto le permite buscar la página "siguiente". Si la dirección ORDER BY
es ASC
, simplemente use >
lugar.
Con el método anterior, no puede pasar de inmediato a la página 4 sin haber buscado primero los 40 registros anteriores. Pero a menudo, no quieres ir tan lejos de todos modos. En cambio, obtiene una consulta mucho más rápida que podría obtener datos en tiempo constante, según su indexación. Además, sus páginas permanecen "estables", sin importar si los datos subyacentes cambian (por ejemplo, en la página 1, mientras se encuentra en la página 4).
Esta es la mejor manera de implementar la paginación cuando la carga es más lenta en las aplicaciones web, por ejemplo.
Tenga en cuenta que el "método de búsqueda" también se denomina paginación del conjunto de claves .
Puede implementar la paginación de esta manera simple pasando el PageIndex
Declare @PageIndex INT = 1
Declare @PageSize INT = 20
Select ROW_NUMBER() OVER ( ORDER BY Products.Name ASC ) AS RowNumber,
Products.ID,
Products.Name
into #Result
From Products
SELECT @RecordCount = COUNT(*) FROM #Results
SELECT *
FROM #Results
WHERE RowNumber
BETWEEN
(@PageIndex -1) * @PageSize + 1
AND
(((@PageIndex -1) * @PageSize + 1) + @PageSize) - 1
Tratando de darle una breve respuesta a su duda, si ejecuta los métodos de skip(n).take(m)
en linq (con SQL 2005/2008 como servidor de base de datos), su consulta utilizará la Select ROW_NUMBER() Over ...
.declaración, con es de alguna manera paginación directa en el motor SQL.
Dando un ejemplo, tengo una tabla db llamada mtcity
y escribí la siguiente consulta (también funciona con linq para las entidades):
using (DataClasses1DataContext c = new DataClasses1DataContext())
{
var query = (from MtCity2 c1 in c.MtCity2s
select c1).Skip(3).Take(3);
//Doing something with the query.
}
La consulta resultante será:
SELECT [t1].[CodCity],
[t1].[CodCountry],
[t1].[CodRegion],
[t1].[Name],
[t1].[Code]
FROM (
SELECT ROW_NUMBER() OVER (
ORDER BY [t0].[CodCity],
[t0].[CodCountry],
[t0].[CodRegion],
[t0].[Name],
[t0].[Code]) AS [ROW_NUMBER],
[t0].[CodCity],
[t0].[CodCountry],
[t0].[CodRegion],
[t0].[Name],
[t0].[Code]
FROM [dbo].[MtCity] AS [t0]
) AS [t1]
WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1
ORDER BY [t1].[ROW_NUMBER]
Lo cual es un acceso de datos en ventanas (muy bien, por cierto, devolverá datos desde el principio y accederá a la tabla siempre que se cumplan las condiciones). Esto será muy similar a:
With CityEntities As
(
Select ROW_NUMBER() Over (Order By CodCity) As Row,
CodCity //here is only accessed by the Index as CodCity is the primary
From dbo.mtcity
)
Select [t0].[CodCity],
[t0].[CodCountry],
[t0].[CodRegion],
[t0].[Name],
[t0].[Code]
From CityEntities c
Inner Join dbo.MtCity t0 on c.CodCity = t0.CodCity
Where c.Row Between @p0 + 1 AND @p0 + @p1
Order By c.Row Asc
Con la excepción de que, esta segunda consulta se ejecutará más rápido que el resultado de linq porque usará exclusivamente el índice para crear la ventana de acceso a datos; esto significa que, si necesita algún tipo de filtrado, el filtro debe estar (o debe estar) en la lista de Entidades (donde se crea la fila) y también se deben crear algunos índices para mantener el buen rendimiento.
Ahora, ¿qué es mejor?
Si tiene un flujo de trabajo bastante sólido en su lógica, implementar la forma SQL correcta será complicado. En ese caso, LINQ será la solución.
Si puede bajar esa parte de la lógica directamente a SQL (en un procedimiento almacenado), será aún mejor porque puede implementar la segunda consulta que le mostré (usando índices) y permitir que SQL genere y almacene el Plan de ejecución del consulta (mejorando el rendimiento).
Utilizamos un CTE envuelto en SQL dinámico (porque nuestra aplicación requiere clasificación dinámica del lado del servidor de datos) dentro de un procedimiento almacenado. Puedo proporcionar un ejemplo básico si lo desea.
No he tenido la oportunidad de ver el T / SQL que produce LINQ. ¿Alguien puede publicar una muestra?
No usamos LINQ o acceso directo a las tablas, ya que requerimos la capa adicional de seguridad (si el SQL dinámico lo rompe un tanto).
Algo como esto debería hacer el truco. Puede agregar valores parametrizados para parámetros, etc.
exec sp_executesql ''WITH MyCTE AS (
SELECT TOP (10) ROW_NUMBER () OVER '' + @SortingColumn + '' as RowID, Col1, Col2
FROM MyTable
WHERE Col4 = ''''Something''''
)
SELECT *
FROM MyCTE
WHERE RowID BETWEEN 10 and 20''
puedes mejorar aún más el rendimiento, chech this
From CityEntities c
Inner Join dbo.MtCity t0 on c.CodCity = t0.CodCity
Where c.Row Between @p0 + 1 AND @p0 + @p1
Order By c.Row Asc
si usa el de de esta manera, dará mejores resultados:
From dbo.MtCity t0
Inner Join CityEntities c on c.CodCity = t0.CodCity
razón: porque estás usando la clase where en la tabla CityEntities que eliminará muchos registros antes de unirte a MtCity, así que 100% seguro de que aumentará el rendimiento muchas veces ...
De todos modos, la respuesta de rodrigoelp es realmente útil.
Gracias