เมื่อเข้าร่วมตารางต้นแบบเข้ากับตารางรายละเอียดฉันจะสนับสนุน SQL Server 2014 ให้ใช้การประมาณค่าเชิงการนับของตารางขนาดใหญ่ (รายละเอียด) เป็นการประมาณค่าความสำคัญของการรวมเอาต์พุตได้อย่างไร
ตัวอย่างเช่นเมื่อเข้าร่วมแถวหลัก 10K ถึงแถวรายละเอียด 100K ฉันต้องการให้ SQL Server ประมาณค่าการเข้าร่วมที่แถว 100K - เหมือนกับจำนวนแถวรายละเอียดโดยประมาณ ฉันควรจัดโครงสร้างคิวรีและ / หรือตารางและ / หรือดัชนีของฉันอย่างไรเพื่อช่วยให้ตัวประมาณของ SQL Server ใช้ประโยชน์จากข้อเท็จจริงที่ว่าทุกแถวรายละเอียดมีแถวหลักที่สอดคล้องกันเสมอ (หมายความว่าการเข้าร่วมระหว่างพวกเขาไม่ควรลดค่าประมาณของ cardinality)
นี่คือรายละเอียดเพิ่มเติม ฐานข้อมูลของเรามีคู่ของตารางต้นแบบ / รายละเอียด: VisitTarget
มีหนึ่งแถวสำหรับแต่ละธุรกรรมการขายและVisitSale
มีหนึ่งแถวสำหรับแต่ละผลิตภัณฑ์ในแต่ละธุรกรรม มันเป็นความสัมพันธ์แบบหนึ่งต่อหลายคน: หนึ่งแถว VisitTarget สำหรับแถว VisitSale เฉลี่ย 10 แถว
ตารางมีลักษณะดังนี้: (ฉันลดความซับซ้อนของคอลัมน์ที่เกี่ยวข้องสำหรับคำถามนี้เท่านั้น)
-- "master" table
CREATE TABLE VisitTarget
(
VisitTargetId int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
SaleDate date NOT NULL,
StoreId int NOT NULL
-- other columns omitted for clarity
);
-- covering index for date-scoped queries
CREATE NONCLUSTERED INDEX IX_VisitTarget_SaleDate
ON VisitTarget (SaleDate) INCLUDE (StoreId /*, ...more columns */);
-- "detail" table
CREATE TABLE VisitSale
(
VisitSaleId int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
VisitTargetId int NOT NULL,
SaleDate date NOT NULL, -- denormalized; copied from VisitTarget
StoreId int NOT NULL, -- denormalized; copied from VisitTarget
ItemId int NOT NULL,
SaleQty int NOT NULL,
SalePrice decimal(9,2) NOT NULL
-- other columns omitted for clarity
);
-- covering index for date-scoped queries
CREATE NONCLUSTERED INDEX IX_VisitSale_SaleDate
ON VisitSale (SaleDate)
INCLUDE (VisitTargetId, StoreId, ItemId, SaleQty, TotalSalePrice decimal(9,2) /*, ...more columns */
);
ALTER TABLE VisitSale
WITH CHECK ADD CONSTRAINT FK_VisitSale_VisitTargetId
FOREIGN KEY (VisitTargetId)
REFERENCES VisitTarget (VisitTargetId);
ALTER TABLE VisitSale
CHECK CONSTRAINT FK_VisitSale_VisitTargetId;
ด้วยเหตุผลด้านประสิทธิภาพเราได้ทำให้บางส่วนเป็นปกติโดยคัดลอกคอลัมน์ตัวกรองที่พบบ่อยที่สุด (เช่นSaleDate
) จากตารางต้นแบบไปยังแถวตารางรายละเอียดแต่ละแถวจากนั้นเราเพิ่มดัชนีที่ครอบคลุมลงในทั้งสองตารางเพื่อรองรับแบบสอบถามที่กรองวันที่ได้ดีขึ้น วิธีนี้ใช้งานได้ดีในการลด I / O เมื่อเรียกใช้แบบสอบถามที่กรองวันที่ แต่ฉันคิดว่าวิธีการนี้ทำให้เกิดปัญหาการประเมิน cardinality เมื่อเข้าร่วมตารางต้นแบบและตารางรายละเอียดด้วยกัน
เมื่อเราเข้าร่วมสองตารางเหล่านี้แบบสอบถามจะมีลักษณะดังนี้:
SELECT vt.StoreId, vt.SomeOtherColumn, Sales = sum(vs.SalePrice*vs.SaleQty)
FROM VisitTarget vt
JOIN VisitSale vs on vt.VisitTargetId = vs.VisitTargetId
WHERE
vs.SaleDate BETWEEN '20170101' and '20171231'
and vt.SaleDate BETWEEN '20170101' and '20171231'
-- more filtering goes here, e.g. by store, by product, etc.
ตัวกรองวันที่บนตารางรายละเอียด ( VisitSale
) ซ้ำซ้อน อยู่ที่นั่นเพื่อเปิดใช้งาน I / O ตามลำดับ (ตัวดำเนินการค้นหาดัชนี) บนตารางรายละเอียดสำหรับข้อความค้นหาที่กรองตามช่วงวันที่
แผนสำหรับข้อความค้นหาประเภทนี้มีลักษณะดังนี้:
แผนการที่เกิดขึ้นจริงของแบบสอบถามที่มีปัญหาเดียวกันสามารถพบได้ที่นี่
อย่างที่คุณเห็นการประมาณค่าความสำคัญของการเข้าร่วม (คำแนะนำเครื่องมือในมุมซ้ายล่างของภาพ) สูงกว่า 4x ต่ำเกินไป: 2.1M จริงกับ 0.5M โดยประมาณ ซึ่งทำให้เกิดปัญหาประสิทธิภาพ (เช่น spilling to tempdb) โดยเฉพาะเมื่อแบบสอบถามนี้เป็นแบบสอบถามย่อยที่ใช้ในแบบสอบถามที่ซับซ้อนมากขึ้น
แต่การประมาณจำนวนแถวสำหรับแต่ละสาขาของการรวมจะใกล้เคียงกับจำนวนแถวจริง ครึ่งบนของการเข้าร่วมคือ 100K จริงกับ 164K โดยประมาณ ครึ่งล่างของการเข้าร่วมคือแถว 2.1M จริงกับ 3.7M โดยประมาณ การกระจายตัวของแฮชถังยังดูดี ข้อสังเกตเหล่านี้แนะนำให้ฉันทราบว่าสถิตินั้นใช้ได้สำหรับแต่ละตารางและปัญหาคือการประมาณความเข้าร่วมของ cardinality
ตอนแรกฉันคิดว่าปัญหาคือ SQL Server ที่คาดหวังว่าคอลัมน์ SaleDate ในแต่ละตารางมีความเป็นอิสระในขณะที่พวกเขาเหมือนกันจริงๆ ดังนั้นฉันจึงพยายามเพิ่มการเปรียบเทียบความเท่าเทียมกันสำหรับวันที่ลดราคาลงในเงื่อนไขการเข้าร่วมหรือข้อ WHERE เช่น
ON vt.VisitTargetId = vs.VisitTargetId and vt.SaleDate = vs.SaleDate
หรือ
WHERE vt.SaleDate = vs.SaleDate
สิ่งนี้ไม่ทำงาน มันยังทำให้การประเมินความสำคัญของหัวใจแย่ลง! ดังนั้น SQL Server ไม่ได้ใช้คำแนะนำความเท่าเทียมกันนั้นหรืออย่างอื่นเป็นสาเหตุของปัญหา
มีความคิดเห็นใด ๆ สำหรับวิธีการแก้ไขปัญหาและหวังว่าจะแก้ไขปัญหาการประมาณค่า cardinality นี้หรือไม่? เป้าหมายของฉันคือความสำคัญของการเข้าร่วมของมาสเตอร์ / รายละเอียดที่จะประมาณเช่นเดียวกับการประมาณสำหรับอินพุต ("ตารางรายละเอียด") ขนาดใหญ่ของการเข้าร่วม
หากเป็นเรื่องสำคัญเรากำลังเรียกใช้ SQL Server 2014 Enterprise SP2 CU8 บิลด์ 12.0.5557.0 บน Windows Server ไม่มีการเปิดใช้งานค่าสถานะการสืบค้นกลับ ระดับความเข้ากันได้ของฐานข้อมูลคือ SQL Server 2014 เราเห็นการทำงานแบบเดียวกันกับเซิร์ฟเวอร์ SQL ที่แตกต่างกันหลายตัวดังนั้นจึงดูเหมือนว่าจะไม่เกิดปัญหาเฉพาะเซิร์ฟเวอร์
มีการเพิ่มประสิทธิภาพในเครื่องมือประมาณการ Cardinality ของ SQL Server 2014ที่เป็นพฤติกรรมที่ฉันกำลังมองหา:
อย่างไรก็ตาม CE ใหม่ใช้อัลกอริทึมที่ง่ายกว่าซึ่งสันนิษฐานว่ามีการเชื่อมโยงการเข้าร่วมแบบหนึ่งต่อกลุ่มระหว่างตารางขนาดใหญ่และตารางขนาดเล็ก สมมติว่าแต่ละแถวในตารางขนาดใหญ่ตรงกับหนึ่งแถวในตารางขนาดเล็ก อัลกอริทึมนี้ส่งคืนขนาดโดยประมาณของอินพุตที่ใหญ่กว่าว่าเป็น cardinality ที่เข้าร่วม
เป็นการดีที่ฉันจะได้รับพฤติกรรมนี้โดยที่ cardinality ประมาณการสำหรับการเข้าร่วมจะเป็นเช่นเดียวกับการประมาณการสำหรับตารางที่มีขนาดใหญ่แม้ว่าตาราง "เล็ก" ของฉันจะยังคงส่งคืนมากกว่า 100K แถว!