SQL Server IN子句的奇怪行为

时间:2014-06-03 22:31:07

标签: sql sql-server

我想知道为什么下面的SQL语句的行为方式如下:

select * 
from tableA 
where document_id in (select document_id
                      from  tableB 
                      where batch_id = 99997)

tableA包含document_id列,但tableB没有,因此查询似乎返回tableA中的所有行,如果您使用任何字段名称,则会出现此行为IN子句的select语句,它是tableA中的一个字段。使用不在tableAtableB中的名称会导致错误。

3 个答案:

答案 0 :(得分:5)

这不是错误。在子查询中,您仍然可以使用父级列。所以当你说

SELECT document_id FROM tableB WHERE batch_id = 99997

您说的是表格B中batch_id为9997的每一行,请从document_id选择tableA。当然,所有这些document_id值都存在,因此它将返回所有这些行。

答案 1 :(得分:3)

这就是为什么我建议您养成为每列添加显式表名的习惯。它对于可维护性或以后扩展查询也很有帮助。

select * 
from tableA A
where A.document_id in (select B.document_id
                  from  tableB B
                  where B.batch_id = 99997)

如果您对这些表进行限定,它将在运行时抛出一个明确的错误并防止任何细微的错误。这会产生类似于

的错误
  

tableB.document_id不存在。

答案 2 :(得分:0)

多年后,我才发现自己的这种行为。我用Exists()代替IN()子句,除了临时和非数据修改查询外,因为过去使用空值会产生意外结果,但这使我感到困惑,直到我阅读了DavidG的响应。完全没有理由以这种方式编写查询:在不引用子查询表的情况下引用外部列,但是语法检查器将不允许这样做。

通常,如果将IN()与子查询一起使用,则您将引用外键,该外键在95%以上的时间中(按照我们公司的标准)与主键同名,但在5%的时间内可以抓住你。

尽管似乎这种行为也存在于Exists()...

Select * from tableA Where Exists(Select document_id from tableB)

... Exists()从来没有这样写过,因为它没有意义,但是写得更明确,就像...

Select * From tableA Where Exists(Select * from tableB tB where tB.TableB_Document_id = tableA.document_id)

,因此发生的可能性较小。

(注意:我会在注释中添加此内容,但我没有要点)