设为首页 - 加入收藏 华夏网 ()- 云主机,资讯,互联网,人工智能,云计算,大赢家论坛,区块链,VR,站长网!
热搜: 系统 统一 2017 市场
当前位置: 主页 > 水果奶奶论坛 > 正文

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

发布时间:2021-01-14 09:13 所属栏目:[水果奶奶论坛] 来源:网络整理
导读:对于我来说,对于SARGability来说,我有一个有趣的问题.在这种情况下,它是关于在两个日期列之间使用谓词.这是设置: USE [tempdb]SET NOCOUNT ON IF OBJECT_ID('tempdb..#sargme') IS NOT NULLBEGINDROP TABLE #sargmeENDSELECT TOP 1000IDENTITY (BIGINT,1,1)

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

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

【免责声明】本站内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

网友评论
推荐文章