แยกการสืบค้น SQL ที่มีการรวมเป็นจำนวนน้อย


18

เราต้องทำการรายงานบางอย่างทุกคืนใน SQL Server 2008 R2 ของเรา การคำนวณรายงานใช้เวลาหลายชั่วโมง เพื่อที่จะย่นเวลาเราจะคำนวณตารางล่วงหน้า ตารางนี้สร้างขึ้นจากการเข้าร่วม 12 ตารางที่มีขนาดค่อนข้างใหญ่ (หลายสิบล้านแถว)

การคำนวณตารางรวมนี้ใช้เวลาไม่กี่วันที่ผ่านมา cca 4 ชั่วโมง DBA ของเราดีกว่าแบ่งการรวมครั้งใหญ่นี้ออกเป็น 3 การรวมที่เล็กกว่า (แต่ละการรวม 4 ตาราง) ผลลัพธ์ชั่วคราวจะถูกบันทึกลงในตารางชั่วคราวทุกครั้งที่ใช้ในการเข้าร่วมครั้งต่อไป

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

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

ฉันพูดคุยเกี่ยวกับเรื่องนี้กับ DBA ของเรา แต่เขาเองก็ไม่แน่ใจเกี่ยวกับสิ่งที่ทำให้เวลาในการปรับปรุงดีขึ้น เขาเพิ่งพูดถึงว่าเขาจะไม่ตำหนิเซิร์ฟเวอร์เพราะมันมีจำนวนมหาศาลในการคำนวณข้อมูลขนาดใหญ่และเป็นไปได้ว่าเครื่องมือเพิ่มประสิทธิภาพมีเวลายากที่จะคาดการณ์แผนการดำเนินการที่ดีที่สุด ... ฉันเข้าใจสิ่งนี้ แต่ฉันต้องการคำตอบที่ชัดเจนยิ่งขึ้นว่าทำไม

ดังนั้นคำถามคือ:

  1. สิ่งที่อาจทำให้เกิดการปรับปรุงครั้งใหญ่?

  2. มันเป็นขั้นตอนมาตรฐานในการแยกการรวมขนาดใหญ่ออกเป็นเล็กหรือไม่?

  3. ปริมาณของข้อมูลที่เซิร์ฟเวอร์มีการประมวลผลที่เล็กกว่าจริง ๆ ในกรณีที่มีการรวมขนาดเล็กหลายครั้งหรือไม่?

นี่คือข้อความค้นหาดั้งเดิม:

    Insert Into FinalResult_Base
SELECT       
    TC.TestCampaignContainerId,
    TC.CategoryId As TestCampaignCategoryId,
    TC.Grade,
    TC.TestCampaignId,    
    T.TestSetId
    ,TL.TestId
    ,TSK.CategoryId
    ,TT.[TestletId]
    ,TL.SectionNo
    ,TL.Difficulty
    ,TestletName = Char(65+TL.SectionNo) + CONVERT(varchar(4),6 - TL.Difficulty) 
    ,TQ.[QuestionId]
    ,TS.StudentId
    ,TS.ClassId
    ,RA.SubjectId
    ,TQ.[QuestionPoints] 
    ,GoodAnswer  = Case When TQ.[QuestionPoints] Is null Then 0
                      When TQ.[QuestionPoints] > 0 Then 1 
                      Else 0 End
    ,WrongAnswer = Case When TQ.[QuestionPoints] = 0 Then 1 
                      When TQ.[QuestionPoints] Is null Then 1
                     Else 0 End
    ,NoAnswer    = Case When TQ.[QuestionPoints] Is null Then 1 Else 0 End
    ,TS.Redizo
    ,TT.ViewCount
    ,TT.SpentTime
    ,TQ.[Position]  
    ,RA.SpecialNeeds        
    ,[Version] = 1 
    ,TestAdaptationId = TA.Id
    ,TaskId = TSK.TaskId
    ,TaskPosition = TT.Position
    ,QuestionRate = Q.Rate
    ,TestQuestionId = TQ.Guid
    ,AnswerType = TT.TestletAnswerTypeId
FROM 
    [TestQuestion] TQ WITH (NOLOCK)
    Join [TestTask] TT WITH (NOLOCK)            On TT.Guid = TQ.TestTaskId
    Join [Question] Q WITH (NOLOCK)         On TQ.QuestionId =  Q.QuestionId
    Join [Testlet] TL WITH (NOLOCK)         On TT.TestletId  = TL.Guid 
    Join [Test]     T WITH (NOLOCK)         On TL.TestId     =  T.Guid
    Join [TestSet] TS WITH (NOLOCK)         On T.TestSetId   = TS.Guid 
    Join [RoleAssignment] RA WITH (NOLOCK)  On TS.StudentId  = RA.PersonId And RA.RoleId = 1
    Join [Task] TSK WITH (NOLOCK)       On TSK.TaskId = TT.TaskId
    Join [Category] C WITH (NOLOCK)     On C.CategoryId = TSK.CategoryId
    Join [TimeWindow] TW WITH (NOLOCK)      On TW.Id = TS.TimeWindowId 
    Join [TestAdaptation] TA WITH (NOLOCK)  On TA.Id = TW.TestAdaptationId
    Join [TestCampaign] TC WITH (NOLOCK)        On TC.TestCampaignId = TA.TestCampaignId 
WHERE
    T.TestTypeId = 1    -- eliminuji ankety 
    And t.ProcessedOn is not null -- ne vsechny, jen dokoncene
    And TL.ShownOn is not null
    And TS.Redizo not in (999999999, 111111119)
END;

splitted ใหม่เข้าร่วมหลังจากการทำงานที่ยอดเยี่ยมของ DBA:

    SELECT       
    TC.TestCampaignContainerId,
    TC.CategoryId As TestCampaignCategoryId,
    TC.Grade,
    TC.TestCampaignId,    
    T.TestSetId
    ,TL.TestId
    ,TL.SectionNo
    ,TL.Difficulty
    ,TestletName = Char(65+TL.SectionNo) + CONVERT(varchar(4),6 - TL.Difficulty) -- prevod na A5, B4, B5 ...
    ,TS.StudentId
    ,TS.ClassId
    ,TS.Redizo
    ,[Version] = 1 -- ? 
    ,TestAdaptationId = TA.Id
    ,TL.Guid AS TLGuid
    ,TS.TimeWindowId
INTO
    [#FinalResult_Base_1]
FROM 
    [TestSet] [TS] WITH (NOLOCK)
    JOIN [Test] [T] WITH (NOLOCK) 
        ON [T].[TestSetId] = [TS].[Guid] AND [TS].[Redizo] NOT IN (999999999, 111111119) AND [T].[TestTypeId] = 1 AND [T].[ProcessedOn] IS NOT NULL
    JOIN [Testlet] [TL] WITH (NOLOCK)
        ON [TL].[TestId] = [T].[Guid] AND [TL].[ShownOn] IS NOT NULL
    JOIN [TimeWindow] [TW] WITH (NOLOCK)
        ON [TW].[Id] = [TS].[TimeWindowId] AND [TW].[IsActive] = 1
    JOIN [TestAdaptation] [TA] WITH (NOLOCK)
        ON [TA].[Id] = [TW].[TestAdaptationId] AND [TA].[IsActive] = 1
    JOIN [TestCampaign] [TC] WITH (NOLOCK)
        ON [TC].[TestCampaignId] = [TA].[TestCampaignId] AND [TC].[IsActive] = 1
    JOIN [TestCampaignContainer] [TCC] WITH (NOLOCK)
        ON [TCC].[TestCampaignContainerId] = [TC].[TestCampaignContainerId] AND [TCC].[IsActive] = 1
    ;

 SELECT       
    FR1.TestCampaignContainerId,
    FR1.TestCampaignCategoryId,
    FR1.Grade,
    FR1.TestCampaignId,    
    FR1.TestSetId
    ,FR1.TestId
    ,TSK.CategoryId AS [TaskCategoryId]
    ,TT.[TestletId]
    ,FR1.SectionNo
    ,FR1.Difficulty
    ,TestletName = Char(65+FR1.SectionNo) + CONVERT(varchar(4),6 - FR1.Difficulty) -- prevod na A5, B4, B5 ...
    ,FR1.StudentId
    ,FR1.ClassId
    ,FR1.Redizo
    ,TT.ViewCount
    ,TT.SpentTime
    ,[Version] = 1 -- ? 
    ,FR1.TestAdaptationId
    ,TaskId = TSK.TaskId
    ,TaskPosition = TT.Position
    ,AnswerType = TT.TestletAnswerTypeId
    ,TT.Guid AS TTGuid

INTO
    [#FinalResult_Base_2]
FROM 
    #FinalResult_Base_1 FR1
    JOIN [TestTask] [TT] WITH (NOLOCK)
        ON [TT].[TestletId] = [FR1].[TLGuid] 
    JOIN [Task] [TSK] WITH (NOLOCK)
        ON [TSK].[TaskId] = [TT].[TaskId] AND [TSK].[IsActive] = 1
    JOIN [Category] [C] WITH (NOLOCK)
        ON [C].[CategoryId] = [TSK].[CategoryId]AND [C].[IsActive] = 1
    ;    

DROP TABLE [#FinalResult_Base_1]

CREATE NONCLUSTERED INDEX [#IX_FR_Student_Class]
ON [dbo].[#FinalResult_Base_2] ([StudentId],[ClassId])
INCLUDE ([TTGuid])

SELECT       
    FR2.TestCampaignContainerId,
    FR2.TestCampaignCategoryId,
    FR2.Grade,
    FR2.TestCampaignId,    
    FR2.TestSetId
    ,FR2.TestId
    ,FR2.[TaskCategoryId]
    ,FR2.[TestletId]
    ,FR2.SectionNo
    ,FR2.Difficulty
    ,FR2.TestletName
    ,TQ.[QuestionId]
    ,FR2.StudentId
    ,FR2.ClassId
    ,RA.SubjectId
    ,TQ.[QuestionPoints] -- 1+ good, 0 wrong, null no answer
    ,GoodAnswer  = Case When TQ.[QuestionPoints] Is null Then 0
                      When TQ.[QuestionPoints] > 0 Then 1 -- cookie
                      Else 0 End
    ,WrongAnswer = Case When TQ.[QuestionPoints] = 0 Then 1 
                      When TQ.[QuestionPoints] Is null Then 1
                     Else 0 End
    ,NoAnswer    = Case When TQ.[QuestionPoints] Is null Then 1 Else 0 End
    ,FR2.Redizo
    ,FR2.ViewCount
    ,FR2.SpentTime
    ,TQ.[Position] AS [QuestionPosition]  
    ,RA.SpecialNeeds -- identifikace SVP        
    ,[Version] = 1 -- ? 
    ,FR2.TestAdaptationId
    ,FR2.TaskId
    ,FR2.TaskPosition
    ,QuestionRate = Q.Rate
    ,TestQuestionId = TQ.Guid
    ,FR2.AnswerType
INTO
    [#FinalResult_Base]
FROM 
    [#FinalResult_Base_2] FR2
    JOIN [TestQuestion] [TQ] WITH (NOLOCK)
        ON [TQ].[TestTaskId] = [FR2].[TTGuid]
    JOIN [Question] [Q] WITH (NOLOCK)
        ON [Q].[QuestionId] = [TQ].[QuestionId] AND [Q].[IsActive] = 1

    JOIN [RoleAssignment] [RA] WITH (NOLOCK)
        ON [RA].[PersonId] = [FR2].[StudentId]
        AND [RA].[ClassId] = [FR2].[ClassId] AND [RA].[IsActive] = 1 AND [RA].[RoleId] = 1

    drop table #FinalResult_Base_2;

    truncate table [dbo].[FinalResult_Base];
    insert into [dbo].[FinalResult_Base] select * from #FinalResult_Base;

    drop table #FinalResult_Base;

3
คำเตือน - ด้วย (NOLOCK) เป็นสิ่งที่ชั่วร้าย - อาจส่งผลให้ข้อมูลที่ไม่ดีกลับมา ฉันขอแนะนำให้ลองด้วย (ROWCOMMITTED)
TomTom

1
@TomTom คุณหมายถึงREADCOMMITTEDอะไร ฉันไม่เคยเห็น ROWCOMMITTED มาก่อน
ypercubeᵀᴹ

4
ด้วย (NOLOCK) ไม่ใช่ความชั่วร้าย มันไม่ใช่แค่สัญลักษณ์เวทย์มนตร์ที่ผู้คนคิดว่าเป็น เช่นเดียวกับสิ่งส่วนใหญ่ใน SQL Server และการพัฒนาซอฟต์แวร์โดยทั่วไปมีอยู่แล้ว
Zane

2
ใช่ แต่เนื่องจาก NOLOCK อาจสร้างคำเตือนในบันทึกและ - สำคัญกว่า - ส่งคืนข้อมูลที่ไม่ถูกต้องฉันคิดว่ามันชั่วร้าย มันสามารถใช้งานได้บนตารางเท่านั้นที่รับประกันว่าจะไม่เปลี่ยนแปลงในคีย์หลักและคีย์ที่เลือกขณะที่รันคิวรี และใช่ฉันหมายถึงพร้อมที่จะขอโทษ
TomTom

คำตอบ:


11

1 การลดลงของ 'พื้นที่การค้นหา' ควบคู่ไปกับสถิติที่ดีขึ้นสำหรับการเข้าร่วมระดับกลาง / ปลาย

ฉันต้องจัดการกับการรวม 90 ตาราง (การออกแบบมิกกี้เมาส์) ซึ่งตัวประมวลผลข้อความค้นหาปฏิเสธที่จะสร้างแผน การแบ่งการเข้าร่วมเป็น 10 ส่วนย่อยของ 9 ตารางแต่ละรายการทำให้ความซับซ้อนของการเข้าร่วมแต่ละครั้งลดลงอย่างมากซึ่งเพิ่มขึ้นอย่างทวีคูณเมื่อเพิ่มตารางเพิ่มเติม นอกจากนี้เครื่องมือเพิ่มประสิทธิภาพข้อความค้นหาปฏิบัติต่อพวกเขาเป็นแผน 10 การใช้จ่าย (อาจ) เวลาโดยรวม (Paul White อาจมีตัวชี้วัด!)

ขณะนี้ตารางผลลัพธ์ระดับกลางจะมีสถิติใหม่ของตัวเองดังนั้นจึงเข้าร่วมได้ดีกว่ามากเมื่อเทียบกับสถิติของต้นไม้ลึกที่เริ่มต้นตั้งแต่ต้นจนจบและกลายเป็นนิยายวิทยาศาสตร์ในไม่ช้าหลังจากนั้น

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

2 มันควรจะได้รับการพิจารณาในมุมมองของผมถ้ามีประสิทธิภาพและประสิทธิภาพการทำงานที่มีความสำคัญ

3 ไม่จำเป็น แต่อาจเป็นไปได้ว่าการรวมที่เลือกมากที่สุดจะถูกดำเนินการตั้งแต่ต้น


3
+1 ขอบคุณ โดยเฉพาะอย่างยิ่งสำหรับคำอธิบายประสบการณ์ของคุณ เป็นความจริงอย่างมากในการพูดว่า "ถ้าคุณสามารถประเมินการเลือกของภาคแสดงของคุณได้ดีกว่าเครื่องมือเพิ่มประสิทธิภาพทำไมไม่บังคับลำดับคำสั่งเข้าร่วม"
Ondrej Peterka

2
มันเป็นคำถามที่ถูกต้องจริง ๆ การเข้าร่วม 90 ตารางสามารถถูกบังคับให้สร้างแผนเพียงแค่ใช้ตัวเลือก 'สั่งกองทัพ' ไม่สำคัญว่าคำสั่งนั้นอาจจะสุ่มและไม่มีประสิทธิภาพเพียงแค่ลดพื้นที่การค้นหาก็เพียงพอที่จะช่วยให้เครื่องมือเพิ่มประสิทธิภาพสร้างแผนภายในไม่กี่วินาที
John Alan

6
  1. เครื่องมือเพิ่มประสิทธิภาพ SQLServer มักจะทำงานได้ดี อย่างไรก็ตามเป้าหมายของมันคือการไม่สร้างแผนที่ดีที่สุดเท่าที่จะเป็นไปได้ แต่เพื่อค้นหาแผนการที่ดีพออย่างรวดเร็ว สำหรับข้อความค้นหาที่มีการเข้าร่วมจำนวนมากอาจทำให้ประสิทธิภาพต่ำมาก การบ่งชี้ที่ดีของกรณีดังกล่าวเป็นความแตกต่างใหญ่ระหว่างจำนวนแถวโดยประมาณกับจำนวนจริงในแผนปฏิบัติการจริง นอกจากนี้ฉันค่อนข้างแน่ใจว่าแผนการดำเนินการสำหรับการค้นหาเริ่มต้นจะแสดง 'การรวมกลุ่มที่ซ้อนกันหลายครั้ง' ซึ่งช้ากว่าการรวมการรวม หลังจำเป็นต้องจัดเรียงอินพุตทั้งสองโดยใช้คีย์เดียวกันซึ่งมีราคาแพงและโดยปกติแล้วเครื่องมือเพิ่มประสิทธิภาพจะยกเลิกตัวเลือกดังกล่าว จัดเก็บผลลัพธ์ในตารางชั่วคราวและเพิ่มดัชนีที่เหมาะสมในขณะที่คุณทำผลลัพธ์ - ฉันเดา - ในการเลือกอัลกอริทึมที่ดีกว่าสำหรับการรวมต่อไป (หมายเหตุข้าง - คุณปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดโดยการเติมตาราง temp ก่อน และเพิ่มดัชนีหลังจาก) นอกจากนี้ SQLServer จะสร้างและเก็บสถิติสำหรับตารางชั่วคราวซึ่งจะช่วยเลือกดัชนีที่เหมาะสม
  2. ฉันไม่สามารถพูดได้ว่ามีมาตรฐานเกี่ยวกับการใช้ตารางชั่วคราวเมื่อจำนวนการเข้าร่วมมากกว่าจำนวนคงที่ แต่เป็นตัวเลือกที่สามารถปรับปรุงประสิทธิภาพได้อย่างแน่นอน มันไม่ได้เกิดขึ้นบ่อยนัก แต่ฉันมีปัญหาที่คล้ายกัน (และวิธีแก้ปัญหาที่คล้ายกัน) สองครั้ง คุณสามารถลองหาแผนการปฏิบัติที่ดีที่สุดด้วยตัวเองจัดเก็บและบังคับใช้ซ้ำได้ แต่จะใช้เวลานานมหาศาล (ไม่รับประกัน 100% ว่าคุณจะประสบความสำเร็จ) อีกด้านหมายเหตุ - ในกรณีที่ถ้า resultset ซึ่งเก็บไว้ในตารางชั่วคราวมีขนาดค่อนข้างเล็ก (พูดเกี่ยวกับ 10k บันทึก) ตัวแปรตารางทำงานได้ดีกว่าตารางอุณหภูมิ
  3. ฉันเกลียดการพูดว่า 'ขึ้นอยู่กับ' แต่อาจเป็นคำตอบสำหรับคำถามที่สามของคุณ เครื่องมือเพิ่มประสิทธิภาพต้องให้ผลลัพธ์ที่รวดเร็ว คุณไม่ต้องการให้ใช้เวลาหลายชั่วโมงในการพยายามวางแผนที่ดีที่สุด การเข้าร่วมแต่ละครั้งจะเพิ่มงานพิเศษและบางครั้งเครื่องมือเพิ่มประสิทธิภาพ 'สับสน'

3
+1 ขอบคุณสำหรับการยืนยันและคำอธิบาย สิ่งที่คุณเขียนมีเหตุผล
Ondrej Peterka

4

ผมขอเริ่มด้วยการบอกว่าคุณทำงานกับข้อมูลขนาดเล็ก - 10 ล้านล้านไม่ใหญ่ Projet DWH ล่าสุดที่ฉันมีเพิ่ม 400 ล้านแถวในตารางข้อเท็จจริง ต่อวัน. การจัดเก็บข้อมูลเป็นเวลา 5 ปี

ปัญหาคือฮาร์ดแวร์บางส่วน เนื่องจากตัวเชื่อมขนาดใหญ่อาจใช้พื้นที่ชั่วคราวจำนวนมากและมี RAM มากเพียงชั่วขณะที่คุณล้นเข้าไปในดิสก์สิ่งต่าง ๆ จะช้าลงอย่างมาก ดังนั้นจึงอาจเหมาะสมที่จะแบ่งงานออกเป็นส่วนเล็ก ๆ เพียงเพราะขณะที่ SQL อยู่ในโลกของชุดและไม่สนใจขนาดเซิร์ฟเวอร์ที่คุณใช้งานนั้นไม่มีที่สิ้นสุด ฉันค่อนข้างเคยชินกับข้อผิดพลาดของพื้นที่ว่างใน 64gb tempdb ในระหว่างการดำเนินการ

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

นอกจากนี้ยังมีเรื่องของการล็อค - นอกจากว่าคุณจะตั้งโปรแกรมที่การเข้าร่วมจำนวนมากอาจล็อคตารางเป็นเวลาหลายชั่วโมง ฉันกำลังทำการคัดลอก 200GB ในขณะนี้และฉันแยกมันออกเป็นส่วนย่อยโดยคีย์ธุรกิจ (วนซ้ำอย่างมีประสิทธิภาพ) ซึ่งทำให้การล็อกสั้นลงมาก

ในตอนท้ายเราทำงานกับฮาร์ดแวร์ที่ จำกัด


1
+1 ขอบคุณสำหรับคำตอบของคุณ มีจุดดีในการพูดมันขึ้นอยู่กับ HW เรามี RAM เพียง 32 GB ซึ่งอาจไม่เพียงพอ
Ondrej Peterka

2
ฉันผิดหวังนิดหน่อยทุกครั้งที่ฉันอ่านคำตอบเช่นนั้น - แม้แต่สองสามล้านแถวก็สร้างภาระซีพียูบนเซิร์ฟเวอร์ฐานข้อมูลของเราเป็นเวลาหลายชั่วโมง อาจเป็นจำนวนมิติสูง แต่ 30 มิติดูเหมือนจะไม่ใหญ่เกินไป ฉันคิดว่าจำนวนแถวที่คุณสามารถดำเนินการได้นั้นมาจากแบบจำลองอย่างง่าย ยิ่งแย่ไปกว่า: ข้อมูลทั้งหมดเหมาะกับ RAM และยังคงใช้เวลาหลายชั่วโมง
flaschenpost

1
30 มิติมีมาก - คุณแน่ใจหรือไม่ว่าโมเดลได้รับการปรับให้เหมาะสมกับดาว ข้อผิดพลาดบางอย่างเช่นค่าใช้จ่าย CPU - ในการสืบค้น OP กำลังใช้ GUID เป็นคีย์หลัก (Uniqueidentifier) ฉันรักพวกเขาเช่นกัน - เช่นเดียวกับดัชนีที่ไม่ซ้ำกันคีย์หลักคือฟิลด์ ID ทำให้การเปรียบเทียบทั้งหมดเร็วขึ้นและดัชนีก็ยิ่ง nawwox (4 หรือ 8 ไบต์ไม่ใช่ 18) เทคนิคอย่างนั้นประหยัด CPU จำนวนมาก
TomTom
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.