8

这种情况是可悲的吗?

AND  DATEDIFF(month,p.PlayerStatusLastTransitionDate,@now) BETWEEN 1 AND 7)

我的经验法则是左侧的函数使条件不可分割。但在某些地方我读过 BETWEEN 子句是可分割的。 那么有人确定吗?

以供参考:

注意:如果有任何大师在这里结束,请更新 Sargable Wikipedia 页面。我更新了一点,但我相信它可以改进更多:)

4

2 回答 2

24

使用 AdventureWorks,如果我们查看这两个等效查询:

SELECT OrderDate FROM Sales.SalesOrderHeader
WHERE DATEDIFF(month,OrderDate,GETDATE()) BETWEEN 1 AND 7;

SELECT OrderDate FROM Sales.SalesOrderHeader
WHERE OrderDate >= DATEADD(MONTH, -7, GETDATE())
  AND OrderDate <= DATEADD(MONTH, -1, GETDATE());

在这两种情况下,我们都会看到聚集索引扫描:

在此处输入图像描述

但请注意仅在后一个查询中推荐/缺失的索引,因为它是唯一可以从中受益的:

在此处输入图像描述

如果我们向 OrderDate 列添加索引,则再次运行查询:

CREATE INDEX dt ON Sales.SalesOrderHeader(OrderDate);
GO

SELECT OrderDate FROM Sales.SalesOrderHeader
WHERE DATEDIFF(month,OrderDate,GETDATE()) BETWEEN 1 AND 7;

SELECT OrderDate FROM Sales.SalesOrderHeader
WHERE OrderDate >= DATEADD(MONTH, -7, GETDATE())
  AND OrderDate <= DATEADD(MONTH, -1, GETDATE());

我们看到了很大的不同——后者使用了一个 seek:

在此处输入图像描述

在此处输入图像描述

还要注意您的查询版本的估计值是如何偏离的。这在大型数据集上绝对是灾难性的。

在极少数情况下,应用于列的函数或其他表达式将是 sargable。我知道的一种情况是CONVERT(DATE, datetime_column)——但是那个特定的优化没有记录在案,我还是建议远离它。不仅因为您暗示对列使用函数/表达式是可以的(并非在所有其他情况下),而且还因为它可能导致浪费读取和灾难性估计

于 2012-06-01T16:24:58.873 回答
8

如果这是可耻的,我会感到非常惊讶。一种选择可能是将其重写为:

WHERE p.PlayerStatusLastTransitionDate >= DATEADD(month,1,CAST(@now AS DATE))
AND   p.PlayerStatusLastTransitionDate <= DATEADD(month,7,CAST(@now AS DATE))

我相信这将是可悲的(即使它不那么漂亮)。

于 2012-06-01T16:19:56.673 回答