Sql 查询的奇怪行为
我从一个开发人员那里找到了这个查询:Sql 查询的奇怪行为,sql,sql-server,Sql,Sql Server,我从一个开发人员那里找到了这个查询: DELETE FROM [MYDB].[dbo].[MYSIGN] where USERID in (select USERID from [MYDB].[dbo].[MYUSER] where Surname = 'Rossi'); 此查询删除表MYSIGN中的每条记录 表MYUSER中不存在字段USERID。如果仅运行子查询: select USERID from [MYDB].[dbo].[MYUSER] where Surname = 'Rossi
DELETE FROM [MYDB].[dbo].[MYSIGN] where USERID in
(select USERID from [MYDB].[dbo].[MYUSER] where Surname = 'Rossi');
此查询删除表MYSIGN中的每条记录
表MYUSER中不存在字段USERID。如果仅运行子查询:
select USERID from [MYDB].[dbo].[MYUSER] where Surname = 'Rossi'
它抛出正确的错误,因为缺少列
我们使用右边的列更正了查询,但没有发现:
为什么第一个查询有效?
为什么它会删除所有记录?
规格:数据库位于SQL SERVER 2016 SP1,CU3上。显然您在[MYDB].[dbo].[MYSIGN]中有用户ID,所以SQL SERVER正是这样解析[MYDB].[dbo].[MYUSER]中的select USERID中未固定的用户ID的,其中姓氏='Rossi'-它将其解析为[MYDB].[dbo].[MYSIGN].USERID]
使用别名,它将失败
DELETE FROM [MYDB].[dbo].[MYSIGN] where USERID in
(select t.USERID from [MYDB].[dbo].[MYUSER] t where Surname = 'Rossi');
它被称为意外相关子查询@NenadZivkovic,我喜欢这个术语 问题在于子查询的范围规则。如果在子查询表中找不到该列,那么SQL引擎将开始查找下一个级别,在SQL Server中也是如此 每当查询中有多个表时,请始终限定列名。这意味着,将表名或别名与列别名放在一起。那么你就没有歧义了:
DELETE
FROM [MYDB].[dbo].[MYSIGN] m
WHERE m.USERID IN (SELECT u.USERID FROM [MYDB].[dbo].[MYUSER] u WHERE u.Surname = 'Rossi');
一个简单的规则可以让你的代码更具可读性,更不容易出错。请共享表结构。然后子查询中的USERID解析为MYSIGN.USERID,因此自然匹配所有行-限定名称。我的理解是:SQL通过对外部查询进行隐含的左连接来评估你的子查询。结果集包含外部查询中的所有行,因此删除将删除所有行。@AlexK.-令人惊叹的我仍然有一个问题:为什么会这样?如果你真的希望在子查询中有一个外部查询列呢?另一种选择是强制使用一个完全限定的名称,但规范规定它们是可选的。很好。这是非常奇怪的行为,应该被视为一个bug。不,这正是符合语言规范的行为。否则MSFT不会等到SQL 2016我们才讨论它。这不是bug,这是设计的。然而,通过别名将所有内容显式化应该是一条规则,这将有助于避免混淆,这就是所谓的意外相关子查询。通常,您可以在子查询中使用外部查询中的列,使其相互关联。在这种情况下,你在没有意识到的情况下就这样做了,而且效果很好。