为什么这个查询,缺少一个 FROM 子句,而不是错误?

Wjd*_*is5 9 sql-server t-sql sql-server-2017

所以我们有一个带有错字的子查询的查询。它缺少 FROM 子句。但是当你运行它时,它不会出错!为什么!?


SELECT

    1
   ,r.id
   ,'0D4133BE-C1B5-4141-AFAD-B171A2CCCE56'
   ,GETDATE()
   ,1
   ,'Y'
   ,'N'
   ,oldItem.can_view
   ,oldItem.can_update

FROM Role r

JOIN RoleObject oldReport
    ON r.customer_id = oldReport.customer_id

JOIN RoleItem oldItem
    ON oldReport.id = oldItem.role_object_id
        AND r.id = oldItem.role_id

WHERE r.id NOT IN (SELECT
        role_id
    WHERE role_object_id = '0D4133BE-C1B5-4141-AFAD-B171A2CCCE56')

AND oldReport.id = '169BA22F-1614-4EBA-AF45-18E333C54C6C'
Run Code Online (Sandbox Code Playgroud)

Aar*_*and 21

该声明是合法的(换句话说,FROM不需要):

SELECT x = 1;
SELECT x = 1 WHERE 1 = 1; -- also try WHERE 1 = 0;
Run Code Online (Sandbox Code Playgroud)

诀窍是当您引入一个明显不存在的列名时。所以这些失败了:

SELECT name WHERE 1 = 1;

SELECT x = 1 WHERE id > 0;
Run Code Online (Sandbox Code Playgroud)

消息 207,级别 16,状态 1
列名称“名称”无效。
消息 207,级别 16,状态 1
列名称“id”无效。

但是当在子查询之类的东西中引入了无效列时,SQL Server 在子查询的内部范围内找不到该列时所做的就是遍历到外部范围,并使子查询与该外部范围相关联。这将返回所有行,例如:

SELECT * FROM sys.columns WHERE name IN (SELECT name WHERE 1 = 1);
Run Code Online (Sandbox Code Playgroud)

因为它本质上是在说:

SELECT * FROM sys.columns WHERE name IN (SELECT sys.columns.name WHERE 1 = 1); /*
              ^^^^^^^^^^^                       -----------
                   |                                 |
                   -----------------------------------    */
Run Code Online (Sandbox Code Playgroud)

您甚至不需要WHERE子查询中的子句:

SELECT * FROM sys.columns WHERE name IN (SELECT name);
Run Code Online (Sandbox Code Playgroud)

您可以看到它确实在查看外部作用域表,因为:

SELECT * FROM sys.columns WHERE name IN (SELECT name WHERE name > N'x');
Run Code Online (Sandbox Code Playgroud)

返回的行要少得多(我的系统上有 11 行)。

这涉及遵守有关范围界定的标准。当您有两个 #temp 表时,您可以看到类似的内容:

CREATE TABLE #foo(foo int);
CREATE TABLE #bar(bar int);

SELECT foo FROM #foo WHERE foo IN (SELECT foo FROM #bar);
Run Code Online (Sandbox Code Playgroud)

显然,这应该是错误的,对吧,因为没有fooin #bar? 不。发生的事情是 SQL Server 说:“哦,我在foo这里没有找到,你一定是指另一个。”

另外,一般来说,我会避免NOT IN. NOT EXISTS在某些情况下有可能更高效,但更重要的是,当目标列可能是NULL. 有关更多信息,请参阅此帖子