sql - 执行 SQL 语句时,可以在过滤之前应用标量函数吗?

标签 sql sql-server join scalar

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

最佳答案

Martin 给出的这个链接几乎解释了正在发生的事情 - 查询优化器可以自由地按照自己喜欢的方式重新排序。我将此作为答案,以便我可以接受一些内容。马丁,如果您创建一个包含链接的答案,我会很乐意接受这个答案,而不是这个。

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

TSQL divide by zero encountered despite no columns containing 0

编辑:随着更多回复的出现,我再次感到困惑。目前还不清楚到底什么时候允许优化器评估 select 子句中的内容。我想我得自己去找一下 SQL 标准,看看我是否能理解它。

关于sql - 执行 SQL 语句时,可以在过滤之前应用标量函数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5249415/

相关文章:

.net - 使用 SQL 2005 在 .NET 中检查错误

sql - 子查询返回每个父 ID 的最新条目

sql - 我应该避免 IN() 因为比 EXISTS() 慢

java - 聚合以合并集合尝试

MySQL - 选择所有客户和每个客户的总订单和总值(value)

sql - 通过@parameter 将文件传递给 OPENROWSET BULK json 查询,SQL 2016

mysql - 加入同一张 table 两次

c# - 管理 SQL 连接和事务的正确/理想方式 - C#

sql-server - 根据表中列的定义在存储过程中创建变量

MySQL - 添加 WHERE 子句时 LEFT JOIN 失败