SQL Server:此 SQL 语句如何工作

Posted

技术标签:

【中文标题】SQL Server:此 SQL 语句如何工作【英文标题】:SQL Server: How does this SQL statement work 【发布时间】:2016-03-26 00:34:37 【问题描述】:

请解释此 SQL 语句如何选择所有 column1 值。

所以我有参数值 A、B 和 *

如果我通过@brand = '*',那么此语句将返回column1 中的所有值:

SELECT * 
FROM TABLE 
WHERE (column1 = @brand OR @brand = '*')

我在想,当@brand = '*' 时,它会将@brand 分配为TRUE,所以column1 中的任何内容都将是true,所以这就是它返回column1 中的所有值的原因

如果我通过A,那么它只会返回A 值,如果B 然后B 值。

【问题讨论】:

【参考方案1】:

你的假设是正确的。它根据最简单的路径依次评估您的WHERE 语句中的子句。

所以,因为有一个OR,它首先评估@brand ='*' 子句;如果产生TRUE,则满足WHERE 子句并且不再进行比较。

如果它产生FALSE,那么它计算表达式中的另一个子句:column1 = @brand

但是,我应该注意,这不是在 SQL 中执行此操作的正确方法,因为 SQL Server 不保证短路,也不保证逻辑子句的执行顺序。它恰好在您的情况下以这种方式工作,但不能保证。您应该将WHERE 子句更改为如下所示:

((@brand = '*') OR (@brand <> '*' AND column1 = @brand))

【讨论】:

那么当它第一次将@brand = '*' 评估为TRUE 时,它如何知道将其应用于column1? (因为没有进行进一步的比较)。我认为它只看到 @brand = '*' 而不是 column1 = @brand @sojim2 这就是重点——它不适用于column1。在OR 子句中,只有一侧需要为TRUE - 因此两个子句运行的唯一方法是其中一个或两个语句是FALSE 举个例子,看看这个(非 SQL)代码:ideone.com/GmzhHU(我不能用 SQL 代码给你这样的例子,因为 SQL 不允许在表达式中产生副作用)。注意有一个基于ORif 语句(||),并注意只有第一个函数是如何执行的。因为它产生true,所以第二个不需要运行。程序已经知道如果第一个为真,则整个语句为真(1 或 0 = 1)。这称为 short-circuiting (虽然 SQL Server 并没有完全以相同的方式进行短路,但这超出了本问题的范围)。 @Ruslan,不,不能保证评估的顺序。见***.com/questions/484135/…。话虽如此,逻辑结果如前所述.. @DanGuzman 是的。他问为什么它以这种方式工作,而不是它是否是正确的方式。这就是我在评论中提到 的原因(尽管 SQL Server 并没有完全以同样的方式进行短路,但这超出了这个问题的范围)。但是,我同意我应该提到在 SQL 中执行此类操作的正确方法是((@brand = '*') OR (@brand &lt;&gt; ='*' AND column1 = @brand));将相应地编辑我的答案。【参考方案2】:

这条 SQL 语句是如何工作的?

你是在逻辑上提问吗?

SQL 使用三值逻辑。每个单独的谓词可以评估为真、假或未知。

您需要考虑示例中OR 的真值表,以了解如何组合谓词以获得整体结果。

WHERE 子句的上下文中,整个谓词的计算结果必须为true,才能在结果中返回行。

上面的真值表表明,只要知道其中一个谓词为真就足够了。因此,如果第一个评估为 true,则无需评估第二个。这称为短路评估。

SQL Server 不保证评估顺序或将使用短路评估。

一个例子

DECLARE @Table TABLE (
  column1 VARCHAR(50) PRIMARY KEY);

INSERT INTO @Table
VALUES     ('brand1'),
            ('brand2'),
            ('brand3')

DECLARE @brand VARCHAR(50) = 'brand1'

SELECT *
FROM   @Table
WHERE  ( column1 = @brand
          OR @brand = '*' );

执行计划显示如下

扫描整个表并在每一行上评估谓词。 SQL Server 可能会或可能不会按所示顺序评估条件,并且可能会或可能不会使用短路评估。这些信息不会向我们公开。

返回单行 - 第一行。

效率

上面查询的语义只是

SELECT * 
FROM @Table 
WHERE column1= 'brand1'

这应该可以通过对支持主键的索引进行简单的查找来评估——而不是扫描整个表。

从 SQL Server 2008 起,您可以使用以下内容

DECLARE @brand VARCHAR(50) = 'brand1'

SELECT * 
FROM @Table 
WHERE (column1 = @brand OR @brand = '*')
OPTION (RECOMPILE)

这会在执行语句之前重新编译语句,并且不会缓存执行计划。这意味着计划可以考虑到@brand传递的具体值。

现在@brand = '*' 的比较在编译时完成并确定为假,谓词简化为column1 = 'brand1' - 允许索引查找该特定值。

【讨论】:

以上是关于SQL Server:此 SQL 语句如何工作的主要内容,如果未能解决你的问题,请参考以下文章

Oracle的SQL语句是如何工作的?

有大神知道,sql server 中如何批量执行sql语句吗?

如何查看sql server 2008的SQL语句执行错误日志

sql server 语句如何将3个表合并成一个表?

如何查找MySQL中查询慢的SQL语句

如何查找MySQL中查询慢的SQL语句