วิธีการเพิ่มประสิทธิภาพการสืบค้น LINQ ใน C#.NET

การแนะนำ

В บทความนี้ มีการพิจารณาวิธีการปรับให้เหมาะสมบางประการ สอบถาม LINQ.
ที่นี่เรายังนำเสนอแนวทางเพิ่มเติมในการเพิ่มประสิทธิภาพโค้ดที่เกี่ยวข้องด้วย สอบถาม LINQ.

รู้จักกันในนามว่า ลิงค์(Language-Integrated Query) เป็นภาษาที่ง่ายและสะดวกสำหรับการสืบค้นแหล่งข้อมูล

А LINQ เป็น SQL เป็นเทคโนโลยีในการเข้าถึงข้อมูลใน DBMS นี่เป็นเครื่องมืออันทรงพลังสำหรับการทำงานกับข้อมูล โดยที่การสืบค้นจะถูกสร้างขึ้นผ่านภาษาที่เปิดเผย ซึ่งจากนั้นจะถูกแปลงเป็น แบบสอบถาม SQL แพลตฟอร์มและส่งไปยังเซิร์ฟเวอร์ฐานข้อมูลเพื่อดำเนินการ ในกรณีของเรา โดย DBMS เราหมายถึง เซิร์ฟเวอร์ MS SQL.

อย่างไรก็ตาม สอบถาม LINQ จะไม่ถูกแปลงเป็นข้อความที่เขียนอย่างเหมาะสมที่สุด แบบสอบถาม SQLซึ่ง DBA ที่มีประสบการณ์สามารถเขียนได้โดยคำนึงถึงความแตกต่างของการปรับให้เหมาะสมทั้งหมด แบบสอบถาม SQL:

  1. การเชื่อมต่อที่เหมาะสมที่สุด (สมัคร) และการกรองผลลัพธ์ (WHERE)
  2. ความแตกต่างมากมายในการใช้การเชื่อมต่อและเงื่อนไขกลุ่ม
  3. เงื่อนไขการเปลี่ยนที่หลากหลาย IN บน มีอยู่и ไม่อยู่ใน, <> เปิด มีอยู่
  4. การแคชผลลัพธ์ระดับกลางผ่านตารางชั่วคราว, CTE, ตัวแปรตาราง
  5. การใช้ประโยค (ทางเลือกที่) พร้อมคำแนะนำและคำแนะนำตาราง กับ (... )
  6. การใช้มุมมองที่จัดทำดัชนีเป็นวิธีหนึ่งในการกำจัดการอ่านข้อมูลที่ซ้ำซ้อนระหว่างการเลือก

คอขวดประสิทธิภาพหลักของผลลัพธ์ แบบสอบถาม SQL เมื่อรวบรวม สอบถาม LINQ พวกเขาคือ

  1. การรวมกลไกการเลือกข้อมูลทั้งหมดไว้ในคำขอเดียว
  2. การทำซ้ำบล็อกโค้ดที่เหมือนกัน ซึ่งท้ายที่สุดจะนำไปสู่การอ่านข้อมูลที่ไม่จำเป็นหลายครั้งในที่สุด
  3. กลุ่มของเงื่อนไขหลายองค์ประกอบ (ตรรกะ "และ" และ "หรือ") - AND и ORเมื่อรวมเข้ากับเงื่อนไขที่ซับซ้อน นำไปสู่ความจริงที่ว่าเครื่องมือเพิ่มประสิทธิภาพซึ่งมีดัชนีที่ไม่ใช่คลัสเตอร์ที่เหมาะสมสำหรับฟิลด์ที่จำเป็น ในที่สุดก็เริ่มสแกนเทียบกับดัชนีคลัสเตอร์ (สแกนดัชนี) ตามกลุ่มเงื่อนไข
  4. การซ้อนแบบสอบถามย่อยแบบลึกทำให้การแยกวิเคราะห์เป็นปัญหามาก คำสั่ง SQL และการวิเคราะห์แผนการสืบค้นในส่วนของนักพัฒนาและ DBA

วิธีการเพิ่มประสิทธิภาพ

ตอนนี้เรามาดูวิธีการปรับให้เหมาะสมโดยตรงกัน

1) การจัดทำดัชนีเพิ่มเติม

วิธีที่ดีที่สุดคือพิจารณาตัวกรองในตารางการเลือกหลัก เนื่องจากบ่อยครั้งที่แบบสอบถามทั้งหมดถูกสร้างขึ้นโดยใช้ตารางหลักหนึ่งหรือสองตาราง (แอปพลิเคชัน-บุคคล-การดำเนินการ) และมีชุดเงื่อนไขมาตรฐาน (ปิด ยกเลิก เปิดใช้งาน สถานะ) สิ่งสำคัญคือต้องสร้างดัชนีที่เหมาะสมสำหรับกลุ่มตัวอย่างที่ระบุ

โซลูชันนี้เหมาะสมเมื่อเลือกฟิลด์เหล่านี้เพื่อจำกัดชุดที่ส่งคืนให้กับแบบสอบถามอย่างมาก

เช่น เรามีใบสมัคร 500000 ใบ อย่างไรก็ตาม มีแอปพลิเคชันที่ใช้งานอยู่เพียง 2000 รายการเท่านั้น จากนั้นดัชนีที่เลือกอย่างถูกต้องจะช่วยเราจาก สแกนดัชนี บนโต๊ะขนาดใหญ่และจะช่วยให้คุณสามารถเลือกข้อมูลได้อย่างรวดเร็วผ่านดัชนีที่ไม่ใช่คลัสเตอร์

นอกจากนี้ การขาดดัชนีสามารถระบุได้ผ่านพร้อมท์สำหรับการแยกวิเคราะห์แผนการสืบค้นหรือการรวบรวมสถิติมุมมองของระบบ เซิร์ฟเวอร์ MS SQL:

  1. sys.dm_db_missing_index_groups
  2. sys.dm_db_missing_index_group_stats
  3. sys.dm_db_missing_index_details

ข้อมูลมุมมองทั้งหมดมีข้อมูลเกี่ยวกับดัชนีที่ขาดหายไป ยกเว้นดัชนีเชิงพื้นที่

อย่างไรก็ตาม ดัชนีและแคชมักเป็นวิธีจัดการกับผลที่ตามมาของการเขียนที่ไม่ดี สอบถาม LINQ и แบบสอบถาม SQL.

ดังที่แสดงให้เห็นวิถีชีวิตอันโหดร้าย บ่อยครั้งที่ธุรกิจต้องใช้คุณลักษณะทางธุรกิจภายในกำหนดเวลาที่แน่นอน ดังนั้นคำขอจำนวนมากจึงมักถูกถ่ายโอนไปยังพื้นหลังด้วยการแคช

นี่เป็นเหตุผลบางส่วน เนื่องจากผู้ใช้ไม่ต้องการข้อมูลล่าสุดเสมอไป และอินเทอร์เฟซผู้ใช้มีระดับการตอบสนองที่ยอมรับได้

แนวทางนี้ช่วยให้สามารถแก้ไขความต้องการทางธุรกิจได้ แต่ท้ายที่สุดแล้วก็จะลดประสิทธิภาพของระบบข้อมูลลงโดยการชะลอการแก้ปัญหาออกไป

นอกจากนี้ยังควรจำไว้ว่าในกระบวนการค้นหาดัชนีที่จำเป็นในการเพิ่มข้อเสนอแนะ MSSQL การเพิ่มประสิทธิภาพอาจไม่ถูกต้อง รวมถึงภายใต้เงื่อนไขต่อไปนี้:

  1. หากมีดัชนีที่มีชุดฟิลด์คล้ายกันอยู่แล้ว
  2. หากเขตข้อมูลในตารางไม่สามารถจัดทำดัชนีได้เนื่องจากข้อจำกัดในการจัดทำดัชนี (อธิบายรายละเอียดเพิ่มเติม ที่นี่).

2) การรวมคุณลักษณะให้เป็นคุณลักษณะใหม่เดียว

บางครั้งบางเขตข้อมูลจากตารางหนึ่งซึ่งทำหน้าที่เป็นพื้นฐานสำหรับกลุ่มเงื่อนไข สามารถถูกแทนที่ด้วยการแนะนำเขตข้อมูลใหม่หนึ่งเขต

โดยเฉพาะอย่างยิ่งสำหรับฟิลด์สถานะ ซึ่งโดยปกติจะเป็นประเภทบิตหรือจำนวนเต็ม

ตัวอย่าง:

ปิด = 0 และยกเลิก = 0 และเปิดใช้งาน = 0 ถูกแทนที่ด้วย สถานะ = 1.

นี่คือที่ที่มีการแนะนำแอ็ตทริบิวต์สถานะจำนวนเต็มเพื่อให้แน่ใจว่าสถานะเหล่านี้ถูกเติมลงในตาราง ถัดไป แอตทริบิวต์ใหม่นี้จะถูกจัดทำดัชนี

นี่เป็นวิธีแก้ปัญหาขั้นพื้นฐานสำหรับปัญหาด้านประสิทธิภาพ เนื่องจากเราเข้าถึงข้อมูลโดยไม่ต้องคำนวณโดยไม่จำเป็น

3) การทำให้เป็นรูปธรรมของมุมมอง

น่าเสียดายที่ใน สอบถาม LINQ ตารางชั่วคราว, CTE และตัวแปรตารางไม่สามารถนำมาใช้โดยตรง

อย่างไรก็ตาม มีวิธีอื่นในการเพิ่มประสิทธิภาพสำหรับกรณีนี้ - มุมมองที่จัดทำดัชนีไว้

กลุ่มเงื่อนไข (จากตัวอย่างด้านบน) ปิด = 0 และยกเลิก = 0 และเปิดใช้งาน = 0 (หรือชุดของเงื่อนไขอื่นๆ ที่คล้ายคลึงกัน) กลายเป็นตัวเลือกที่ดีในการใช้ในมุมมองที่มีการจัดทำดัชนี โดยแคชข้อมูลส่วนเล็กๆ จากชุดใหญ่

แต่มีข้อจำกัดหลายประการเมื่อทำให้มุมมองเป็นรูปธรรม:

  1. การใช้แบบสอบถามย่อย clauses มีอยู่ ควรเปลี่ยนมาใช้ สมัคร
  2. คุณไม่สามารถใช้ประโยคได้ ยูเนี่ยน, ยูเนี่ยนทั้งหมด, ข้อยกเว้น, ตัด
  3. คุณไม่สามารถใช้คำแนะนำและส่วนคำสั่งของตารางได้ ทางเลือกที่
  4. ไม่มีความเป็นไปได้ในการทำงานกับวงจร
  5. เป็นไปไม่ได้ที่จะแสดงข้อมูลในมุมมองเดียวจากตารางต่างๆ

สิ่งสำคัญคือต้องจำไว้ว่าประโยชน์ที่แท้จริงของการใช้มุมมองที่จัดทำดัชนีจะบรรลุได้โดยการจัดทำดัชนีจริงเท่านั้น

แต่เมื่อเรียกมุมมอง ดัชนีเหล่านี้อาจไม่สามารถใช้ได้ และหากต้องการใช้อย่างชัดเจน คุณต้องระบุ ด้วย(ไม่ขยาย).

ตั้งแต่ใน สอบถาม LINQ เป็นไปไม่ได้ที่จะกำหนดคำแนะนำตาราง ดังนั้นคุณต้องสร้างการนำเสนออื่น - "wrapper" ของแบบฟอร์มต่อไปนี้:

CREATE VIEW ИМЯ_представления AS SELECT * FROM MAT_VIEW WITH (NOEXPAND);

4) การใช้ฟังก์ชันตาราง

มักจะเข้า. สอบถาม LINQ บล็อกขนาดใหญ่ของแบบสอบถามย่อยหรือบล็อกที่ใช้มุมมองที่มีโครงสร้างที่ซับซ้อนก่อให้เกิดการสืบค้นขั้นสุดท้ายที่มีโครงสร้างการดำเนินการที่ซับซ้อนมากและด้อยประสิทธิภาพ

ประโยชน์หลักของการใช้ฟังก์ชันตารางใน สอบถาม LINQ:

  1. ความสามารถ ในกรณีของมุมมอง ที่จะใช้และระบุเป็นออบเจ็กต์ แต่คุณสามารถส่งชุดของพารามิเตอร์อินพุตได้:
    จาก FUNCTION(@param1, @param2 ...)
    ส่งผลให้สามารถสุ่มตัวอย่างข้อมูลที่ยืดหยุ่นได้
  2. ในกรณีของการใช้ฟังก์ชันตาราง ไม่มีข้อจำกัดที่เข้มงวดเช่นในกรณีของมุมมองที่จัดทำดัชนีไว้ตามที่อธิบายไว้ข้างต้น:
    1. คำแนะนำตาราง:
      ตลอด ลิงค์ คุณไม่สามารถระบุได้ว่าควรใช้ดัชนีใดและกำหนดระดับการแยกข้อมูลเมื่อทำการสืบค้น
      แต่ฟังก์ชันมีความสามารถเหล่านี้
      ด้วยฟังก์ชันนี้ คุณสามารถบรรลุแผนการสืบค้นการดำเนินการที่ค่อนข้างคงที่ โดยมีการกำหนดกฎสำหรับการทำงานกับดัชนีและระดับการแยกข้อมูล
    2. การใช้ฟังก์ชันนี้ช่วยให้ได้รับ: เมื่อเปรียบเทียบกับมุมมองที่จัดทำดัชนีแล้ว
      • ตรรกะการสุ่มตัวอย่างข้อมูลที่ซับซ้อน (แม้จะใช้ลูป)
      • ดึงข้อมูลจากตารางต่างๆ มากมาย
      • ใช้ ยูเนี่ยน и มีอยู่

  3. เสนอ ทางเลือกที่ มีประโยชน์มากเมื่อเราต้องการควบคุมการทำงานพร้อมกัน ตัวเลือก(MAXDOP N)ลำดับของแผนการดำเนินการแบบสอบถาม ตัวอย่างเช่น:
    • คุณสามารถระบุการบังคับให้สร้างแผนการสืบค้นใหม่ได้ ตัวเลือก (คอมไพล์ใหม่)
    • คุณสามารถระบุว่าจะบังคับให้แผนแบบสอบถามใช้ลำดับการรวมที่ระบุในแบบสอบถามหรือไม่ ตัวเลือก (บังคับสั่ง)

    รายละเอียดเพิ่มเติมเกี่ยวกับ ทางเลือกที่ อธิบายไว้ ที่นี่.

  4. การใช้ส่วนข้อมูลที่แคบที่สุดและจำเป็นที่สุด:
    ไม่จำเป็นต้องจัดเก็บชุดข้อมูลขนาดใหญ่ในแคช (เช่นเดียวกับกรณีที่มีมุมมองที่จัดทำดัชนี) ซึ่งคุณยังคงต้องกรองข้อมูลตามพารามิเตอร์
    ตัวอย่างเช่น มีตารางที่มีตัวกรอง WHERE มีการใช้สามฟิลด์ (ก ข ค).

    ตามอัตภาพ คำขอทั้งหมดจะมีเงื่อนไขคงที่ a = 0 และ b = 0.

    อย่างไรก็ตามการขอลงสนาม c ตัวแปรมากขึ้น

    ปล่อยให้มีสภาพ a = 0 และ b = 0 ช่วยให้เราจำกัดชุดผลลัพธ์ที่ต้องการเป็นหลายพันเรกคอร์ดได้จริง แต่เปิดเงื่อนไขไว้ с จำกัดการเลือกให้เหลือเพียงร้อยบันทึก

    ฟังก์ชันตารางอาจเป็นตัวเลือกที่ดีกว่า

    นอกจากนี้ ฟังก์ชันตารางยังคาดเดาได้มากขึ้นและสม่ำเสมอในเวลาดำเนินการอีกด้วย

ตัวอย่าง

ลองดูตัวอย่างการใช้งานโดยใช้ฐานข้อมูลคำถามเป็นตัวอย่าง

มีเรื่องขอ SELECTซึ่งรวมหลายตารางและใช้มุมมองเดียว (OperativeQuestions) ซึ่งตรวจสอบความเกี่ยวข้องทางอีเมล (ผ่าน มีอยู่) ถึง “คำถามเชิงปฏิบัติการ”:

คำขอหมายเลข 1

(@p__linq__0 nvarchar(4000))SELECT
1 AS [C1],
[Extent1].[Id] AS [Id],
[Join2].[Object_Id] AS [Object_Id],
[Join2].[ObjectType_Id] AS [ObjectType_Id],
[Join2].[Name] AS [Name],
[Join2].[ExternalId] AS [ExternalId]
FROM [dbo].[Questions] AS [Extent1]
INNER JOIN (SELECT [Extent2].[Object_Id] AS [Object_Id],
[Extent2].[Question_Id] AS [Question_Id], [Extent3].[ExternalId] AS [ExternalId],
[Extent3].[ObjectType_Id] AS [ObjectType_Id], [Extent4].[Name] AS [Name]
FROM [dbo].[ObjectQuestions] AS [Extent2]
INNER JOIN [dbo].[Objects] AS [Extent3] ON [Extent2].[Object_Id] = [Extent3].[Id]
LEFT OUTER JOIN [dbo].[ObjectTypes] AS [Extent4] 
ON [Extent3].[ObjectType_Id] = [Extent4].[Id] ) AS [Join2] 
ON [Extent1].[Id] = [Join2].[Question_Id]
WHERE ([Extent1].[AnswerId] IS NULL) AND (0 = [Extent1].[Exp]) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[OperativeQuestions] AS [Extent5]
WHERE (([Extent5].[Email] = @p__linq__0) OR (([Extent5].[Email] IS NULL) 
AND (@p__linq__0 IS NULL))) AND ([Extent5].[Id] = [Extent1].[Id])
));

มุมมองมีโครงสร้างที่ค่อนข้างซับซ้อน: มีการรวมแบบสอบถามย่อยและใช้การเรียงลำดับ DISTINCTซึ่งโดยทั่วไปเป็นการดำเนินการที่ค่อนข้างใช้ทรัพยากรมาก

ตัวอย่างจาก OperativeQuestions มีประมาณหนึ่งหมื่นบันทึก

ปัญหาหลักของแบบสอบถามนี้คือสำหรับบันทึกจากการสืบค้นภายนอก แบบสอบถามย่อยภายในจะถูกดำเนินการในมุมมอง [OperativeQuestions] ซึ่งควรสำหรับ [Email] = @p__linq__0 อนุญาตให้เราจำกัดการเลือกผลลัพธ์ (ผ่าน มีอยู่) มากถึงหลายร้อยรายการ

และอาจดูเหมือนว่าแบบสอบถามย่อยควรคำนวณบันทึกหนึ่งครั้งโดย [Email] = @p__linq__0 จากนั้นบันทึกสองสามร้อยรายการเหล่านี้ควรเชื่อมต่อด้วย Id กับคำถาม และการสืบค้นจะรวดเร็ว

ในความเป็นจริง มีการเชื่อมต่อตามลำดับของตารางทั้งหมด: การตรวจสอบความสอดคล้องของคำถาม Id กับ Id จาก OperativeQuestions และการกรองทางอีเมล

ที่จริงแล้ว คำขอดังกล่าวใช้ได้กับบันทึก OperativeQuestions ทั้งหมดนับหมื่นรายการ แต่ต้องการเฉพาะข้อมูลที่สนใจทางอีเมลเท่านั้น

ข้อความดู OperationQuestions:

คำขอหมายเลข 2

 
CREATE VIEW [dbo].[OperativeQuestions]
AS
SELECT DISTINCT Q.Id, USR.email AS Email
FROM            [dbo].Questions AS Q INNER JOIN
                         [dbo].ProcessUserAccesses AS BPU ON BPU.ProcessId = CQ.Process_Id 
OUTER APPLY
                     (SELECT   1 AS HasNoObjects
                      WHERE   NOT EXISTS
                                    (SELECT   1
                                     FROM     [dbo].ObjectUserAccesses AS BOU
                                     WHERE   BOU.ProcessUserAccessId = BPU.[Id] AND BOU.[To] IS NULL)
) AS BO INNER JOIN
                         [dbo].Users AS USR ON USR.Id = BPU.UserId
WHERE        CQ.[Exp] = 0 AND CQ.AnswerId IS NULL AND BPU.[To] IS NULL 
AND (BO.HasNoObjects = 1 OR
              EXISTS (SELECT   1
                           FROM   [dbo].ObjectUserAccesses AS BOU INNER JOIN
                                      [dbo].ObjectQuestions AS QBO 
                                                  ON QBO.[Object_Id] =BOU.ObjectId
                               WHERE  BOU.ProcessUserAccessId = BPU.Id 
                               AND BOU.[To] IS NULL AND QBO.Question_Id = CQ.Id));

การแมปมุมมองเริ่มต้นใน DbContext (EF Core 2)

public class QuestionsDbContext : DbContext
{
    //...
    public DbQuery<OperativeQuestion> OperativeQuestions { get; set; }
    //...
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Query<OperativeQuestion>().ToView("OperativeQuestions");
    }
}

แบบสอบถาม LINQ เริ่มต้น

var businessObjectsData = await context
    .OperativeQuestions
    .Where(x => x.Email == Email)
    .Include(x => x.Question)
    .Select(x => x.Question)
    .SelectMany(x => x.ObjectQuestions,
                (x, bo) => new
                {
                    Id = x.Id,
                    ObjectId = bo.Object.Id,
                    ObjectTypeId = bo.Object.ObjectType.Id,
                    ObjectTypeName = bo.Object.ObjectType.Name,
                    ObjectExternalId = bo.Object.ExternalId
                })
    .ToListAsync();

ในกรณีเฉพาะนี้ เรากำลังพิจารณาวิธีแก้ไขปัญหานี้โดยไม่มีการเปลี่ยนแปลงโครงสร้างพื้นฐาน โดยไม่ต้องแนะนำตารางแยกต่างหากพร้อมผลลัพธ์สำเร็จรูป (“แบบสอบถามที่ใช้งานอยู่”) ซึ่งจะต้องใช้กลไกในการกรอกข้อมูลและรักษาให้ทันสมัยอยู่เสมอ .

แม้ว่านี่จะเป็นวิธีแก้ปัญหาที่ดี แต่ก็มีอีกทางเลือกหนึ่งในการเพิ่มประสิทธิภาพปัญหานี้

วัตถุประสงค์หลักคือการแคชรายการโดย [Email] = @p__linq__0 จากมุมมอง OperationQuestions

แนะนำฟังก์ชันตาราง [dbo].[OperativeQuestionsUserMail] ลงในฐานข้อมูล

โดยการส่งอีเมลเป็นพารามิเตอร์อินพุต เราจะได้ตารางค่ากลับมา:

คำขอหมายเลข 3


CREATE FUNCTION [dbo].[OperativeQuestionsUserMail]
(
    @Email  nvarchar(4000)
)
RETURNS
@tbl TABLE
(
    [Id]           uniqueidentifier,
    [Email]      nvarchar(4000)
)
AS
BEGIN
        INSERT INTO @tbl ([Id], [Email])
        SELECT Id, @Email
        FROM [OperativeQuestions]  AS [x] WHERE [x].[Email] = @Email;
     
    RETURN;
END

ซึ่งจะส่งคืนตารางค่าที่มีโครงสร้างข้อมูลที่กำหนดไว้ล่วงหน้า

เพื่อให้การสืบค้นไปยัง OperativeQuestionsUserMail เหมาะสมที่สุดและมีแผนสืบค้นที่เหมาะสมที่สุด จำเป็นต้องมีโครงสร้างที่เข้มงวด และไม่ ส่งคืนตารางเป็นการส่งคืน...

ในกรณีนี้ Query 1 ที่จำเป็นจะถูกแปลงเป็น Query 4:

คำขอหมายเลข 4

(@p__linq__0 nvarchar(4000))SELECT
1 AS [C1],
[Extent1].[Id] AS [Id],
[Join2].[Object_Id] AS [Object_Id],
[Join2].[ObjectType_Id] AS [ObjectType_Id],
[Join2].[Name] AS [Name],
[Join2].[ExternalId] AS [ExternalId]
FROM (
    SELECT Id, Email FROM [dbo].[OperativeQuestionsUserMail] (@p__linq__0)
) AS [Extent0]
INNER JOIN [dbo].[Questions] AS [Extent1] ON([Extent0].Id=[Extent1].Id)
INNER JOIN (SELECT [Extent2].[Object_Id] AS [Object_Id], [Extent2].[Question_Id] AS [Question_Id], [Extent3].[ExternalId] AS [ExternalId], [Extent3].[ObjectType_Id] AS [ObjectType_Id], [Extent4].[Name] AS [Name]
FROM [dbo].[ObjectQuestions] AS [Extent2]
INNER JOIN [dbo].[Objects] AS [Extent3] ON [Extent2].[Object_Id] = [Extent3].[Id]
LEFT OUTER JOIN [dbo].[ObjectTypes] AS [Extent4] 
ON [Extent3].[ObjectType_Id] = [Extent4].[Id] ) AS [Join2] 
ON [Extent1].[Id] = [Join2].[Question_Id]
WHERE ([Extent1].[AnswerId] IS NULL) AND (0 = [Extent1].[Exp]);

การแมปมุมมองและฟังก์ชันใน DbContext (EF Core 2)

public class QuestionsDbContext : DbContext
{
    //...
    public DbQuery<OperativeQuestion> OperativeQuestions { get; set; }
    //...
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Query<OperativeQuestion>().ToView("OperativeQuestions");
    }
}
 
public static class FromSqlQueries
{
    public static IQueryable<OperativeQuestion> GetByUserEmail(this DbQuery<OperativeQuestion> source, string Email)
        => source.FromSql($"SELECT Id, Email FROM [dbo].[OperativeQuestionsUserMail] ({Email})");
}

แบบสอบถาม LINQ สุดท้าย

var businessObjectsData = await context
    .OperativeQuestions
    .GetByUserEmail(Email)
    .Include(x => x.Question)
    .Select(x => x.Question)
    .SelectMany(x => x.ObjectQuestions,
                (x, bo) => new
                {
                    Id = x.Id,
                    ObjectId = bo.Object.Id,
                    ObjectTypeId = bo.Object.ObjectType.Id,
                    ObjectTypeName = bo.Object.ObjectType.Name,
                    ObjectExternalId = bo.Object.ExternalId
                })
    .ToListAsync();

ลำดับของเวลาดำเนินการลดลงจาก 200-800 ms เป็น 2-20 ms ฯลฯ กล่าวคือ เร็วขึ้นหลายสิบเท่า

หากเราพิจารณาโดยเฉลี่ยมากขึ้น แทนที่จะเป็น 350 มิลลิวินาที เราได้ 8 มิลลิวินาที

จากข้อได้เปรียบที่ชัดเจน เรายังได้รับ:

  1. การลดภาระการอ่านโดยทั่วไป
  2. ลดโอกาสในการบล็อกลงอย่างมาก
  3. ลดเวลาการบล็อกเฉลี่ยลงเป็นค่าที่ยอมรับได้

เอาท์พุต

การเพิ่มประสิทธิภาพและการปรับแต่งการเรียกฐานข้อมูลอย่างละเอียด MSSQL ตลอด ลิงค์ เป็นปัญหาที่สามารถแก้ไขได้

ความเอาใจใส่และความสม่ำเสมอมีความสำคัญมากในงานนี้

ที่จุดเริ่มต้นของกระบวนการ:

  1. จำเป็นต้องตรวจสอบข้อมูลที่คำขอทำงาน (ค่า ประเภทข้อมูลที่เลือก)
  2. ดำเนินการจัดทำดัชนีข้อมูลนี้อย่างเหมาะสม
  3. ตรวจสอบความถูกต้องของเงื่อนไขการเข้าร่วมระหว่างตาราง

การทำซ้ำการปรับให้เหมาะสมครั้งถัดไปเผยให้เห็น:

  1. พื้นฐานของคำขอและกำหนดตัวกรองคำขอหลัก
  2. ทำซ้ำบล็อกข้อความค้นหาที่คล้ายกันและวิเคราะห์จุดตัดของเงื่อนไข
  3. ใน SSMS หรือ GUI อื่น ๆ สำหรับ SQL Server ปรับให้เหมาะสมเอง แบบสอบถาม SQL (จัดสรรที่เก็บข้อมูลระดับกลาง สร้างแบบสอบถามผลลัพธ์โดยใช้ที่เก็บข้อมูลนี้ (อาจมีหลายอัน))
  4. ในขั้นตอนสุดท้ายโดยคำนึงถึงผลลัพธ์เป็นหลัก แบบสอบถาม SQL, กำลังสร้างโครงสร้างใหม่ แบบสอบถาม LINQ

ผลลัพท์ที่ได้ แบบสอบถาม LINQ ควรจะมีโครงสร้างที่เหมือนกันเพื่อระบุจุดที่เหมาะสมที่สุด แบบสอบถาม SQL จากจุดที่ 3

บลาโกดาเรนนอสตี

ขอบคุณมากสำหรับเพื่อนร่วมงาน จ็อบเจมวส์ и alex_ozr จากบริษัท กรดไนตริก เพื่อขอความช่วยเหลือในการเตรียมเอกสารนี้

ที่มา: will.com

เพิ่มความคิดเห็น