2015-05-25 69 views
6

有人想刺探解釋這種機制...查詢解析器的這個小怪癖幾乎對我今天造成了重大損害。如何意外刪除表中的所有行

用100行創建一個測試表,1-100。

create table test(JobID int primary key); 

;with numbers as (
    select 1 as n 
    union all 
    select n + 1 as n 
    from numbers 
    where n < 100 
) 
insert into test 
select n from numbers 

在它創建整數1-50臨時表:

select jobid as number into #deletions 
from test 
where jobid <= 50 

現在做了刪除使用IN條款,但在內部查詢錯誤的列名:

delete from test where JobID in (select JobID from #deletions) 

最後一條刪除語句從外觀上給出了刪除50行的外觀... 但是,在#deletions中沒有JobID,所以我t種類從外部查詢中拉出來,並以某種方式結束,刪除測試中的所有行。

我的問題是,它究竟如何解釋內部查詢... #deletions只有50行,那麼它如何從外部表中拉出所有100個ID?這種類型的錯字/錯誤今天幾乎對我造成了重大損害。

在我看來,這應該會引發某種解析/語法錯誤或某種模糊性錯誤。

這裏有一個SQL Fiddle Demo

+1

至少你不會再這樣查詢:-)千萬不要在沒有測試的情況下提交'DELETE'或'UPDATE'。將'DELETE'更改爲'SELECT *'並查看返回的內容。 – dnoeth

+1

這裏沒有歧義,因爲JobID僅存在於外部'test'表中。你說的範圍並不明顯,這就是爲什麼習慣使用多表查詢中的表名或別名來限定列名的習慣。 –

+0

每當你搞亂UPDATE/DELETE/MERGE/INSERT時,總是用BEGIN TRAN和ROLLBACK TRAN包裝。其中一個最重要的建議是製作Ops的人。 – Greg

回答

10

如果使用表的別名,邏輯是清楚的。你以爲你是寫:

delete from test 
    where test.JobID in (select d.JobID from #deletions d); 

這是有道理的,但由於JobId不存在#deletions它會產生一個語法錯誤。所以,SQL的作用域規則去到一個新的水平,以找到JobId和解釋查詢爲:

delete from test 
    where test.JobID in (select test.JobID from #deletions d); 

這將刪除所有JobId非NULL值。

道德:始終使用合格的列名稱。

+0

什麼是簡單的清理腳本中的邏輯錯誤很難找到。幸運的是,它只刪除了一個無關緊要的表中的數據,但我有點困惑了一段時間,直到我發現問題。從現在起,我將一直使用別名,或者使用JOIN作爲刪除而不是IN子句。 –

相關問題