2015-05-25 12 views
6

Jemand möchte einen Versuch machen, die Mechanik zu erklären ... diese kleine Eigenart des Abfrageparsers hat mir heute fast schon großen Schaden zugefügt.Wie man versehentlich alle Zeilen in einer Tabelle löscht

Erstellen Sie eine Testtabelle mit 100 Zeilen mit 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 

erstellen eine temporäre Tabelle mit ganzen Zahlen 1-50 in ihm:

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

Jetzt löscht ein eine IN Klausel aber mit den falschen Spaltennamen in der inneren Abfrage:

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

Diese letzte Löschanweisung, aus dem Aussehen, gibt das Aussehen des Löschens von 50 Zeilen ... Allerdings gibt es keine in #deletions, so dass ich t Art-of zieht das aus der äußeren Abfrage und endet irgendwie, löscht alle Zeilen im Test.

Meine Frage ist, wie in aller Welt interpretiert es diese innere Abfrage ... #deletions hat nur 50 Zeilen, also wie zieht es alle 100 IDs aus der äußeren Tabelle? Diese Art von Tippfehler hat mir heute fast schon großen Schaden zugefügt.

Meiner Meinung nach sollte dies eine Art Parsing/Syntaxfehler oder irgendeine Art von Mehrdeutigkeitsfehler werfen.

hier ein SQL Fiddle Demo

+1

Zumindest werden Sie eine Abfrage wie diese nie wieder tun :-) Senden Sie niemals ein 'DELETE' oder' UPDATE', ohne zu testen. Ändern Sie 'DELETE' in' SELECT * 'und sehen Sie, was zurückgegeben wurde. – dnoeth

+1

Hier gibt es keine Mehrdeutigkeit, da JobID nur auf der äußeren "Test" -Tabelle existiert. Sie haben Recht, dass der Umfang nicht offensichtlich ist. Aus diesem Grund ist es eine gute Methode, sich die Aneignung von Spaltennamen mit dem Tabellennamen oder Alias ​​in Abfragen mit mehreren Tabellen anzueignen. –

+0

Immer wenn Sie mit UPDATE/DELETE/MERGE/INSERT zu tun haben, wickeln Sie immer mit BEGIN TRAN und ROLLBACK TRAN. Einer der wichtigsten Ratschläge für die Produktion Ops Jungs. – Greg

Antwort

10

Wenn Sie Tabellen-Aliases verwenden, die Logik klar sein würde. Sie denken, Sie schreiben:

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

das Sinn macht, aber es würde zu einem Syntaxfehler, weil JobId nicht in #deletions existiert. Also, gehen Sie Scoping-Regeln von SQL auf die nächste Ebene JobId und interpretieren Sie die Abfrage wie zu finden:

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

Dadurch werden alle Nicht-NULL-Werte von JobId löschen.

Die Moral: Verwenden Sie immer qualifizierte Spaltennamen.

+0

Der logische Fehler in einem einfachen Bereinigungsskript war sehr schwer zu erkennen. Glücklicherweise entfernte es nur Daten von einem belanglosen Tisch, aber ich war ein wenig verwirrt, bis ich das Problem entdeckte. Ich werde von nun an immer Aliase verwenden oder einen JOIN für die Löschung anstelle einer IN-Klausel verwenden. –

Verwandte Themen