私には、SARGabilityに関する興味深い質問があります。この場合、2つの日付列の違いに関する述語を使用することです。セットアップは次のとおりです。
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;
...これは間違いなくSARGではありません。その結果、インデックススキャンが行われ、1000行すべてが読み取られます。推定行が悪臭を放ちます。これを本番環境に配置することはありません。
CTEを具体化できればいいと思います。それは、技術的に言えば、これをもっとSARGできるようにするのに役立つからです。しかし、いいえ、トップと同じ実行計画を取得します。
/*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;
そしてもちろん、定数を使用していないので、このコードは何も変更せず、SARGの半分でもありません。楽しくない。同じ実行計画。
/*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
これにより、3つのクエリでインデックスシークが取得されます。奇妙な人は、DateCol1に48日を追加するところです。句DATEDIFF
内のクエリ、、および計算列の述語を含む最終クエリはすべて、より優れた推定値を備えた優れたプランを提供します。WHERE
CTE
それは私に質問をもたらします:単一のクエリで、この検索を実行するためのSARGableな方法はありますか?
一時テーブル、テーブル変数、テーブル構造の変更、ビューはありません。
自己結合、CTE、サブクエリ、またはデータの複数パスで問題ありません。SQL Serverの任意のバージョンで動作できます。
計算列を避けることは人為的な制限です。なぜなら、私は他の何よりもクエリソリューションに関心があるからです。