sql-server – 两个日期列的SARGable WHERE子句

前端之家收集整理的这篇文章主要介绍了sql-server – 两个日期列的SARGable WHERE子句前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
对于我来说,对于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方法来执行此搜索

没有临时表,没有表变量,没有更改表结构,也没有视图.

我对自连接,CTE,子查询或多次传递数据很好.可以使用任何版本的sql Server.

避免计算列是一个人为的限制因为我对查询解决方案比对其他任何东西更感兴趣.

解决方法

只需快速添加,以便它作为答案存在(虽然我知道这不是你想要的答案).

索引计算列通常是此类问题的正确解决方案.

它:

>使谓词成为可转换表达式
>允许创建自动统计数据以更好地进行基数估算
>不需要在基表中占用任何空间

要明确最后一点,在这种情况下不需要保留计算列:

-- 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所说的那样,所有这一切才真正重要(代码挑战除外).

考虑这个很有趣,但我不知道在问题中有什么限制可以合理地实现你想要的东西.似乎任何最佳解决方案都需要某种类型的新数据结构;我们最接近的是由上面的非持久计算列上的索引提供的“函数索引”近似.

猜你在找的MsSQL相关文章