วิธีที่มีประสิทธิภาพมากที่สุดในการรับช่วงวันที่


16

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

create table SomeDateTable
(
    id int identity(1, 1) not null,
    StartDate datetime not null,
    EndDate datetime not null
)
go

สมมติว่าคุณต้องการช่วงสำหรับทั้งสองและStartDate EndDateดังนั้นในคำอื่น ๆ ถ้าStartDateตกอยู่ในระหว่าง@StartDateBeginและ@StartDateEndและEndDateตกอยู่ในระหว่าง@EndDateBeginและ@EndDateEndแล้วทำบางสิ่งบางอย่าง

ฉันรู้ว่ามีสองสามวิธีที่จะไปเกี่ยวกับเรื่องนี้ แต่สิ่งที่แนะนำมากที่สุดคืออะไร?

คำตอบ:


29

นี่เป็นปัญหาที่ยากในการแก้ปัญหาโดยทั่วไป แต่มีสองสิ่งที่เราสามารถทำได้เพื่อช่วยให้เครื่องมือเพิ่มประสิทธิภาพเลือกแผน สคริปต์นี้สร้างตารางที่มี 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คอลัมน์เพื่อเพิ่มประสิทธิภาพอย่างน้อยสามารถเลือกได้ว่าจะแสวงหาบนหรือStartDateEndDate

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 อาจได้รับการสนับสนุนเป็นระยะ หากสิ่งนั้นมาพร้อมกับการสนับสนุนทางสถิติที่ดีนักพัฒนาอาจกลัวแผนการสืบค้นที่ปรับแต่งเช่นนี้น้อย


5

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

SELECT  TaskId ,    
        TaskDescription ,
        StartedAt ,    
        FinishedAt    
FROM    dbo.Tasks    
WHERE   '20101203' BETWEEN StartedAt AND FinishedAt

เราสามารถเพิ่มอีกหนึ่งเงื่อนไข:

SELECT  TaskId ,    
        TaskDescription ,
        StartedAt ,    
        FinishedAt    
FROM    dbo.Tasks    
WHERE   '20101203' BETWEEN StartedAt AND FinishedAt
    AND StartedAt >= '20101202'
    AND FinishedAt <= '20101204' ;

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.