【问题标题】:How to Short-Circuit SQL Where Clause如何短路 SQL Where 子句
【发布时间】:2013-10-17 14:17:33
【问题描述】:

我正在尝试在 SQL Server 中执行以下查询:

declare @queryWord as nvarchar(20) = 'asdas'

SELECT  * FROM TABLE_1 
WHERE (ISDATE(@queryWord) = 1) 
AND TABLE_1.INIT_DATE = CONVERT(Date, @queryWord)

这显然会导致错误,因为'asdas' 无法转换为Date。虽然,我期待不同的行为。也就是说,因为ISDATE(@queryWord) = 1false,所以我希望SQL检查第二个条件,但显然它确实如此。

我知道还有其他一些方法可以执行此查询,但这不是我的问题。我想知道是否有某种方法可以不检查第二个条件是第一个不满足。我很好奇,因为我认为 SQL 已经这样做了。

【问题讨论】:

标签: sql sql-server tsql


【解决方案1】:

SQL Server 不做短路(也不应该)。

如果您需要它在某些情况下不尝试某事,则需要以编写查询的方式强制这样做。

对于这个查询,最简单的解决方法是在 WHERE 子句中使用 CASE 表达式。

declare @queryWord as nvarchar(20) = 'asdas'

SELECT  * FROM TABLE_1 
WHERE TABLE_1.INIT_DATE = (CASE WHEN ISDATE(@queryWord) = 1 
                                THEN CONVERT(Date, @queryWord)
                           ELSE NULL  END)

我能想到的唯一两种受支持的方式,即CASE 和查询嵌套,可以强制对 SQL 中的依赖条件进行求值顺序。

【讨论】:

  • 并非如此。这将从 TABLE_1 返回 INIT_DATE 为空的行。我知道如何执行查询,但我的问题不是这个。
  • @nachovall 你的问题是“有办法不检查第二个条件是第一个不满足吗?”我已经回答了这个问题。
  • @nachovall 此外,“这将返回 TABLE_1 中 INIT_DATE 为空的行”是不正确的。在正确的 SQL NULL = NULL 中返回 False。只有NULL IS NULL 可以返回True。
  • 如果 sql server 不做短路,那你怎么解释这个:select 1 where 1=0 and 1/0 = 7 通常,你会期望除以 0 错误,但是查询在我的 sql 实例 (sql2005) 上运行得很好。
  • @i-one - 在这种情况下,优化器不会在 when 之前评估 then。问题是CASE 的结果被赋予了具有最高数据类型优先级的分支的数据类型。因为money 的数据类型优先级高于nvarchar,所以会评估else。然后结果就是金钱。如果您执行select case @proptype when 'money' then $1.0 else @val end,您会看到相同的错误。有where the short circuiting behaviour seems to fail 的场合,但比这更复杂。
【解决方案2】:

我猜你可以通过 2 次完成:

declare @queryWord as nvarchar(20) = 'asdas'


    select
    *
    from
    (
    SELECT  * FROM TABLE_1 
    WHERE (ISDATE(@queryWord) = 1) ) t1
    where t1.INIT_DATE = CONVERT(Date, @queryWord)

因此,您的内部查询运行第一个测试,外部查询运行第二个。在单个查询中,我不相信有任何方法可以强制评估条件的任何顺序。

【讨论】:

  • 您更喜欢简单的“否”吗?
  • :) 我的错。这只是一个例子,我并没有试图解决它,但还是谢谢
【解决方案3】:

为什么不在 WHERE 条件下做一个 CASE?

DECLARE @tester TABLE (
    theDate DATE,
    theValue INT
    )

INSERT INTO @tester VALUES ('2013-10-17', 35)
INSERT INTO @tester VALUES ('2013-10-16', 50)
INSERT INTO @tester VALUES ('2013-10-15', 2)

declare @queryWord as nvarchar(20) = 'asdas'
SELECT  *
FROM @tester
WHERE theDate =
    CASE
        WHEN ISDATE(@queryWord) = 1 THEN CONVERT(Date, @queryWord)
        ELSE theDate
    END

SET @queryWord = '2013-10-17'
SELECT  *
FROM @tester
WHERE theDate =
    CASE
        WHEN ISDATE(@queryWord) = 1 THEN CONVERT(Date, @queryWord)
        ELSE theDate
    END

【讨论】:

  • 你的问题是让它检查第一个条件(@queryWord 是有效的 DATE 类型),然后如果它为真,则继续执行条件 2 - 这样做。
【解决方案4】:

可以用CASE 语句“模拟”它。但是你必须让第一个条件给出一个TRUE 值以避免检查第二个条件:

declare @queryWord as nvarchar(20) = 'asdas'

SELECT  * 
FROM TABLE_1
WHERE (CASE 
       WHEN ISDATE(@queryWord) = 0 THEN 0 
       WHEN TABLE_1.INIT_DATE = CONVERT(Date, @queryWord) THEN 1
       ELSE 0 END) = 1

【讨论】:

    【解决方案5】:

    在 SQL 语句中没有定义求值顺序——除了 case 表达式,即使在这种情况下,也没有像保证结果那样定义顺序。 where 子句中的条件理论上可以并行或交替顺序完成。

    Case 表达式的不同之处不是具有定义的顺序,而是具有保证的结果。 IOW,case when 1=1 then 0 When longrunningfunction() = 1 then 2 end 保证返回零,但不保证不运行 longrunning 函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-18
      • 2013-02-03
      • 2014-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-16
      相关资源
      最近更新 更多