У меня есть интересный вопрос о SARGability. В данном случае речь идет об использовании предиката разницы между двумя столбцами даты. Вот настройки:
USE [tempdb]
SET NOCOUNT ON
IF OBJECT_ID('tempdb..#sargme') IS NOT NULL
BEGIN
DROP TABLE #sargme
END
SELECT TOP 1000
IDENTITY (BIGINT, 1,1) AS ID,
CAST(DATEADD(DAY, [m].[severity] * -1, GETDATE()) AS DATE) AS [DateCol1],
CAST(DATEADD(DAY, [m].[severity], GETDATE()) AS DATE) AS [DateCol2]
INTO #sargme
FROM sys.[messages] AS [m]
ALTER TABLE [#sargme] ADD CONSTRAINT [pk_whatever] PRIMARY KEY CLUSTERED ([ID])
CREATE NONCLUSTERED INDEX [ix_dates] ON [#sargme] ([DateCol1], [DateCol2])
То, что я вижу довольно часто, выглядит примерно так:
/*definitely not sargable*/
SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
[#sargme] AS [s]
WHERE
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2]) >= 48;
... что определенно не SARGable. Это приводит к сканированию индекса, читает все 1000 строк, ничего хорошего. Расчетные ряды воняют. Ты бы никогда не запустил это в производство.
Было бы хорошо, если бы мы могли материализовать CTE, потому что это помогло бы нам сделать это, ну, в общем, более САРГЕЙЛЬНО, технически говоря. Но нет, мы получаем тот же план выполнения, что и наверху.
/*would be nice if it were sargable*/
WITH [x] AS ( SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2]) AS [ddif]
FROM
[#sargme] AS [s])
SELECT
*
FROM
[x]
WHERE
[x].[ddif] >= 48;
И, конечно, поскольку мы не используем константы, этот код ничего не меняет и даже не наполовину SARGable. Не весело. Тот же план выполнения.
/*not even half sargable*/
SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
[#sargme] AS [s]
WHERE
[s].[DateCol2] >= DATEADD(DAY, 48, [s].[DateCol1])
Если вам повезло, и вы соблюдаете все параметры ANSI SET в строках подключения, вы можете добавить вычисляемый столбец и выполнить поиск по нему ...
ALTER TABLE [#sargme] ADD [ddiff] AS
DATEDIFF(DAY, DateCol1, DateCol2) PERSISTED
CREATE NONCLUSTERED INDEX [ix_dates2] ON [#sargme] ([ddiff], [DateCol1], [DateCol2])
SELECT [s].[ID] ,
[s].[DateCol1] ,
[s].[DateCol2]
FROM [#sargme] AS [s]
WHERE [ddiff] >= 48
Это даст вам поиск по индексу с тремя запросами. Странно, что мы добавляем 48 дней к DateCol1. Запрос DATEDIFF
в WHERE
предложении, CTE
и окончательный запрос с предикатом в вычисляемом столбце дают вам гораздо более приятный план с гораздо более хорошими оценками, и все такое.
Что приводит меня к вопросу: есть ли в одном запросе SARGable способ выполнить этот поиск?
Никаких временных таблиц, никаких табличных переменных, никаких изменений структуры таблицы и никаких представлений.
Я в порядке с самостоятельными объединениями, CTE, подзапросами или несколькими проходами по данным. Может работать с любой версией SQL Server.
Избегать вычисляемого столбца - это искусственное ограничение, потому что меня больше интересует решение для запросов, чем все остальное.
источник