我想我一直天真地假设 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”构造中?