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

时间:2014-02-26 16:06:02

标签: sql sql-server

在应用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谓词的每个值上成功。在每种情况下都会出现故障,源数据中总会有一些零值。

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

我的理论听起来不错?有没有办法确认呢?我更感兴趣的是确认理论而不是其他任何东西。如果我的理论是正确的,那么我知道如何解决这个问题。

1 个答案:

答案 0 :(得分:3)

唉,你无法控制何时发生过滤。 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语句保证在when之前评估then子句,尽管在聚合的上下文中使用它时有一些特殊性。