5

我想我一直天真地假设 SQL 查询的 select 部分中的标量函数只会应用于满足 where 子句的所有条件的行。

今天我正在调试来自供应商的一些代码,并且对这个假设提出了挑战。我能想到这段代码失败的唯一原因是 Substring() 函数被调用了应该被 WHERE 子句过滤掉的数据。但似乎在过滤发生之前正在应用子字符串调用,查询失败。这是我的意思的一个例子。假设我们有两个表,每个表有 2 列,分别有 2 行和 1 行。每个中的第一列只是一个 id。NAME 只是一个字符串,NAME_LENGTH 告诉我们名称中有多少个字符具有相同的 ID。请注意,只有具有多个字符的名称在 LONG_NAMES 表中有对应的行。

NAMES: ID, NAME
    1, "Peter"
    2, "X"
LONG_NAMES: ID, NAME_LENGTH
    1, 5

如果我想要一个查询来打印每个名称的最后 3 个字母被截断,我可能会首先尝试这样的事情(假设现在是 SQL Server 语法):

SELECT substring(NAME,1,len(NAME)-3)
    FROM NAMES;

我很快就会发现这会给我一个错误,因为当它到达“X”时,它会尝试在子字符串调用中使用负数,它会失败。我的供应商决定解决这个问题的方法是过滤掉字符串太短而无法使用 len - 3 查询的行。他通过加入另一张桌子来做到这一点:

SELECT substring(NAMES.NAME,1,len(NAMES.NAME)-3) 
    FROM NAMES 
        INNER JOIN LONG_NAMES 
            ON NAMES.ID = LONG_NAMES.ID;

乍一看,这个查询似乎可行。连接条件将消除任何具有足够短的 NAME 字段以使子字符串调用失败的行。

但是,据我观察,SQL Server 有时会尝试计算表中所有内容的子字符串表达式,然后应用连接过滤掉行。这应该以这种方式发生吗?是否有记录在案的操作顺序,我可以在其中找出某些事情何时发生?它是特定于特定的数据库引擎还是 SQL 标准的一部分?如果我决定在我的 NAMES 表中包含一些谓词以过滤掉短名称(如 len(NAME) > 3),SQL Server 是否也可以在尝试应用子字符串后选择应用它?如果是这样,那么执行子字符串的唯一安全方法似乎是将其包装在选择中的“case when”构造中?

4

3 回答 3

2

Martin 给出的这个链接几乎解释了发生了什么 - 查询优化器可以自由地重新排序它喜欢的东西。我将其作为答案包括在内,因此我可以接受一些东西。马丁,如果你用你的链接创建一个答案,我会很乐意接受这个而不是这个。

我确实想把我的问题留在这里,因为我认为这是一个很难搜索的问题,而且我对这个问题的特定措辞将来可能更容易被其他人找到。

尽管没有包含 0 的列,但遇到 TSQL 除以零

编辑:随着更多回应的出现,我再次感到困惑。目前尚不清楚何时允许优化器对 select 子句中的内容进行评估。我想我必须自己去寻找 SQL 标准,看看我是否能理解它。

于 2011-03-09T17:51:19.523 回答
1

帮助编写早期 SQL 标准的 Joe Celko 在各种 USENET 新闻组中多次发布了类似的内容。(我跳过了不适用于您的 SELECT 语句的子句。)他通常会说“这就是语句应该像它们一样工作的方式”。换句话说,SQL 实现的行为应该与它们执行这些步骤的行为完全相同,而实际上并不需要执行这些步骤中的每一个。

  1. 从 FROM 子句中的所有表构造函数构建一个工作表。
  2. 从工作表中删除那些不满足 WHERE 子句的行。
  3. 针对工作表构造 SELECT 子句中的表达式。

因此,在此之后,任何 SQL dbms 都不应该像在应用 WHERE 子句之前那样评估 SELECT 子句中的函数。

在最近的一篇文章中,Joe 扩展了包含 CTE 的步骤

CJ Date 和 Hugh Darwen 在他们的书A Guide to the SQL Standard的第 11 章(“表表达式”)中说的基本相同。他们还注意到这一章对应于 SQL 标准中的“查询规范”部分(sections?)。

于 2011-03-09T17:55:05.240 回答
0

您正在考虑称为查询执行计划的东西。它基于查询优化规则、索引、临时缓冲区和执行时间统计。如果您使用的是 SQL Managment Studio,您的查询编辑器上有一个工具箱,您可以在其中查看估计的执行计划,它显示您的查询将如何改变以获得一些速度。因此,如果只是使用您的 Name 表并且它在缓冲区中,引擎可能会首先尝试子查询您的数据,然后将其与其他表连接。

于 2011-03-09T17:21:14.650 回答