Вставить запись, только если запись еще не существует в таблице - PullRequest
30 голосов
/ 17 июля 2010

Мне интересно, есть ли способ вставить запись в таблицу, только если таблица еще не содержит эту запись?

Есть ли запрос, который это сделает, или мне понадобитсяхранимая процедура?

Ответы [ 4 ]

25 голосов
/ 17 июля 2010

Вы не говорите, какая версия SQL Server.Если в SQL Server 2008 вы можете использовать MERGE

Примечание: обычно для объединения используется слияние, которое, как я изначально думал, задавался вопрос, но оно действительно без 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);

С точки зрения затрат на выполнение, при выполнении вставки они выглядят примерно одинаково ...

Ссылка на изображения плана для первого запуска

но на втором проходе, когда вставка не выполняется, ответ Мэтью выглядит дешевле.Я не уверен, есть ли способ улучшить это.

Ссылка на изображения плана для второго запуска

Тестовый сценарий

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
12 голосов
/ 17 июля 2010
IF NOT EXISTS 
    (SELECT {Columns} 
    FROM {Table} 
    WHERE {Column1 = SomeValue AND Column2 = SomeOtherVale AND ...}) 
INSERT INTO {Table} {Values}
1 голос
/ 17 июля 2010

Короче говоря, вам нужна таблица, которая гарантированно предоставит вам возможность вернуть одну строку:

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 голосов
/ 17 июля 2010

Я должен проголосовать за добавление CONSTRAINT.Это самый простой и надежный ответ.Я имею в виду, глядя на то, насколько сложны другие ответы, я бы сказал, что их гораздо сложнее понять (и сохранить правильность).

Недостатки: [1] из чтения уникальности кода не очевидноприменяется в БД [2], код клиента должен знать, чтобы перехватить исключение.Другими словами, парень, идущий за вами, может задаться вопросом: «Как это вообще работало?»Server 2005), и это не было значительным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...