Найефективніший спосіб отримання діапазонів дат


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

Це важко вирішити проблему загалом, але є кілька речей, які ми можемо зробити, щоб допомогти оптимізатору вибрати план. Цей сценарій створює таблицю з 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 з пакетом оновлень 1 або пізнішої версії 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 може отримати вбудовану підтримку інтервалів. Якщо це приходить з гарною статистичною підтримкою, розробники можуть трохи боятися налаштування планів запитів.


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-запитів за допомогою обмежень

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.