为什么这个SQL有效?

tra*_*max 2 sql sql-server sql-server-2012

我的同事用一个我认为实际上应该失败的工作SQL使我感到困惑.

  • [IdentityUserRoles]:列RoleId,UserId;
  • [IdentityRoles]:列Id,Name;

执行的SQL有效:

Delete from IdentityUserRoles where RoleId in ( 
   Select RoleId 
   FROM [dbo].[IdentityRoles] 
   where Name in ('TeamManager','Manager')
)
Run Code Online (Sandbox Code Playgroud)

它看起来是合法的,但如果你仔细查看子查询,表IdentityRoles没有RoleId列,它应该是Id.

所以,如果我单独运行子查询:

Select RoleId 
FROM [dbo].[IdentityRoles] 
where Name in ('TeamManager','Manager')
Run Code Online (Sandbox Code Playgroud)

我收到错误消息:

消息207,级别16,状态1,行1
无效的列名称"RoleId".

这是预期的,因为列RoleId确实不存在.

问题是,如果子查询包含语法错误,为什么父查询会继续执行?

我在SQL Server 2012 Express Edition(v11.0.5058.0)中运行它

更新 使此查询工作 - 没问题.问题是为什么它首先起作用并且没有抛出语法错误.

dat*_*ale 5

这里的SQL Server没有任何问题.这是预期的行为!

错误地在子查询中指定RoleId导致SQL Server认为它是一个相关的子查询.

SQL Server评估它的方式是,它首先在IdentityRoles中搜索RoleId,但在那里找不到它,所以它在IdentityUserRoles表中搜索.在那里发现了!所以这是一个有效的查询.

但是,如果您期望自包含子查询的行为不同...添加别名,如下所示.

Delete from IdentityUserRoles where RoleId in 
 ( 
   Select r.RoleId FROM [dbo].[IdentityRoles] r where Name in ('TeamManager','Manager')
 )
Run Code Online (Sandbox Code Playgroud)

上述查询将失败,因为SQL Server将在IdentityRoles表中显式搜索该列.

但是,如果遵循一些最佳实践,则不会出现这种无意的错误.最佳实践(99%的情况下)是使相同的列名表示相同的属性.

另一件事是,作为一种做法,最好遵循连接,子查询等的别名,并在选择中引用具有这些别名的列名.这样可以避免任何此类意外的错误或问题.

希望有所帮助!