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

Pet*_*ore 5 sql sql-server scalar join

我想我总是天真地认为SQL查询的select部分中的标量函数只会应用于满足where子句所有条件的行.

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

NAMES: ID, NAME
    1, "Peter"
    2, "X"
LONG_NAMES: ID, NAME_LENGTH
    1, 5
Run Code Online (Sandbox Code Playgroud)

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

SELECT substring(NAME,1,len(NAME)-3)
    FROM NAMES;
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Sil*_*ilx 0

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