对于我来说,我有一个关于可保存性的有趣问题。在这种情况下,它是关于两个日期列之间的差异使用谓词。设置如下:
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;
...这绝对不可救药。它导致索引扫描,读取所有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;
当然,由于我们没有使用常量,因此此代码不会更改任何内容,甚至无法保存一半。没有乐趣。相同的执行计划。
/*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
这将为您提供三个查询的索引查找。奇怪的是,我们向DateCol1添加48天。与查询DATEDIFF
中WHERE
子句,CTE
以计算列上的谓词,并最终查询全部给你好得多估计更加美好的计划,以及所有。
这就引出了我的问题:在单个查询中,是否存在一种可以执行此搜索的SARGable方法?
没有临时表,没有表变量,没有更改表结构,也没有视图。
我可以使用自联接,CTE,子查询或对数据进行多次传递。可以使用任何版本的SQL Server。
避免计算列是人为的限制,因为我对查询解决方案比其他任何东西都更感兴趣。