it-swarm.com.de

Wie vermeide ich einen "doppelten Schlüssel" -Fehler?

Diese Aussage:

INSERT INTO deleteme
SELECT #t.id, 'test' FROM #t
LEFT JOIN  deleteme  ON deleteme.id = #t.id
WHERE deleteme.id IS NULL;

... schlägt mit einer Primärschlüsselverletzung im gleichzeitigen Szenario fehl (d. h. beim Einfügen desselben Schlüssels, der nicht gleichzeitig in mehreren Threads gelöscht wird) mit der Standardtransaktionsisolationsstufe READ COMMITTED:

Fehler: Verletzung der PRIMARY KEY-Einschränkung 'PK_DeleteMe'. Es kann kein doppelter Schlüssel in das Objekt 'dbo.deleteme' eingefügt werden.

Was ist der beste Weg, dies zu verhindern?

Der Tisch sieht ungefähr so ​​aus:

CREATE TABLE [dbo].[DeleteMe](
    [id] [uniqueidentifier] NOT NULL,
    [Value] [varchar](max) NULL,
CONSTRAINT [PK_DeleteMe] PRIMARY KEY ([id] ASC));

pdate

Aus Kommentaren:

Warum haben Sie mehrere Sitzungen, bei denen offensichtlich dieselbe ID von irgendwoher abgerufen werden kann, wobei dieselbe permanente Tabelle verwendet wird, ohne dass ein Sitzungsschlüssel vorhanden ist, der sie voneinander unterscheidet? Woher wissen Sie auch ohne doppelten Fehler, welche Zeilen zu welcher Sitzung gehören?

Dies ist eine gespeicherte Prozedur, die von einem externen Dienst aufgerufen wird, der Daten in dieser Tabelle auffüllt. Der Dienst generiert die Datensatz-ID und bietet keine Garantie dafür, dass nicht zweimal dieselben Daten gesendet werden. Oder senden Sie die gleichen Daten zur gleichen Zeit.

Der Datenbankcode soll alle Datensätze mit derselben ID verwerfen, falls bereits einer vorhanden ist. Der Dienst sendet niemals zwei Datensätze mit derselben ID im selben Stapel.

5
Andrew Savinykh

Wenn Sie wirklich mehrere Threads gleichzeitig ausführen müssen, können Sie die Option ignore_dup_key Auf dem Primärschlüssel aktivieren.

Dies gibt nur eine Warnung anstelle eines Fehlers aus, wenn eine Einfügung zu einer doppelten Schlüsselverletzung führen würde. Aber anstatt zu scheitern, werden die Zeilen verworfen, die beim Einfügen die Verletzung der Eindeutigkeit verursachen würden.

CREATE TABLE [dbo].[DeleteMe](
[id] [uniqueidentifier] NOT NULL,
[Value] [varchar](max) NULL,
CONSTRAINT [PK_DeleteMe] 
PRIMARY KEY ([id] ASC) 
WITH (IGNORE_DUP_KEY = ON));

Beispiel auf sqlfiddle

Paul Whites ausführliche Erklärung zur Option IGNORE_DUP_KEY . Danke Paul.

10
Aaron

Der Name der Tabelle DeleteMe deutet darauf hin, dass Sie IDs in dieser Tabelle sammeln und dann regelmäßig Zeilen mit diesen IDs aus einer anderen permanenten Tabelle löschen. Ist es wahr?

Wenn dies der Fall ist, können Sie DeleteMe erlauben, doppelte IDs zu haben. Haben Sie einfach einen nicht eindeutigen Index für id anstelle von eindeutig (und da iduniqueidentifier ist, ist es sinnvoll, diesen Index auch nicht gruppiert zu machen):

CREATE TABLE [dbo].[DeleteMe](
    [id] [uniqueidentifier] NOT NULL,
    [Value] [varchar](max) NULL,
)

CREATE NONCLUSTERED INDEX [IX_ID] ON [dbo].[DeleteMe]
(
    [id] ASC
)

Wenn Sie DeleteMe verwenden, um Zeilen aus einem anderen MainTable wie diesem zu entfernen, spielt es keine Rolle, ob id in DeleteMe eindeutig ist oder nicht:

DELETE FROM MainTable
WHERE MainTable.ID IN (SELECT id FROM DeleteMe)

Übrigens wird die Abfrage zum Auffüllen von DeleteMe ebenfalls viel einfacher:

INSERT INTO DeleteMe (id, Value)
SELECT #t.id, 'test' 
FROM #t
0