ฉันควรใช้ LINQ Skip()และTake()เมธอดในการเพจหรือใช้เพจจิ้งของตัวเองกับคิวรี SQL?
ข้อใดมีประสิทธิภาพสูงสุด ทำไมฉันถึงเลือกอย่างใดอย่างหนึ่ง
ฉันใช้ SQL Server 2008, ASP.NET MVC และ LINQ
ฉันควรใช้ LINQ Skip()และTake()เมธอดในการเพจหรือใช้เพจจิ้งของตัวเองกับคิวรี SQL?
ข้อใดมีประสิทธิภาพสูงสุด ทำไมฉันถึงเลือกอย่างใดอย่างหนึ่ง
ฉันใช้ SQL Server 2008, ASP.NET MVC และ LINQ
คำตอบ:
พยายามให้คำตอบสั้น ๆ สำหรับข้อสงสัยของคุณหากคุณดำเนินการskip(n).take(m)เมธอดบน linq (โดยใช้ SQL 2005/2008 เป็นเซิร์ฟเวอร์ฐานข้อมูล) แบบสอบถามของคุณจะใช้Select ROW_NUMBER() Over ...คำสั่งโดยมีการเพจโดยตรงในเอ็นจิ้น SQL
ตัวอย่างเช่นฉันมีตาราง db ที่เรียกว่าmtcityและฉันเขียนแบบสอบถามต่อไปนี้ (ทำงานร่วมกับ linq กับเอนทิตี):
using (DataClasses1DataContext c = new DataClasses1DataContext())
{
    var query = (from MtCity2 c1 in c.MtCity2s
                select c1).Skip(3).Take(3);
    //Doing something with the query.
}แบบสอบถามผลลัพธ์จะเป็น:
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]ซึ่งเป็นการเข้าถึงข้อมูลที่มีหน้าต่าง (ค่อนข้างดี btw เพราะจะส่งคืนข้อมูลตั้งแต่เริ่มต้นและจะเข้าถึงตารางตราบเท่าที่ตรงตามเงื่อนไข) สิ่งนี้จะคล้ายกับ:
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มีข้อยกเว้นว่าแบบสอบถามที่สองนี้จะดำเนินการเร็วกว่าผลลัพธ์ของ linq เนื่องจากจะใช้เฉพาะดัชนีเพื่อสร้างหน้าต่างการเข้าถึงข้อมูล ซึ่งหมายความว่าหากคุณต้องการการกรองการกรองควรเป็น (หรือต้อง) ในรายการเอนทิตี (ที่สร้างแถว) และควรสร้างดัชนีบางรายการด้วยเพื่อให้ได้ประสิทธิภาพที่ดี
ตอนนี้อะไรดีขึ้น?
หากคุณมีขั้นตอนการทำงานที่ค่อนข้างมั่นคงในตรรกะของคุณการใช้วิธี SQL ที่เหมาะสมจะมีความซับซ้อน ในกรณีนี้ LINQ จะเป็นทางออก
หากคุณสามารถลดส่วนนั้นของตรรกะลงใน SQL ได้โดยตรง (ในขั้นตอนที่เก็บไว้) จะดียิ่งขึ้นเพราะคุณสามารถใช้แบบสอบถามที่สองที่ฉันแสดงให้คุณเห็น (โดยใช้ดัชนี) และอนุญาตให้ SQL สร้างและจัดเก็บแผนการดำเนินการของ แบบสอบถาม (ปรับปรุงประสิทธิภาพ)
ลองใช้
FROM [TableX]
ORDER BY [FieldX]
OFFSET 500 ROWS
FETCH NEXT 100 ROWS ONLYเพื่อรับแถวจาก 501 ถึง 600 ในเซิร์ฟเวอร์ SQL โดยไม่ต้องโหลดในหน่วยความจำ โปรดทราบว่าไวยากรณ์นี้สามารถใช้ได้กับSQL Server 2012เท่านั้น
ในขณะที่ LINQ-to-SQL จะสร้างOFFSETอนุประโยค (อาจจำลองโดยใช้ROW_NUMBER() OVER() ตามที่คนอื่นกล่าวถึง ) แต่ก็มีวิธีที่แตกต่างกันโดยสิ้นเชิงและเร็วกว่ามากในการดำเนินการเพจใน SQL นี้มักจะถูกเรียกว่า "แสวงหาวิธีการ" ที่อธิบายไว้ในบล็อกโพสต์ที่นี่
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และ@previousPlayerIdค่าเป็นค่าตามลำดับของระเบียนสุดท้ายจากหน้าก่อนหน้านี้ ซึ่งช่วยให้คุณสามารถดึงข้อมูลหน้า "ถัดไป" หากORDER BYทิศทางเป็นASCเพียงใช้>แทน
ด้วยวิธีการข้างต้นคุณจะไม่สามารถข้ามไปยังหน้าที่ 4 ได้ทันทีโดยไม่ต้องดึงข้อมูล 40 ระเบียนก่อนหน้านี้ก่อน แต่บ่อยครั้งคุณไม่ต้องการที่จะกระโดดไปไกลขนาดนั้น แต่คุณจะได้รับคำค้นหาที่เร็วกว่ามากซึ่งอาจดึงข้อมูลได้ในเวลาคงที่ขึ้นอยู่กับการจัดทำดัชนีของคุณ นอกจากนี้หน้าเว็บของคุณยังคง "คงที่" ไม่ว่าข้อมูลพื้นฐานจะเปลี่ยนไปหรือไม่ (เช่นในหน้าที่ 1 ขณะที่คุณอยู่ในหน้าที่ 4)
นี่เป็นวิธีที่ดีที่สุดในการใช้เพจเมื่อขี้เกียจโหลดข้อมูลเพิ่มเติมในเว็บแอปพลิเคชันเป็นต้น
LinqToSql จะแปลงไฟล์. Skip (N1) โดยอัตโนมัติใช้ (N2) เป็นไวยากรณ์ TSQL ให้คุณ ในความเป็นจริงทุก "แบบสอบถาม" ที่คุณทำใน Linq เป็นเพียงการสร้างแบบสอบถาม SQL สำหรับคุณในพื้นหลัง ในการทดสอบสิ่งนี้เพียงเรียกใช้ SQL Profiler ในขณะที่แอปพลิเคชันของคุณกำลังทำงาน
วิธีการข้าม / ใช้เวลาได้ผลดีสำหรับฉันและอื่น ๆ จากสิ่งที่ฉันอ่าน
ด้วยความอยากรู้คุณมีแบบสอบถามการเพจด้วยตนเองประเภทใดที่คุณเชื่อว่ามีประสิทธิภาพมากกว่าการข้าม / รับของ Linq
เราใช้ CTE ที่รวมอยู่ใน Dynamic SQL (เนื่องจากแอปพลิเคชันของเราต้องการการเรียงลำดับแบบไดนามิกของฝั่งเซิร์ฟเวอร์ข้อมูล) ภายในกระบวนงานที่จัดเก็บ ฉันสามารถให้ตัวอย่างพื้นฐานได้หากคุณต้องการ
ฉันไม่ได้มีโอกาสดู T / SQL ที่ LINQ สร้างขึ้น มีใครโพสตัวอย่างได้ไหม
เราไม่ใช้ LINQ หรือการเข้าถึงตารางโดยตรงเนื่องจากเราต้องการความปลอดภัยชั้นพิเศษ (ให้ไดนามิก SQL แบ่งสิ่งนี้บ้าง)
สิ่งนี้ควรทำเคล็ดลับ คุณสามารถเพิ่มค่าพารามิเตอร์สำหรับพารามิเตอร์ ฯลฯ
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 EXECUTE sp_executesql 'WITH myCTE AS ... WHERE Col4=@p1) ...', '@p1 nvarchar(max)', @ValueForCol4การรักษาความปลอดภัยในบริบทนี้หมายความว่ามีประสิทธิภาพเมื่อเทียบกับการแทรก SQL - คุณสามารถส่งผ่านค่าที่เป็นไปได้ทุกค่าภายในตัวแปร@ValueForCol4- แม้กระทั่ง'--'การสืบค้นจะยังคงใช้งานได้!
                    SELECT ROW_NUMBER() OVER (ORDER BY CASE  WHEN @CampoId = 1 THEN Id WHEN @CampoId = 2 THEN field2 END)
                    ROW_NUMBER() OVER()จำลองออฟเซ็ตมาก ดูเพิ่มเติมที่: 4guysfromrolla.com/webtech/042606-1.shtml
                    ใน 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]ใน t0 คือระเบียนทั้งหมดใน t1 เป็นเฉพาะระเบียนที่ตรงกับหน้านั้น
แนวทางที่ฉันให้คือการแบ่งหน้าเร็วที่สุดที่เซิร์ฟเวอร์ SQL สามารถทำได้ ฉันได้ทดสอบสิ่งนี้กับ 5 ล้านรายการ แนวทางนี้ดีกว่า "OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY" ที่จัดทำโดย SQL Server
-- 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>>คุณสามารถปรับปรุงประสิทธิภาพได้มากขึ้นตรวจสอบสิ่งนี้
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 ในลักษณะนี้จะให้ผลลัพธ์ที่ดีกว่า:
From   dbo.MtCity  t0
   Inner Join  CityEntities c on c.CodCity = t0.CodCityเหตุผล: เนื่องจากคุณใช้คลาส where บนตาราง CityEntities ซึ่งจะกำจัดบันทึกจำนวนมากก่อนเข้าร่วม MtCity ดังนั้นมั่นใจได้ 100% ว่าจะเพิ่มประสิทธิภาพได้หลายเท่า ...
อย่างไรก็ตามคำตอบโดย rodrigoelp เป็นประโยชน์จริงๆ
ขอบคุณ
@p0และเฉพาะเจาะจงมากขึ้น@p1
                    คุณสามารถใช้เพจจิ้งได้ด้วยวิธีง่ายๆนี้โดยส่ง 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ในปี 2008 เราไม่สามารถใช้ Skip () Take ()
วิธีคือ:
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();