Сканирование индекса при фильтрации по столбцу, рассчитанному как DATEADD из столбца с кластеризованным индексом - PullRequest
0 голосов
/ 14 июля 2020

Я озадачен, почему я получаю поиск кластерного индекса по запросу в вычисляемом столбце, который использует простую функцию DATEADD (которая должна быть c) по другому столбцу, который индексируется кластером.

Я может сохранить нематериализованный вычисляемый столбец (поскольку он предназначен для существующих огромных устаревших данных) и иметь возможность фильтровать по этому столбцу и иметь sql попадание в индекс? Мне нужно отфильтровать вычисляемый столбец (так как я не могу изменить запросы, и они должны запрашивать с правильным смещением).

Simplisti c пример определения:

-- table with calculated column
CREATE TABLE [dbo].[_test](
    [Dt] [datetime] NOT NULL
) ON [PRIMARY]
GO

--clustered index
CREATE CLUSTERED INDEX [Idx] ON [dbo].[_test]
(
    [Dt] ASC
) ON [PRIMARY]
GO



--simulation of legacy data
DECLARE @rdate DATE
DECLARE @startLoopID INT = 1
DECLARE @endLoopID INT = 1000

WHILE @startLoopID <= @endLoopID
BEGIN
    SET @rdate = DATEADD(Hour, ABS(CHECKSUM(NEWID()) % (365 * 24) ), '2020-01-01');
    SET @startLoopID = @startLoopID + 1;

    INSERT INTO [_test] (Dt)
    VALUES (@rdate);
END

--adding the calculated column with proper offset
alter table _test ADD [Dt_2] AS  DATEADD(MINUTE, 300, Dt) 

Пример выполнения: DECLARE @EndTime DATETIME2 (7) = '2020-07-10 00: 00: 00.000'

DECLARE @StartTime DATETIME2(7) = DATEADD(day, -12, @EndTime)

--select is performing seek
select * from _test
where 
 Dt > @StartTime AND Dt < @EndTime
 
--select is performing scan
select * from _test
where 
 Dt_2 > @StartTime AND Dt_2 < @EndTime

План запроса:

План запроса

При фильтрации по столбцу Dt я получаю ожидаемый поиск. При фильтрации по столбцу Dt_2, который вычисляется из Dt с помощью функции c, я получаю сканирование индекса. В реальном сценарии с большим объемом данных это сильно снижает производительность.

1 Ответ

1 голос
/ 14 июля 2020

Вам необходимо проиндексировать вычисляемый столбец, чтобы SQL мог выполнить поиск. Вычисляемый столбец оценивается только во время выбора, если он не сохраняется или не индексируется. Даже если бы он был помечен как persisted, его все равно нужно было бы сканировать без индекса. Тот факт, что выражение является детерминированным c и точным, означает, что оно может быть проиндексировано, но вам все равно нужно добавить индекс.

Ваш комментарий ниже указывает на то, что вы не можете добавить индекс в вычисляемый столбец. Но в этом конкретном случае он вам все равно не нужен для выполнения того же запроса, потому что ваш вычисляемый столбец добавляет постоянное смещение к исходному столбцу. Следовательно, константное выражение можно переместить в правую часть операции сравнения в самом запросе, и SQL по-прежнему сможет использовать исходный кластерный индекс.

То есть вместо создавая новый столбец, который равен Dt плюс некоторая константа, вы можете вычесть константу из правой части неравенства. Вместо:

alter table _test ADD [Dt_2] AS  DATEADD(MINUTE, 300, Dt);

DECLARE @EndTime DATETIME2(7) = '2020-07-10 00:00:00.000'
DECLARE @StartTime DATETIME2(7) = DATEADD(day, -12, @EndTime);

select * from _test
where 
 Dt_2 > @StartTime AND Dt_2 < @EndTime;

можно использовать:

--subtract 300 minutes from the @endTime parameter instead of adding 300 minutes to every value of Dt

DECLARE @EndTime DATETIME2(7) = dateadd(minute, -300, '2020-07-10 00:00:00.000');
DECLARE @StartTime DATETIME2(7) = DATEADD(day, -12, @EndTime);

select * from _test
where 
 Dt > @StartTime
AND Dt < @EndTime;
...