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

dba.stackexchange https://dba.stackexchange.com/questions/10113

  •  16-10-2019
  •  | 
  •  

Вопрос

Какой наиболее эффективный способ получить диапазоны дат с помощью такой структуры таблицы?

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, тогда сделай что-нибудь.

Я знаю, что есть несколько способов сделать это, но какой из них наиболее рекомендуется?

Это было полезно?

Решение

В целом решить эту проблему сложно, но есть несколько вещей, которые мы можем сделать, чтобы помочь оптимизатору выбрать план.Этот скрипт создает таблицу из 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

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

Другие советы

Я не знаю решение, которое быстро для всех распределений данных, но если все ваши диапазоны короткие, мы обычно можем ускорить его. Если, например, диапазоны короче, чем один день, вместо этого запроса:

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 с помощью ограничений

Лицензировано под: CC-BY-SA с атрибуция
Не связан с dba.stackexchange
scroll top