นี่เป็นปัญหาที่ยากในการแก้ปัญหาโดยทั่วไป แต่มีสองสิ่งที่เราสามารถทำได้เพื่อช่วยให้เครื่องมือเพิ่มประสิทธิภาพเลือกแผน สคริปต์นี้สร้างตารางที่มี 10,000 แถวพร้อมการกระจายแบบสุ่มหลอกที่รู้จักกันเพื่อแสดง:
CREATE TABLE dbo.SomeDateTable
(
Id INTEGER IDENTITY(1, 1) PRIMARY KEY NOT NULL,
StartDate DATETIME NOT NULL,
EndDate DATETIME NOT NULL
);
GO
SET STATISTICS XML OFF
SET NOCOUNT ON;
DECLARE
@i INTEGER = 1,
@s FLOAT = RAND(20120104),
@e FLOAT = RAND();
WHILE @i <= 10000
BEGIN
INSERT dbo.SomeDateTable
(
StartDate,
EndDate
)
VALUES
(
DATEADD(DAY, @s * 365, {d '2009-01-01'}),
DATEADD(DAY, @s * 365 + @e * 14, {d '2009-01-01'})
)
SELECT
@s = RAND(),
@e = RAND(),
@i += 1
END
คำถามแรกคือวิธีการจัดทำดัชนีตารางนี้ ทางเลือกหนึ่งคือการให้สองดัชนีในDATETIME
คอลัมน์เพื่อเพิ่มประสิทธิภาพอย่างน้อยสามารถเลือกได้ว่าจะแสวงหาบนหรือStartDate
EndDate
CREATE INDEX nc1 ON dbo.SomeDateTable (StartDate, EndDate)
CREATE INDEX nc2 ON dbo.SomeDateTable (EndDate, StartDate)
ความไม่เท่าเทียมกันของทั้งสองStartDate
และEndDate
หมายความว่ามีเพียงคอลัมน์เดียวในแต่ละดัชนีที่สามารถรองรับการสืบค้นในแบบสอบถามตัวอย่าง แต่นี่เป็นสิ่งที่ดีที่สุดที่เราสามารถทำได้ เราอาจพิจารณาสร้างคอลัมน์ที่สองในแต่ละดัชนีINCLUDE
แทนที่จะเป็นคีย์ แต่เราอาจมีข้อความค้นหาอื่น ๆ ที่สามารถทำการค้นหาความเสมอภาคในคอลัมน์นำและการค้นหาความไม่เท่าเทียมกันในคอลัมน์ที่สอง นอกจากนี้เราอาจได้รับสถิติที่ดีขึ้นด้วยวิธีนี้ อย่างไรก็ตาม...
DECLARE
@StartDateBegin DATETIME = {d '2009-08-01'},
@StartDateEnd DATETIME = {d '2009-10-15'},
@EndDateBegin DATETIME = {d '2009-08-05'},
@EndDateEnd DATETIME = {d '2009-10-22'}
SELECT
COUNT_BIG(*)
FROM dbo.SomeDateTable AS sdt
WHERE
sdt.StartDate BETWEEN @StartDateBegin AND @StartDateEnd
AND sdt.EndDate BETWEEN @EndDateBegin AND @EndDateEnd
แบบสอบถามนี้ใช้ตัวแปรดังนั้นโดยทั่วไปเพิ่มประสิทธิภาพจะคาดเดาการคัดสรรและการกระจายส่งผลให้ประมาณการ cardinality เดาของ81 แถว ในความเป็นจริงแบบสอบถามสร้าง 2076 แถวซึ่งเป็นความแตกต่างที่อาจมีความสำคัญในตัวอย่างที่ซับซ้อนมากขึ้น
บน SQL Server 2008 SP1 CU5 หรือใหม่กว่า (หรือ R2 RTM CU1) เราสามารถใช้ประโยชน์จากการเพิ่มประสิทธิภาพการฝังพารามิเตอร์เพื่อให้ได้ค่าประมาณที่ดีขึ้นเพียงแค่เพิ่มOPTION (RECOMPILE)
ลงในSELECT
แบบสอบถามด้านบน สิ่งนี้ทำให้การรวบรวมก่อนที่แบตช์จะดำเนินการทำให้ SQL Server 'ดู' ค่าพารามิเตอร์จริงและปรับให้เหมาะสมสำหรับสิ่งเหล่านั้น ด้วยการเปลี่ยนแปลงนี้การประมาณจะดีขึ้นเป็น468 แถว (แม้ว่าคุณจะต้องตรวจสอบแผนรันไทม์เพื่อดูสิ่งนี้) การประมาณนี้ดีกว่า 81 แถว แต่ก็ยังไม่ปิดทั้งหมด ส่วนขยายการสร้างแบบจำลองที่เปิดใช้งานโดยการตั้งค่าสถานะติดตาม 2301อาจช่วยได้ในบางกรณี แต่ไม่ใช่สำหรับการสืบค้นนี้
ปัญหาคือที่ที่แถวที่ผ่านการรับรองจากทั้งสองช่วงค้นหาทับซ้อนกัน หนึ่งในข้อสันนิษฐานที่ทำให้เข้าใจได้ง่ายที่สุดในองค์ประกอบการคิดต้นทุนและการประมาณค่าของเครื่องมือเพิ่มประสิทธิภาพคือภาคแสดงผลมีความเป็นอิสระ ) เมื่อความสัมพันธ์แบบนี้เป็นปัญหาเรามักจะสามารถแก้ไขได้ด้วยสถิติแบบหลายคอลัมน์และ / หรือการกรอง ด้วยสองช่วงที่ไม่ทราบจุดเริ่มต้นและจุดสิ้นสุดสิ่งนี้จะไม่สามารถทำได้ นี่คือที่บางครั้งเราต้องหันไปเขียนใหม่แบบสอบถามที่เกิดขึ้นเพื่อให้ประมาณการดีขึ้น:
SELECT COUNT(*) FROM
(
SELECT
sdt.Id
FROM dbo.SomeDateTable AS sdt
WHERE
sdt.StartDate BETWEEN @StartDateBegin AND @StartDateEnd
INTERSECT
SELECT
sdt.Id
FROM dbo.SomeDateTable AS sdt
WHERE
sdt.EndDate BETWEEN @EndDateBegin AND @EndDateEnd
) AS intersected (id)
OPTION (RECOMPILE)
แบบฟอร์มนี้เกิดขึ้นเพื่อสร้างค่าประมาณรันไทม์ของ 2,610 แถว (เทียบกับ 2076 จริง) ถ้าคุณไม่ได้เปิด TF 2301 ในกรณีนี้เทคนิคการสร้างแบบจำลองขั้นสูงจะมองเห็นเคล็ดลับและสร้างการประมาณการแบบเดียวกันกับก่อนหน้านี้: 468 แถว
วันหนึ่งของ SQL Server อาจได้รับการสนับสนุนเป็นระยะ หากสิ่งนั้นมาพร้อมกับการสนับสนุนทางสถิติที่ดีนักพัฒนาอาจกลัวแผนการสืบค้นที่ปรับแต่งเช่นนี้น้อย