2010-07-16 98 views

回答

24

你不說什麼版本的SQL Server。如果SQL Server 2008中您可以使用MERGE

注:這是通常的使用合併爲一的Upsert這就是我本來以爲這個問題是問,但它是有效的,而不WHEN MATCHED條款,只是用WHEN NOT MATCHED子句,以便不工作對於這種情況也。使用示例。

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); 

在執行方面花費兩個看時的插入是必須要做的大致相等......

Link to plan images for first run

但在沒有插入要做馬修第二輪答案看起來成本較低。我不確定是否有改善這種情況的方法。

Link to plan images for second run

測試腳本

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

我實際上不確定我目前使用哪個版本。合併將如何工作? – 2010-07-16 21:14:22

+0

@Mega - 我已經更新了我的答案,舉例說明MERGE如何用於這種情況。我會檢查執行統計數據,看看這兩種方法之間是否有區別。 – 2010-07-16 21:35:03

+3

問題是正確的,而不是性能。 '如果不存在(SELECT ...)INSERT'會在負載下導致重複錯誤,保證。 – 2010-07-16 22:57:54

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

-1,因爲這是在高負載下獲得重複錯誤的** sureshot方式。 SELECT和INSERT在不同的時間運行,並且沒有什麼可以阻止兩個併發線程嘗試插入相同的值。正如Martin發佈的MERGE是一個合適的解決方案 – 2010-07-16 22:46:11

1

總之,你需要保證你提供的能力表中返回一行:

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 .... 
        ) 

我已經看到了這個變化野生以及:

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

我必須投票加入CONSTRAINT。這是最簡單和最健壯的答案。我的意思是,看看其他答案有多複雜,我會說他們很難正確(並保持正確)。

缺點:[1]讀取代碼時,客戶端代碼在捕獲異常時必須知道的唯一性在DB [2]中強制執行時不明顯。換句話說,那個追隨你的人可能會想「這是怎麼回事?」除此之外:我曾經擔心拋出/捕捉異常是一個性能問題,但我做了一些測試(在SQL Server 2005上),但它並不重要。

相關問題