Самый эффективный способ получения диапазонов дат


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 (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 может получить встроенную поддержку для интервалов. Если это идет с хорошей статистической поддержкой, разработчики могут бояться настраивать планы запросов, подобные этому, чуть меньше.


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.