2010-07-16 14 views

Antwort

24

Sie sagen nicht, welche Version von SQL Server. Wenn SQL Server 2008 können Sie MERGE

NB verwenden: Es ist üblich, für eine Upsert verwenden Merge das ist, was ursprünglich dachte, dass ich die Frage stellt, aber es gilt ohne WHEN MATCHED Klausel und nur mit einer WHEN NOT MATCHED Klausel so funktioniert auch für diesen Fall. Beispiel Verwendung.

CREATE TABLE #A(
[id] [int] NOT NULL PRIMARY KEY CLUSTERED, 
[C] [varchar](200) NOT NULL) 


    MERGE #A AS target 
    USING (SELECT 3, 'C') AS source (id, C) 
    ON (target.id = source.id) 
    /*Uncomment for Upsert Semantics 
     WHEN MATCHED THEN 
     UPDATE SET C = source.C */ 
    WHEN NOT MATCHED THEN  
     INSERT (id, C) 
     VALUES (source.id, source.C); 

In Bezug auf die Ausführung kostet die beiden in etwa gleich aussehen, wenn ein Insert zu tun ist ...

Link to plan images for first run

aber auf dem zweiten Durchlauf, wenn kein Einsatz ist Matthäus zu tun Antwort sieht niedriger aus. Ich bin mir nicht sicher, ob es eine Möglichkeit gibt, dies zu verbessern.

Link to plan images for second run

Test Script

select * 
into #testtable 
from master.dbo.spt_values 

CREATE UNIQUE CLUSTERED INDEX [ix] ON #testtable([type] ASC,[number] ASC,[name] ASC) 


declare @name nvarchar(35)= 'zzz' 
declare @number int = 50 
declare @type nchar(3) = 'A' 
declare @low int 
declare @high int 
declare @status int = 0; 



MERGE #testtable AS target 
USING (SELECT @name, @number, @type, @low, @high, @status) AS source (name, number, [type], low, high, [status]) 
ON (target.[type] = source.[type] AND target.[number] = source.[number] and target.[name] = source.[name]) 
WHEN NOT MATCHED THEN  
INSERT (name, number, [type], low, high, [status]) 
VALUES (source.name, source.number, source.[type], source.low, source.high, source.[status]); 

set @name = 'yyy' 

IF NOT EXISTS 
    (SELECT * 
    FROM #testtable 
    WHERE [type] = @type AND [number] = @number and name = @name) 
    BEGIN 
INSERT INTO #testtable 
(name, number, [type], low, high, [status]) 
VALUES (@name, @number, @type, @low, @high, @status); 
END 
+0

Ich bin mir eigentlich nicht sicher, welche Version ich gerade verwende. Wie würde die Zusammenführung funktionieren? –

+0

@Mega - Ich habe meine Antwort mit einem Beispiel aktualisiert, wie 'MERGE' für diesen Fall verwendet werden könnte. Ich überprüfe die Ausführungsstatistiken, um zu sehen, ob es einen Unterschied zwischen den beiden Ansätzen gibt. –

+3

Das Problem ist die Richtigkeit, nicht die Leistung. 'IF NOT EXISTS (SELECT ...) INSERT' verursacht doppelte Fehler unter Last, garantiert. –

13
IF NOT EXISTS 
    (SELECT {Columns} 
    FROM {Table} 
    WHERE {Column1 = SomeValue AND Column2 = SomeOtherVale AND ...}) 
INSERT INTO {Table} {Values} 
+8

-1, da dies die ** sichere Art ist, doppelte Fehler unter hoher Last zu erhalten. SELECT und INSERT werden zu unterschiedlichen Zeiten ausgeführt, und es gibt nichts, was verhindern könnte, dass zwei gleichzeitige Threads versuchen, den gleichen Wert einzufügen. MERGE, wie von Martin gepostet, ist eine richtige Lösung –

1

Kurz gesagt, müssen Sie eine Tabelle garantiert Ihnen die Möglichkeit zu bieten, eine Zeile zurück:

Insert dbo.Table (Col1, Col2, Col3.... 
Select 'Value1', 'Value2', 'Value3',.... 
From Information_Schema.Tables 
Where Table_Schema = 'dbo' 
    And Table_Name = 'Table' 
    And Not Exists (
        Select 1 
        From dbo.Table 
        Where Col1 = 'Foo' 
         And Col2 = 'Bar' 
         And .... 
        ) 

ich diese Variante gesehen habe in die wilde auch:

Insert Table (Col1, Col2, Col3.... 
Select 'Value1', 'Value2', 'Value3'.... 
From (
     Select 1 As Num 
     ) As Z 
Where Not Exists (
        Select 1 
        From Table 
        Where Col1 = Foo 
         And Col2 = Bar 
         And .... 
        ) 
0

Ich muss für das Hinzufügen einer CONSTRAINT stimmen. Es ist die einfachste und die robusteste Antwort. Ich meine, wenn ich mir anschaue, wie kompliziert die anderen Antworten sind, würde ich sagen, dass sie viel schwieriger sind, richtig zu werden (und recht behalten).

Die Nachteile: [1] Es ist nicht offensichtlich aus dem Lesen des Codes, dass die Eindeutigkeit in der DB [2] erzwungen wird, muss der Client-Code eine Ausnahme zu fangen wissen. Mit anderen Worten, der Typ, der nach Ihnen kommt, könnte sich fragen: "Wie hat das jemals funktioniert?"

Das beiseite: Ich war besorgt, dass das Werfen/Abfangen der Ausnahme ein Leistungseinbruch war, aber ich habe einige Tests (auf SQL Server 2005) und es war nicht signifikant.

Verwandte Themen