谓词的求值顺序
就在本系列的第 3 部分之前,我们稍作停顿,讨论了 SQL 中的谓词,因为它们会导致与外部联接相关的错误。在本系列“常见的 SQL 查询错误”的最后一部分中,谓词将再次出现,因为我们将研究谓词的求值顺序如何导致看似结构良好的查询因错误而运行失败。
谓词处理顺序的概述
按照逻辑查询处理顺序,查询按以下顺序执行:
- FROM
- WHERE
- GROUP BY
- HAVING
- SELECT
因此,从逻辑上讲,首先处理 FROM 子句以定义源数据集。接下来,应用 WHERE 谓词以缩减结果集,然后是 GROUP BY,依此类推。
实际上,谓词的求值和处理顺序远没有那么严格,因为查询优化器可能会移动查询中的表达式,以便生成最有效的数据检索计划。因此,在处理下一个子句之前,可能不会应用 WHERE 子句中的筛选。事实上,谓词在物理执行计划中的应用可能比你预期的要晚得多。
另一个让数据库开发人员感到困惑和沮丧的常见原因是,与大多数编程语言不同,谓词并不总是从左到右执行。这意味着,如果你有一个包含筛选“WHERE a=1 AND b=2”的 WHERE 子句,则不能保证首先对“a=1”求值。事实上,没有简单的方法可以仅通过查看查询来判断筛选将以什么顺序执行。
一个实际的例子
为了更好地理解谓词评估顺序,我们将对以下 accounts 表编写一个 SELECT 查询,如 Navicat 16 的表设计器所示:
以下是我们将要查询的一些示例数据:
在 account_number 列中,业务帐户被分配了一个数字标识符,而个人帐户被分配了一个由字符组成的标识符。这不是很好的表设计,因为 account_number 列应该由两个不同的字段表示,每个帐户类型都应该有正确的数据类型,并且不应在同一个表中。尽管如此,我们通常无法改变表设计,所以必须按表的原样处理。
因此,考虑到这一点,让我们设计一个查询来检索所有 account_number 大于 50 的业务类型帐户。查询可能如下所示:
在某些数据库中,查询会产生错误:
Conversion failed when converting the varchar value 'ACFB' to data type int
只要查询优化器决定优先处理“CAST(account_number AS UNSIGNED INTEGER) > 50”谓词而不是“account_type LIKE 'Business%'”,查询就会失败。避免上述错误的最安全方法是:
- 正确设计表格,避免将混合数据存储在单个列中。
或者
- 使用 CASE 表达式确保只有有效的数值将被转换为 INTEGER 数据类型,如下所示:
总结
在这个关于“常见的 SQL 查询错误”的系列文章中,我们探索看似直观的 SQL 查询构造方法如何导致反模式,从而导致错误结果和/或性能降低。特别要注意谓词的位置和求值顺序,因为它们会导致许多意想不到的问题。