Mam interesujące pytanie dotyczące SARGability. W tym przypadku chodzi o użycie predykatu na podstawie różnicy między dwiema kolumnami dat. Oto konfiguracja:
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])
To, co często widuję, to coś takiego:
/*definitely not sargable*/
SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
[#sargme] AS [s]
WHERE
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2]) >= 48;
... co zdecydowanie nie jest SARGable. Powoduje to skanowanie indeksu, odczytuje wszystkie 1000 wierszy, co nie jest dobre. Szacowane rzędy śmierdzą. Nigdy nie wprowadziłbyś tego do produkcji.
Byłoby miło, gdybyśmy mogli zmaterializować CTE, ponieważ pomogłoby nam to uczynić to, cóż, bardziej SARGable, technicznie rzecz biorąc. Ale nie, otrzymujemy ten sam plan wykonania, co do góry.
/*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;
I oczywiście, ponieważ nie używamy stałych, ten kod nic nie zmienia, a nawet nie jest w połowie SARGable. Nie ma zabawy. Ten sam plan wykonania.
/*not even half sargable*/
SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
[#sargme] AS [s]
WHERE
[s].[DateCol2] >= DATEADD(DAY, 48, [s].[DateCol1])
Jeśli masz szczęście i przestrzegasz wszystkich opcji ANSI SET w ciągach połączeń, możesz dodać kolumnę obliczoną i wyszukać ją ...
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
Otrzymasz wyszukiwanie indeksu z trzema zapytaniami. Dziwny człowiek obecnie dodaje 48 dni do DateCol1. Zapytanie ze DATEDIFF
w WHERE
klauzuli, w miejsce CTE
, a ostateczna kwerendy z predykat na kolumnie komputerowej wszystkim daje dużo ładniejszy planu ze znacznie szacunków ładniejszych, i to wszystko.
Co prowadzi mnie do pytania: czy w jednym zapytaniu istnieje SARGable sposób na przeprowadzenie tego wyszukiwania?
Bez tabel tymczasowych, bez zmiennych tabel, bez zmiany struktury tabeli i bez widoków.
Nie przeszkadzają mi samodzielne połączenia, CTE, podzapytania lub wielokrotne przekazywanie danych. Może współpracować z dowolną wersją SQL Server.
Unikanie kolumny obliczeniowej jest sztucznym ograniczeniem, ponieważ bardziej interesuje mnie rozwiązanie do zapytań niż cokolwiek innego.