Подтвердить что ты не робот

Вставить запись только в том случае, если запись еще не существует в таблице

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

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

4b9b3361

Ответ 1

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

NB: Обычно используется 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);

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

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

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

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

Тест 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

Ответ 2

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

Ответ 3

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

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

Ответ 4

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

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

В стороне: я часто волновался, что бросать/ловить исключение было хитом производительности, но я сделал некоторое тестирование (на SQL Server 2005), и это было незначительно.