it-swarm.asia

الطريقة الأكثر فعالية لاسترداد نطاقات التاريخ

ما الطريقة الأكثر فعالية لاسترداد النطاقات الزمنية باستخدام بنية جدول مثل هذه؟

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، ثم تفعل شيئا.

أعلم أن هناك عدة طرق لتحقيق ذلك على الأرجح ، ولكن ما هي أكثر النصائح الموصى بها؟

16
Thomas Stringer

هذه مشكلة يصعب حلها بشكل عام ، ولكن هناك شيئان يمكننا القيام به لمساعدة المُحسّن على اختيار خطة. ينشئ هذا البرنامج النصي جدولًا يحتوي على 10000 صف بتوزيع عشوائي عشوائي للصفوف لتوضيح:

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

يستخدم هذا الاستعلام المتغيرات ، لذلك بشكل عام سيخمن المحسن الانتقائية والتوزيع ، مما يؤدي إلى تقدير الكاردينال التخميني 81 صفًا . في الواقع ، ينتج الاستعلام 2076 صفًا ، وهو تناقض قد يكون مهمًا في مثال أكثر تعقيدًا.

في SQL Server 2008 SP1 CU5 أو أحدث (أو R2 RTM CU1) يمكننا الاستفادة من تحسين تضمين المعلمات للحصول على تقديرات أفضل ، ببساطة عن طريق إضافة OPTION (RECOMPILE) إلى استعلام SELECT أعلاه. يؤدي هذا إلى تجميع قبل تنفيذ الدفعة مباشرة ، مما يسمح لـ SQL Server "برؤية" قيم المعلمات الحقيقية وتحسينها. مع هذا التغيير ، يتحسن التقدير إلى 468 صفًا (على الرغم من أنك تحتاج إلى التحقق من خطة وقت التشغيل لرؤية ذلك). هذا التقدير أفضل من 81 صفًا ، ولكن لا يزال ليس كل ذلك قريبًا. تم تمكين ملحقات النمذجة بواسطة إشارة التتبع 2301 في بعض الحالات ، ولكن ليس مع هذا الاستعلام.

تكمن المشكلة في تداخل الصفوف المؤهلة من خلال عمليتي بحث النطاق. أحد الافتراضات المبسطة التي تم إجراؤها في مكوِّن التكلفة وتقدير الكاردينال الخاص بالمحسّن هو أن المسندات مستقلة (لذلك إذا كان كلاهما انتقائيًا بنسبة 50٪ ، فإن نتيجة تطبيق كلاهما يفترض أن يؤهل 50٪ من 50٪ = 25٪ من الصفوف ). عندما يكون هذا النوع من الارتباط مشكلة ، يمكننا غالبًا العمل حوله باستخدام إحصائيات متعددة الأعمدة و/أو تمت تصفيتها. مع نطاقين بنقاط بداية ونهاية غير معروفة ، يصبح هذا غير عملي. هذا هو المكان الذي نلجأ إليه أحيانًا لإعادة كتابة الاستعلام إلى نموذج يحدث لإنتاج تقدير أفضل:

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)

يحدث هذا النموذج لإنتاج تقدير وقت التشغيل لـ 2110 صفًا (مقابل 2076 فعليًا). ما لم يكن لديك TF 2301 قيد التشغيل ، في هذه الحالة ، ترى تقنيات النمذجة الأكثر تقدمًا من خلال الحيلة وتنتج نفس التقدير تمامًا كما كان من قبل: 468 صفًا.

قد يحصل SQL Server يومًا على دعم أصلي للفواصل الزمنية. إذا كان ذلك يأتي مع دعم إحصائي جيد ، فقد يخشى المطورون خطط الاستعلام عن الضبط مثل هذا أقل قليلاً.

29
Paul White 9

لا أعرف حلاً سريعًا لجميع توزيعات البيانات ، ولكن إذا كانت جميع نطاقاتك قصيرة ، فيمكننا عادةً تسريعها. إذا كانت النطاقات ، على سبيل المثال ، أقصر من يوم واحد ، بدلاً من هذا الاستعلام:

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 بمساعدة القيود

5
A-K