ขึ้นอยู่กับข้อมูลในตารางดัชนีของคุณ .... ยากที่จะพูดโดยไม่สามารถเปรียบเทียบแผนการดำเนินการ / สถิติเวลา + io
ความแตกต่างที่ฉันคาดหวังคือการกรองพิเศษเกิดขึ้นก่อนเข้าร่วมระหว่างสองตาราง ในตัวอย่างของฉันฉันเปลี่ยนการปรับปรุงเพื่อเลือกเพื่อใช้ตารางของฉันซ้ำ
แผนการดำเนินการด้วย "การเพิ่มประสิทธิภาพ"
แผนปฏิบัติการ
คุณเห็นการทำงานของตัวกรองเกิดขึ้นอย่างชัดเจนในข้อมูลการทดสอบของฉันไม่มีบันทึกที่ถูกกรองออกและทำให้ไม่มีการปรับปรุงเมื่อทำ
แผนการดำเนินการโดยไม่มี "การเพิ่มประสิทธิภาพ"
แผนปฏิบัติการ
ตัวกรองหายไปซึ่งหมายความว่าเราจะต้องพึ่งพาการเข้าร่วมเพื่อกรองระเบียนที่ไม่จำเป็น
เหตุผลอื่น ๆ
อีกเหตุผล / ผลลัพธ์ของการเปลี่ยนแปลงแบบสอบถามอาจเป็นได้ว่าแผนการดำเนินการใหม่ถูกสร้างขึ้นเมื่อเปลี่ยนแบบสอบถามซึ่งจะเกิดขึ้นเร็วขึ้น ตัวอย่างของสิ่งนี้คือเอ็นจิ้นที่เลือกโอเปอเรเตอร์เข้าร่วมที่แตกต่างกัน แต่นั่นเป็นเพียงการเดาที่จุดนี้
แก้ไข:
ความชัดเจนหลังจากได้รับแผนแบบสอบถามสองแบบ:
ข้อความค้นหากำลังอ่าน 550M Rows จากตารางขนาดใหญ่และกรองออก
หมายความว่าเพรดิเคตนั้นเป็นตัวที่ทำการกรองส่วนใหญ่ไม่ใช่เพรดิเคตที่ค้นหา ส่งผลให้ข้อมูลกำลังอ่าน แต่จะถูกส่งกลับน้อยลง
การทำให้เซิร์ฟเวอร์ sql ใช้ดัชนีอื่น (แผนแบบสอบถาม) / การเพิ่มดัชนีสามารถแก้ไขปัญหานี้ได้
เหตุใดข้อความค้นหาการเพิ่มประสิทธิภาพจึงไม่มีปัญหาเดียวกันนี้
เนื่องจากมีการใช้แผนคิวรีที่แตกต่างกันโดยมีการสแกนแทนการค้นหา
โดยไม่ทำการค้นหาใด ๆ แต่ส่งคืนแถว 4M เท่านั้นที่จะทำงานได้
ความแตกต่างถัดไป
การไม่คำนึงถึงความแตกต่างของการอัปเดต (ไม่มีการอัปเดตในเคียวรีที่ปรับให้เหมาะสม) การจับคู่แบบแฮชจะใช้กับเคียวรีที่ปรับให้เหมาะสม:
แทนที่จะเข้าร่วมลูปซ้อนกันบนที่ไม่เหมาะสม:
การวนซ้ำซ้อนกันจะดีที่สุดเมื่อตารางหนึ่งมีขนาดเล็กและอีกอันหนึ่งใหญ่ เนื่องจากทั้งคู่มีขนาดใกล้เคียงกันฉันจึงขอยืนยันว่าการจับคู่แฮชเป็นตัวเลือกที่ดีกว่าในกรณีนี้
ภาพรวม
แบบสอบถามที่ปรับให้เหมาะสม
แผนของแบบสอบถามที่ปรับให้เหมาะสมนั้นมีความคล้ายคลึงกันใช้การเข้าร่วมแฮชจับคู่และต้องการกรอง IO ที่เหลือน้อยลง นอกจากนี้ยังใช้บิตแมปเพื่อกำจัดค่าคีย์ที่ไม่สามารถสร้างแถวเข้าร่วมได้ (ยังไม่มีการอัพเดทอะไร)
แบบสอบถาม
ที่ไม่ปรับให้เหมาะสมแผนของแบบสอบถามที่ไม่ได้รับการเพิ่มประสิทธิภาพนั้นไม่มีความคล้ายคลึงกันใช้การเข้าร่วมลูปซ้อนกันและจำเป็นต้องทำการกรอง IO ที่เหลือในบันทึก 550M (เช่นการอัปเดตที่เกิดขึ้น)
คุณสามารถทำอะไรเพื่อปรับปรุงการค้นหาที่ไม่ปรับให้เหมาะสม
การเปลี่ยนดัชนีให้มี first_name & last_name ในรายการคอลัมน์คีย์:
สร้างดัชนี IX_largeTableOfPeople_birth_date_first_name_last_name บน dbo.largeTableOfPeople (วันเกิด, ชื่อ _, นามสกุล _ ชื่อ) รวม (id)
แต่เนื่องจากการใช้ฟังก์ชั่นและตารางนี้มีขนาดใหญ่จึงอาจไม่ใช่ทางออกที่ดีที่สุด
- การอัปเดตสถิติโดยใช้การคอมไพล์ใหม่เพื่อลองและรับแผนที่ดีกว่า
- การเพิ่ม OPTION
(HASH JOIN, MERGE JOIN)
ให้กับแบบสอบถาม
- ...
ทดสอบข้อมูล + คำค้นหาที่ใช้
CREATE TABLE #smallTableOfPeople(importantValue int, birthDate datetime2, first_name varchar(50),last_name varchar(50));
CREATE TABLE #largeTableOfPeople(importantValue int, birth_date datetime2, first_name varchar(50),last_name varchar(50));
set nocount on;
DECLARE @i int = 1
WHILE @i <= 1000
BEGIN
insert into #smallTableOfPeople (importantValue,birthDate,first_name,last_name)
VALUES(NULL, dateadd(mi,@i,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @i += 1;
END
set nocount on;
DECLARE @j int = 1
WHILE @j <= 20000
BEGIN
insert into #largeTableOfPeople (importantValue,birth_Date,first_name,last_name)
VALUES(@j, dateadd(mi,@j,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @j += 1;
END
SET STATISTICS IO, TIME ON;
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å');
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
--AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å')
drop table #largeTableOfPeople;
drop table #smallTableOfPeople;
AND LEFT(TRIM(largeTbl.last_name), 1) BETWEEN 'a' AND 'z' COLLATE LATIN1_GENERAL_CI_AI
ควรทำในสิ่งที่คุณต้องการโดยไม่ต้องให้คุณแสดงรายการอักขระทั้งหมดและมีรหัสที่อ่านยาก