SELECT子句中的计算是否可以在应用WHERE谓词之前执行



是否可以在应用WHERE子句之前执行SQL查询的SELECT子句中的计算?我的目标是SQL Server 2008 R2,但我对一般情况感兴趣。我想这已经包含在SQL标准中了。

取下表进行查询:

CREATE TABLE TestTable
(
ID bigint primary key,
Accessed bigint null,
Created bigint null,
Modified bigint null
)
--insert the data here
SELECT ID, dateadd(ms, (Created % 864000000000) / 10000, dateadd(day, (Created / 864000000000) - 55517, '17530101')) AS EventTime, 3 AS EventType
FROM TestTable
WHERE (Created IS NOT NULL)
AND (Created <> 0)
AND (Created <= 2650467743999999999)

WHERE谓词用于筛选在通过SELECT子句中的嵌套dateadd()调用传递时会导致日期时间溢出的值。在某些情况下,即使我确信每个满足WHERE谓词的值的计算都会成功,我仍然会看到溢出发生。在每种故障情况下,源数据中总是有一些零值。

我的猜测是,查询规划器有时会决定在应用过滤器之前进行计算,从而允许输入导致溢出的零值。我无法确认,因为当溢出发生时,我看不到执行计划。

我的理论正确吗?有办法确认吗?我最感兴趣的是证实这个理论。如果我的理论是正确的,那么我知道如何解决这个问题。

遗憾的是,您无法控制何时进行过滤。SQL Server认为它在实际执行计划中将SELECT计算移动到WHERE筛选之前是在帮你一个忙。你不需要这样的优惠,因为它会导致错误。

有一个修复方法,就是使用case语句:

SELECT ID,
(case when Created <> 0 and Created < 2650467743999999999
then dateadd(ms, (Created % 864000000000) / 10000, dateadd(day, (Created / 864000000000) - 55517, '17530101'))
end) AS EventTime, 3 AS EventType
FROM TestTable
WHERE (Created IS NOT NULL) AND (Created <> 0) AND (Created <= 2650467743999999999);

如果你不喜欢重复的条件,你可以把它放在一个子查询中,并测试NULL:

SELECT *
FROM (SELECT ID,
(case when Created <> 0 and Created < 2650467743999999999
then dateadd(ms, (Created % 864000000000) / 10000, dateadd(day, (Created / 864000000000) - 55517, '17530101'))
end) AS EventTime, 3 AS EventType
FROM TestTable
) tt
WHERE EventTime is not null;

case语句保证在then之前评估when子句,尽管在聚合上下文中使用它时有一些特殊性。

最新更新