在SQL中使用危险的IN子句

在SQL中使用危险的IN子句,sql,sql-server,sql-server-2005,Sql,Sql Server,Sql Server 2005,为什么SQL server会这样做。我在SQL2005上运行这个 IN子句不验证子查询中的列名,而是验证它 针对外部查询中的表名。下面是一个获得成功的例子 Create table #table1(col1 int, col2 char(10), col3 char(15)); Create table #table2(col10 int, col11 char(10), col2 char(15)); insert into #table1(col1, col2, col3) selec

为什么SQL server会这样做。我在SQL2005上运行这个

IN子句不验证子查询中的列名,而是验证它 针对外部查询中的表名。下面是一个获得成功的例子

Create table #table1(col1 int, col2 char(10), col3 char(15));

Create table #table2(col10 int, col11 char(10), col2 char(15));


insert into #table1(col1, col2, col3)
select 1, 'one', 'three'

insert into #table1(col1, col2, col3)
select 2, 'two', 'three'

insert into #table1(col1, col2, col3)
select 3, 'three', 'four'


insert into #table2(col10, col11, col2)
select 1, 'one', 'three'

insert into #table2(col10, col11, col2)
select 2, 'two', 'three'

insert into #table2(col10, col11, col2)
select 3, 'three', 'four'


select * from #table1
where col1 IN
(select col1 from #table2)
其中,好像我只是从表2中选择selectcol1并运行它,它会抛出一个错误

Msg 207, Level 16, State 1, Line 1
Invalid column name 'col1'.

为什么??因为能够在子查询中引用外部查询中的列通常很有用。没有可以用来关闭此行为的设置,但如果您养成了使用别名的习惯,则应避免出现大多数问题:

select * from #table1 t1
where t1.col1 IN
(select t2.col1 from #table2 t2)

将产生错误。

问题不在于IN子句

这:

…有效,因为优化器假定col1来自表1。如果使用表别名以避免歧义:

SELECT t1.* 
  FROM #table1 t1
 WHERE t1.col1 IN (SELECT t2.col1 
                     FROM #table2 t2)
…您将得到消息207:无效列错误


这与使用DELETE和UPDATE语句时的原理相同,因为典型的语法不允许您为正在删除或更新的表添加别名。

这允许您通过外部查询筛选子查询中的数据,并使用此数据返回表达式。期望Sql Server将col1视为无效但col1*col10视为有效是太过分了。根据表2的定义,该表中没有col1-这就是出现错误的原因。您可以查看查询计划,以确定它没有将col1视为在第二个表中-请参阅-查看谓词部分。顺便说一句,很好,您提供了示例数据+1。下一次,您可能还想发布一篇sql文章:编程的第一条规则:击败我;我也会像你一样展示显式的实现。Damien,谢谢你的解释。大多数情况下,我会在处理多个表时使用别名,但这种特殊的SQL行为让我感到厌烦,我必须记住始终使用别名。没错,但大多数情况下,我们在处理单个表时,特别是在使用in子句时,通常不使用别名。在处理迁移脚本转换来自不同来源的数据时,如果不使用别名,这种SQL行为将受到影响。但是谢谢你指出,我会强调使用别名。@user1141441:是的,像这样的情况正是我对表别名用法如此坚持的原因。
SELECT t1.* 
  FROM #table1 t1
 WHERE t1.col1 IN (SELECT t2.col1 
                     FROM #table2 t2)