对于我来说,对于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],[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].[DateCol2]) >= 48;
……绝对不是SARGable.它导致索引扫描,读取所有1000行,没有好处.估计行很臭.你永远不会把它投入生产.
如果我们能够实现CTE将会很好,因为从技术上讲,这将有助于我们做出更好的SARG.但不,我们得到了同样的执行计划.
/*would be nice if it were sargable*/ WITH [x] AS ( SELECT *,[s].[DateCol2]) AS [ddif] FROM [#sargme] AS [s]) SELECT * FROM [x] WHERE [x].[ddif] >= 48;
当然,由于我们没有使用常量,因此这些代码没有任何改变,甚至不是SARGable的一半.没有什么好玩的.相同的执行计划.
/*not even half sargable*/ SELECT *,[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].[DateCol2] FROM [#sargme] AS [s] WHERE [ddiff] >= 48
这将为您提供三个查询的索引查找.奇怪的人是我们在DateCol1上添加48天的地方.在WHERE子句中使用DATEDIFF的查询,CTE以及在计算列上使用谓词的最终查询都可以为您提供更好的计划,并且具有更好的估计值,以及所有这些.
这让我想到了这个问题:在一个查询中,是否有一种SARGable方法来执行此搜索?
没有临时表,没有表变量,没有更改表结构,也没有视图.
解决方法
只需快速添加,以便它作为答案存在(虽然我知道这不是你想要的答案).
索引计算列通常是此类问题的正确解决方案.
它:
>使谓词成为可转换表达式
>允许创建自动统计数据以更好地进行基数估算
>不需要在基表中占用任何空间
要明确最后一点,在这种情况下不需要保留计算列:
-- Note: not PERSISTED,Metadata change only ALTER TABLE #sargme ADD DayDiff AS DATEDIFF(DAY,DateCol2); -- Index the expression CREATE NONCLUSTERED INDEX index_name ON #sargme (DayDiff) INCLUDE (DateCol1,DateCol2);
现在查询:
SELECT S.ID,S.DateCol1,S.DateCol2,S.DateCol2) FROM #sargme AS S WHERE DATEDIFF(DAY,S.DateCol2) >= 48;
……给出以下琐碎的计划:
正如Martin Smith所说,如果你有使用错误的set选项的连接,你可以创建一个常规列并使用触发器维护计算值.
如果有一个真正的问题需要解决,当然,正如Aaron在his answer所说的那样,所有这一切才真正重要(代码挑战除外).
考虑这个很有趣,但我不知道在问题中有什么限制可以合理地实现你想要的东西.似乎任何最佳解决方案都需要某种类型的新数据结构;我们最接近的是由上面的非持久计算列上的索引提供的“函数索引”近似.