2012-09-07 49 views
3

爲什麼SQL服務器的行爲如此。我正在SQL 2005上運行此操作。在SQL中使用危險的IN子句

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 COL1」並運行它吐出一個錯誤

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

這允許您通過外部查詢過濾子查詢中的數據,並使用此數據返回表達式。期望Sql Server認爲'col1'無效,但'col1 * col10'是有效的。 –

+0

根據'#table2'的定義,該表中沒有'col1' - 這就是出現錯誤的原因。 –

+1

你可能只是看了一下查詢計劃,發現它並不考慮col1在第二個表中 - 請參閱[這個屏幕截圖](http://i49.tinypic.com/2rfvl37.png) - 查看Predicate部分。 – Bridge

回答

13

爲什麼?因爲它經常是有用的能夠在子查詢中引用來自外部查詢的列。有沒有設置你可以用它來關閉此行爲,但如果你進入使用別名的習慣,你應該避免用它的大多數問題:

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

將產生錯誤。

+1

打敗我吧;正如你所做的那樣,它將顯示出明確的實施。 –

+0

+1:擊敗我~20秒。 –

+0

+1:30秒,正忙着糾正另一個答案 – LittleBobbyTables

6

這不是IN子句的問題。

此:

SELECT * 
    FROM #table1 
WHERE col1 IN (SELECT col1 
        FROM #table2) 

...作品,因爲優化是假設col1從表1#。如果使用表的別名,所以沒有歧義:

SELECT t1.* 
    FROM #table1 t1 
WHERE t1.col1 IN (SELECT t2.col1 
        FROM #table2 t2) 

...你會得到Msg 207: Invalid column error

這與使用DELETE和UPDATE語句時的原理相同,因爲典型語法不允許您別名刪除或更新表。

+0

是正確的,但大多數情況下,當使用單個表時,我們經常不使用別名,特別是在使用IN子句時。在處理從不同來源轉換數據的遷移腳本時,如果不使用別名,此SQL行爲會受到影響。但是,謝謝你指出,我會讓它使用別名。 – user1141441

+0

@ user1141441:是的,像這樣的情況正是我對於表別名用法如此堅決的原因。 –