我是否仍应在 SQL 中的 WHERE 子句中使用短路来测试条件?



在4年前的一个问题中,海报询问是否可以以及如何简化以下代码:

CREATE Procedure getAllEmployeesByDeptAndFlag
@Dept int,
@sal int,
@Flag int
AS
if @flag = 1
select Department, Salary
from employee 
where Department = @dept and Salary > @sal
else 
select Department, Salary
from employee 
where Department = @dept

这个问题的答案和类似的问题似乎总是建议使用逻辑 OR 运算符来短路布尔表达式。虽然,值得注意的是,大多数作者实际上并没有用这么多话说出来,而只是给你留下一个应该有效的例子。就像在这种情况下接受的答案一样,它只包含一行:

select * from employee where Department = @dept AND (@flag != 1 OR Salary > @sal)

使用SQL已经有一段时间了,当我第一次看到这个时,我感到困惑。如果我正在考虑一个看起来像典型示例的 where 子句WHERE Country = 'USA' OR Country='Canada',我们知道查询不会在发现第一个条件为真后停止对完整子句的计算,它还将始终评估第二个条件并相应地呈现结果。

然而,在上面提到的答案中,如果发现第一个条件为真,则不会评估第二个条件。虽然我发现这种行为被称为短路,但我仍然很难理解为什么它以这种方式工作。需要明确的是,我理解这在 C 等过程语言中是如何工作的,但我不太明白为什么它在 SQL 中工作,对此的任何更详细的见解将不胜感激。

在进一步研究这个主题时,我遇到了几篇文章,比如这里和这里,进一步解释了这种所谓的短路并不总是在SQL中起作用。SO上的一些答案似乎朝着同一个方向发展。这似乎与查询优化器可以自由选择它认为最好的路径这一事实有关,这可能意味着它将以不同的顺序评估条件。

你会认为接受的答案是今天的正确答案吗?如果是,您能否详细说明为什么在此特定示例中不认为短路是一个问题?还有什么其他更好的方法来获得相同的结果?

更新:正如戈登所注意到的,最初的程序有一个问题。第二个条件读作@sal < 10000。因此,将变量与常量进行比较,两者与查询表之间没有关系。我确信原始海报的意思是将常量与数据表中的值进行比较,因此我重写了过程以及给定的答案以适应这一点。

这解决了问题的原始版本。

在此逻辑中:

where Department = @dept AND (@flag <> 1 OR @sal < 10000)

(概念上(为表中的所有行计算这两个条件。 我从概念上说,因为 SQL 查询描述了结果集;它不指定各个步骤。 因此,例如,可以使用索引或分区跳过行。

也就是说,第二个条件仅包含常量值。 一个聪明的(甚至不那么聪明的(优化器会识别这一点,并在编译阶段对其进行评估。 因此,实际执行的查询将是以下查询之一:

where Department = @dept and <true>
where Department = @dept and <false>

如果是后者,优化阶段的进一步步骤可以说:"嘿,这总是错误的,所以我不需要做任何事情。

但是,在所有情况下,结果集都基于查询描述的内容 - 可以预先计算常量部分,而不是逐行计算。

最新更新