Sayfalama için LINQ'leri Skip()
ve Take()
yöntemini kullanmalı mıyım yoksa kendi sayfamı bir SQL sorgusuyla uygulamalı mıyım?
Hangisi en verimli? Neden birini diğerine tercih edeyim?
SQL Server 2008, ASP.NET MVC ve LINQ kullanıyorum.
Sayfalama için LINQ'leri Skip()
ve Take()
yöntemini kullanmalı mıyım yoksa kendi sayfamı bir SQL sorgusuyla uygulamalı mıyım?
Hangisi en verimli? Neden birini diğerine tercih edeyim?
SQL Server 2008, ASP.NET MVC ve LINQ kullanıyorum.
Yanıtlar:
Şüphenize kısa bir cevap vermeye çalışıyorum, eğer skip(n).take(m)
metotları linq üzerinde (veritabanı sunucusu olarak SQL 2005/2008 ile) çalıştırırsanız, sorgunuz Select ROW_NUMBER() Over ...
SQL motorunda bir şekilde doğrudan sayfalama ile ifadeyi kullanacaktır .
Size bir örnek vermek gerekirse, adında bir db tablom var mtcity
ve aşağıdaki sorguyu yazdım (linq to varlıklarla da çalışın):
using (DataClasses1DataContext c = new DataClasses1DataContext())
{
var query = (from MtCity2 c1 in c.MtCity2s
select c1).Skip(3).Take(3);
//Doing something with the query.
}
Ortaya çıkan sorgu şöyle olacaktır:
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]
Pencereli bir veri erişimi olan (oldukça havalı, btw cuz en başından beri verileri döndürür ve koşullar karşılandığı sürece tabloya erişir). Bu şuna çok benzer olacaktır:
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
Bu ikinci sorgunun linq sonucundan daha hızlı yürütülmesi istisnai olarak, veri erişim penceresini oluşturmak için yalnızca indeksi kullanacaktır; Bu, bazı filtrelemeye ihtiyacınız varsa, filtrelemenin Varlık listesinde (satırın oluşturulduğu yer) olması (veya olması gerekir) ve iyi performansı sürdürmek için bazı dizinlerin de oluşturulması gerektiği anlamına gelir.
Şimdi, daha iyi olan ne?
Mantığınızda oldukça sağlam bir iş akışınız varsa, doğru SQL yöntemini uygulamak karmaşık olacaktır. Bu durumda LINQ çözüm olacaktır.
Mantığın bu kısmını doğrudan SQL'e indirebilirseniz (bir saklı yordamda), daha da iyi olacaktır çünkü size gösterdiğim ikinci sorguyu (dizinleri kullanarak) uygulayabilir ve SQL'in Yürütme Planını oluşturmasına ve sorgu (performansı artırma).
Kullanmayı dene
FROM [TableX]
ORDER BY [FieldX]
OFFSET 500 ROWS
FETCH NEXT 100 ROWS ONLY
SQL sunucusunda 501'den 600'e kadar olan satırları belleğe yüklemeden almak için. Bu sözdiziminin yalnızca SQL Server 2012 ile kullanılabildiğini unutmayın
LINQ-to-SQL bir OFFSET
yan tümce oluşturacak olsa da (muhtemelen ROW_NUMBER() OVER()
başkalarının da bahsettiği gibi taklit edilmiştir ), SQL'de disk belleği gerçekleştirmenin tamamen farklı, çok daha hızlı bir yolu vardır. Bu blog yazısında açıklandığı gibi bu genellikle "arama yöntemi" olarak adlandırılır .
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
@previousScore
Ve @previousPlayerId
değerler önceki sayfadan son kaydın ilgili değerlerdir. Bu, "sonraki" sayfayı getirmenizi sağlar. Eğer ORDER BY
yön ise ASC
, sadece kullanım >
yerine.
Yukarıdaki yöntemle, önceki 40 kaydı almadan önce 4. sayfaya hemen atlayamazsınız. Ama çoğu zaman zaten o kadar uzağa atlamak istemezsiniz. Bunun yerine, indekslemenize bağlı olarak verileri sabit zamanda alabilen çok daha hızlı bir sorgu elde edersiniz. Ayrıca, verileriniz değişse de değişse de sayfalarınız "sabit" kalır (örn. 1. sayfada, 4. sayfadayken).
Bu, örneğin web uygulamalarında daha fazla veriyi tembel olarak yüklerken sayfalamayı uygulamanın en iyi yoludur.
Not, "arama yöntemi" aynı zamanda tuş takımı sayfalama olarak da adlandırılır .
LinqToSql otomatik olarak bir .Skip (N1) .Take (N2) 'yi sizin için TSQL sözdizimine dönüştürecektir. Aslında, Linq'te yaptığınız her "sorgu", aslında arka planda sizin için bir SQL sorgusu oluşturmaktır. Bunu test etmek için, uygulamanız çalışırken SQL Profiler'ı çalıştırın.
Atla / al metodolojisi benim ve okuduklarımdan diğerleri için çok iyi çalıştı.
Merak ettiğim için, Linq'in atlama / alma işleminden daha verimli olduğuna inandığınız ne tür kendi kendine sayfalama sorgunuz var?
Bir saklı yordam içinde Dinamik SQL'e sarılmış bir CTE kullanıyoruz (çünkü uygulamamız veri sunucusu tarafında dinamik sıralama gerektiriyor). İsterseniz basit bir örnek verebilirim.
LINQ'nun ürettiği T / SQL'e bakma şansım olmadı. Birisi bir örnek gönderebilir mi?
Ekstra güvenlik katmanına ihtiyaç duyduğumuz için LINQ veya tablolara doğrudan erişim kullanmıyoruz (dinamik SQL kesintileri bunu bir şekilde kabul etti).
Bunun gibi bir şey hile yapmalı. Parametreler, vb. İçin parametreli değerler ekleyebilirsiniz.
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'
sp_executesql
Güvenli bir şekilde, örneğin parametrelerini geçirmek olanağına sahip: EXECUTE sp_executesql 'WITH myCTE AS ... WHERE Col4=@p1) ...', '@p1 nvarchar(max)', @ValueForCol4
. Bu bağlamda güvenli, SQL enjeksiyonuna karşı sağlam olduğu anlamına gelir - değişken içindeki her olası değeri @ValueForCol4
bile geçirebilirsiniz - '--'
ve sorgu çalışmaya devam eder!
SELECT ROW_NUMBER() OVER (ORDER BY CASE WHEN @CampoId = 1 THEN Id WHEN @CampoId = 2 THEN field2 END)
ROW_NUMBER() OVER()
ofset öykünmesinden çok daha hızlı olabilir . Ayrıca bakınız: 4guysfromrolla.com/webtech/042606-1.shtml
SQL Server 2008'de:
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]
T0'daki tüm kayıtlar t1'deki sadece o sayfaya karşılık gelenlerdir
Benim verdiğim yaklaşım, SQL sunucusunun ulaşabileceği en hızlı sayfalandırmadır. Bunu 5 milyon kayıtta test ettim. Bu yaklaşım, SQL Server tarafından sağlanan "YALNIZCA SONRAKİ 10 SIRA KAYDIRIN" dan çok daha iyidir.
-- The below given code computes the page numbers and the max row of previous page
-- Replace <<>> with the correct table data.
-- Eg. <<IdentityColumn of Table>> can be EmployeeId and <<Table>> will be dbo.Employees
DECLARE @PageNumber int=1; --1st/2nd/nth page. In stored proc take this as input param.
DECLARE @NoOfRecordsPerPage int=1000;
DECLARE @PageDetails TABLE
(
<<IdentityColumn of Table>> int,
rownum int,
[PageNumber] int
)
INSERT INTO @PageDetails values(0, 0, 0)
;WITH CTE AS
(
SELECT <<IdentityColumn of Table>>, ROW_NUMBER() OVER(ORDER BY <<IdentityColumn of Table>>) rownum FROM <<Table>>
)
Insert into @PageDetails
SELECT <<IdentityColumn of Table>>, CTE.rownum, ROW_NUMBER() OVER (ORDER BY rownum) as [PageNumber] FROM CTE WHERE CTE.rownum%@NoOfRecordsPerPage=0
--SELECT * FROM @PageDetails
-- Actual pagination
SELECT TOP (@NoOfRecordsPerPage)
FROM <<Table>> AS <<Table>>
WHERE <<IdentityColumn of Table>> > (SELECT <<IdentityColumn of Table>> FROM
@PageDetails WHERE PageNumber=@PageNumber)
ORDER BY <<Identity Column of Table>>
performansı daha da artırabilirsiniz, bunu kontrol edin
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
from öğesini bu şekilde kullanırsanız daha iyi sonuç verecektir:
From dbo.MtCity t0
Inner Join CityEntities c on c.CodCity = t0.CodCity
nedeni: çünkü, CityEntities tablosunda MtCity'ye katılmadan önce birçok rekoru ortadan kaldıracak olan where sınıfını kullanıyorsunuz, bu yüzden% 100 eminim performansı kat kat artıracak ...
Neyse, rodrigoelp'in cevabı gerçekten yardımcı oldu.
Teşekkürler
@p0
ve daha spesifik olarak @p1
nereden geldiğinden emin değilim
PageIndex'i geçerek sayfalamayı bu basit şekilde uygulayabilirsiniz.
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
2008'de Skip () kullanamıyoruz. Take ()
Yol şudur:
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();