将 IF EXISTS 移至 WHERE 子句

Posted

技术标签:

【中文标题】将 IF EXISTS 移至 WHERE 子句【英文标题】:Moving IF EXISTS to the WHERE clause 【发布时间】:2014-12-19 20:54:31 【问题描述】:

在此示例查询中(针对设计不佳的供应商数据库):

DECLARE @OrderNo AS CHAR(8) = LEFT(@FullOrderNo,8)

IF EXISTS (SELECT 1 FROM Foo WHERE FullOrderNumber = @FullOrderNo)

    SELECT  Stuff
    FROM    Foo
    WHERE   X = 'Y'
            AND FullOrderNumber = @FullOrderNo

  ELSE

    SELECT  Stuff
    FROM    Foo
    WHERE   X = 'Y'
            AND OrderNumber = @OrderNo

由于 SELECT 和 WHERE 子句的第一部分是相同的,有没有办法安全地组合查询同时确保首先检查 FullOrderNumber 匹配项?我看到有no guarantees for WHERE clause evaluation order。

【问题讨论】:

WHERE FullOrderNumber = @FullOrderNo OR OrderNumber = @OrderNo @M.Ali:当有FullOrderNumber = @FullOrderNo的记录时,这将返回OrderNumber = @OrderNo不需要的记录 @M.Ali 如果在表中的任何地方都没有 FullOrderNumber 匹配项,我只想匹配 OrderNumber。 【参考方案1】:

你可以这样写:

SELECT  Stuff
FROM    Foo
WHERE   X = 'Y' AND
        (FullOrderNumber = @FullOrderNo OR
         (NOT EXISTS (SELECT 1 FROM Foo WHERE FullOrderNumber = @FullOrderNo) and OrderNumber = @OrderNo) )

如果您只寻找一行,您可以使用order by 进行优先排序:

SELECT  TOP (1) Stuff
FROM    Foo
WHERE   X = 'Y' AND
        (FullOrderNumber = @FullOrderNo OR OrderNumber = @OrderNo)
ORDER BY (CASE WHEN FullOrderNumber = @FullOrderNo THEN 1 ELSE 2 END)

其实就算有重复,你也可以这样使用with ties

SELECT  TOP (1) WITH TIES Stuff
FROM    Foo
WHERE   X = 'Y' AND
        (FullOrderNumber = @FullOrderNo OR OrderNumber = @OrderNo)
ORDER BY (CASE WHEN FullOrderNumber = @FullOrderNo THEN 1 ELSE 2 END)

【讨论】:

这些例子可能会将两个语句重写为一个;但他们不保证:that FullOrderNumber matches are checked first。如果 FullOrderNumber 上有索引,但 OrderNumber 上没有索引,则它可能会执行全表扫描(据推测 OP 试图通过此要求避免)。 @Gerrat 。 . .我将其解释为“首先返回完整的订单号”。但是,我不太确定 OP 是什么意思。 @GordonLinoff - 谢谢;澄清一下,我宁愿避免表扫描。如果原版表现更好,我会坚持下去。 @TrueWill 。 . . or 通常会导致表扫描。但是,数据的大小和第一个过滤器的选择性对性能有很大影响。 在答案中的第一个 SELECT 中,我认为您需要在表达式的 OR 部分周围加上括号。否则,它可以返回匹配 X <> 'Y'.

以上是关于将 IF EXISTS 移至 WHERE 子句的主要内容,如果未能解决你的问题,请参考以下文章