这两个查询在逻辑上是等价的吗?

Alf*_*f47 10 performance sql-server query query-refactor query-performance

这两个查询在逻辑上是等价的吗?

DECLARE @DateTime DATETIME = GETDATE()
Run Code Online (Sandbox Code Playgroud)

查询 1

SELECT *
FROM   MyTable
WHERE  Datediff(DAY, LogInsertTime, @DateTime) > 7   
Run Code Online (Sandbox Code Playgroud)

查询 2

SELECT *
FROM   MyTable
WHERE  LogInsertTime < @DateTime - 7 
Run Code Online (Sandbox Code Playgroud)

如果它们在逻辑上不是等价的,您能否给我第一个查询的逻辑等价,以便 WHERE 子句可以有效地使用索引(即消除函数包装)?

Aar*_*and 15

您发布的两个查询在逻辑上是否相同无关紧要;你不应该使用它们中的任何一个。我将尝试引导您远离以下几件事:

  1. 只要有可能,尽量避免将函数应用于列。保持这些计算针对常量而不是列总是一样好,而且通常更好 - 这会破坏 SARGability 并使这些列上的索引无用。在这种情况下,我更喜欢查询 2,特别是如果LogDateTime已编入索引(或可能曾经编入索引)。
  2. 我不喜欢速记日期数学,我建议反对它。当然,打字速度更快,但尝试使用DATE数据类型,你会得到一个丑陋的错误。最好把它拼出来,例如:

    WHERE LogInsertTime < DATEADD(DAY, -7, @DateTime);
    
    Run Code Online (Sandbox Code Playgroud)


A-K*_*A-K 8

我会使用以下 sargeable 查询:

SELECT * FROM MyTable WHERE LogInsertTime < DATEADD(DAY, -7, @DateTime)
Run Code Online (Sandbox Code Playgroud)

原因:我认为 @DateTime-7 的结果没有记录。即使它恰好相当于 DATEADD(DAY, -7, @DateTime),它也可能在以后的版本中中断。

  • 事实上,它是[记录和明确定义的](http://msdn.microsoft.com/en-us/library/ms189518.aspx):`-(减法):减去两个数字(算术减法运算符) . 也可以从日期中减去一个数字,以天为单位。`。尽管如此,我同意使用显式日期函数使结果查询比“算术运算符魔术”更具可读性和可维护性。 (2认同)

The*_*ter 6

它们不是等价的。7 天前但在当前时间之前的记录 - 只会在查询 #2 中返回:

使用该DATEADD函数比较天数时,不考虑时间部分。在比较星期日和星期一时,无论时间如何,该函数都将返回 1。

演示:

DECLARE @MyTable TABLE(pk INT, LogInsertTime DATETIME);

INSERT @MyTable
VALUES (1, DATEADD(HOUR, 1, CAST(DATEADD(DAY, -7, CAST (GETDATE() AS DATE))AS DATETIME))),
(2, DATEADD(HOUR, 23, CAST(DATEADD(DAY, -7, CAST (GETDATE() AS DATE)) AS DATETIME)));

DECLARE @DateTime DATETIME = GETDATE();

SELECT *
FROM @MyTable
WHERE DATEDIFF(DAY, LogInsertTime, @DateTime) > 7;

-- 0 records.

SELECT *
FROM @MyTable
WHERE LogInsertTime < @DateTime - 7;
-- 1 record.
Run Code Online (Sandbox Code Playgroud)

将启用潜在索引使用的第一个查询的逻辑等效项是删除时间部分@DateTime或将时间设置为0:00:00

SELECT *
FROM @MyTable
WHERE LogInsertTime < CAST(@DateTime - 7 AS DATE);
Run Code Online (Sandbox Code Playgroud)

第一个查询不能使用索引LogInsertTime的原因是因为该列隐藏在一个函数中。查询 #2 将列与常量值进行比较,这使优化器能够选择 上的索引LogInsertTime