Ich habe für mich eine interessante Frage zu SARGability. In diesem Fall wird ein Prädikat für die Differenz zwischen zwei Datumsspalten verwendet. Hier ist das Setup:
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])
Was ich ziemlich oft sehe, ist ungefähr so:
/*definitely not sargable*/
SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
[#sargme] AS [s]
WHERE
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2]) >= 48;
... was definitiv nicht SARGable ist. Es führt zu einem Index-Scan, liest alle 1000 Zeilen, nicht gut. Geschätzte Reihen stinken. Sie würden das nie in Produktion setzen.
Es wäre schön, wenn wir CTEs verwirklichen könnten, denn das würde uns dabei helfen, das technisch besser zu machen. Aber nein, wir bekommen den gleichen Ausführungsplan wie oben oben.
/*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;
Und da wir keine Konstanten verwenden, ändert dieser Code natürlich nichts und ist nicht einmal halb SARGable. Kein Spaß. Gleicher Ausführungsplan.
/*not even half sargable*/
SELECT
* ,
DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
[#sargme] AS [s]
WHERE
[s].[DateCol2] >= DATEADD(DAY, 48, [s].[DateCol1])
Wenn Sie sich glücklich schätzen und alle ANSI SET-Optionen in Ihren Verbindungszeichenfolgen befolgen, können Sie eine berechnete Spalte hinzufügen und danach suchen ...
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
Dadurch erhalten Sie eine Indexsuche mit drei Abfragen. Der seltsame Mann ist, wo wir DateCol1 48 Tage hinzufügen. Die Abfrage mit DATEDIFF
in der WHERE
Klausel, die CTE
und die abschließende Abfrage mit einem Prädikat in der berechneten Spalte geben Ihnen einen viel besseren Plan mit viel besseren Schätzungen und all dem.
Was mich zu der Frage bringt: Gibt es in einer einzelnen Abfrage eine SARGable-Methode, um diese Suche durchzuführen?
Keine temporären Tabellen, keine Tabellenvariablen, keine Änderung der Tabellenstruktur und keine Ansichten.
Selbstverknüpfungen, CTEs, Unterabfragen oder mehrere Durchgänge über die Daten sind in Ordnung. Kann mit jeder Version von SQL Server arbeiten.
Das Vermeiden der berechneten Spalte ist eine künstliche Einschränkung, da ich mehr an einer Abfragelösung als an irgendetwas anderem interessiert bin.