2013-03-13 11 views
5

ich eine gespeicherte Prozedur ausgeführt wird, die Werte meine temporäre Tabelle und fügt sie in die Datenbank wie so wählt:INSERT INTO .. ​​SELECT .. eindeutige Einschränkung Verletzung

INSERT INTO emails (EmailAddress) (
    SELECT 
     DISTINCT eit.EmailAddress 
    FROM #EmailInfoTemp eit 
     LEFT JOIN emails ea 
     ON eit.EmailAddress = ea.EmailAddress 
    WHERE ea.EmailAddressID IS NULL ) 

In seltenen Fällen (~ einmal alle paar von Stunden auf einem Server, der Tausende von Anfragen pro Minute behandelt), dann erhalte ich einen eindeutigen Constraint-Fehler "Verletzung der UNIQUE KEY-Einschränkung" in einem Index der EmailAddress-Spalte.

Ich kann bestätigen, dass ich keine doppelten Werte übergebe. Selbst wenn ich es wäre, sollte es von der DISTINCT aufgefangen werden.

-SQL Server 2008 -Stored proc + nicht mit Transaktionen + JDBC Callablestatement

Könnte es passieren, dass zwischen dem SELECT und der nachfolgenden INSERT, wenn ein weiterer Teilnehmer an der gleichen/verschiedenen gespeicherte Prozedur war, die eine INSERT abgeschlossen mit ähnlichen Daten? Wenn ja, was wäre der beste Weg, dies zu verhindern?

Einige Ideen: Wir haben viele doppelte Instanzen von "Clients", die mit diesem einen SQL Server auf einmal in der Produktion kommunizieren, so meine erste Reaktion war ein Nebenläufigkeitsproblem, aber ich kann nicht scheinen, es selbst zu replizieren. Das ist die beste Vermutung, die ich hatte, aber es ist nirgends so weit gegangen. Dies geschieht nicht in unserer Staging-Umgebung, in der die Last im Vergleich zur Produktionsumgebung unbedeutend ist. Das war der Hauptgrund, warum ich angefangen habe, Nebenläufigkeitsprobleme zu untersuchen.

+1

Ein NULL-Wert in EmailAddress auf beiden Seiten könnte die eindeutige Schlüsseleinschränkung verletzen. Wenn diese Spalte nullfähig ist, können Sie die Einfügung als "nicht vorhanden" umschreiben. –

+0

Guter Punkt. Ich überprüfe im Code Nullen, aber ich nehme an, ich könnte es nochmal in SQL überprüfen. –

Antwort

5

Der Fehler wird wahrscheinlich von zwei Sitzungen verursacht, die gleichzeitig eine Einfügung ausführen.

Sie können Ihren SQL-Code sicherer machen, indem Sie MERGE verwenden. Wie Aaron Bertrands Kommentar sagt (Danke!), you have to include a with (holdlock) hint to make merge really safe.

; merge emails e with (holdlock) 
using #EmailInfoTemp eit 
on  e.EmailAddress = eit.EmailAddress 
when not matched then insert 
     (EmailAddress) values (eit.EmailAddress) 

Die merge Anweisung entsprechende Sperren, um sicherzustellen, dass keine andere Sitzung in schleichen kann zwischen es „nicht zugeordnet“ überprüfen und die „Einfügen“.

Wenn Sie merge nicht verwenden können, können Sie das Problem clientseitig lösen. Stellen Sie sicher, dass keine zwei Einfügungen gleichzeitig ausgeführt werden. Dies ist typischerweise einfach mit einem mutex oder einem anderen Synchronisationskonstrukt zu tun.

+1

['MERGE' ist nicht unbedingt sicherer, es sei denn, Sie fügen einen 'HOLDLOCK'-Hinweis hinzu] (http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx). –

+0

@AaronBertrand: Danke. Die MSDN-Seite für MERGE sagt "READPAST mit WENN NICHT [DURCH TARGET] DANN INSERT abgestimmt wird, kann zu INSERT-Operationen führen, die gegen UNIQUE-Einschränkungen verstoßen." Also nahm ich an, dass es sicher ist, wenn Sie nicht "readpast" angegeben haben. – Andomar

+1

yeah Microsoft ist normalerweise nicht in der Lage, alle Fälle zu dokumentieren, in denen es möglicherweise beschädigt wird. Die Dokumentation spricht auch nicht über [alle diese noch zu lösenden Fehler (scrollen Sie nach unten)] (http://www.sqlperformance.com/2013/02/t-sql-queries/another- Merge-Fehler). :-) –

Verwandte Themen