30 votes

Insérer un enregistrement uniquement si l'enregistrement n'existe pas déjà dans la table

Je me demande s'il existe un moyen d'insérer un enregistrement dans une table uniquement si la table ne contient pas déjà cet enregistrement?

Y a-t-il une requête qui fera cela, ou aurai-je besoin d'une procédure stockée?

25voto

Martin Smith Points 174101

Vous ne dites pas quelle est la version de SQL Server. Si SQL Server 2008, vous pouvez utiliser de FUSION

NB: Il est d'usage d'utiliser de Fusion pour un Upsert qui est ce que j'ai pensé à l'origine, la question a été poser mais c'est valable sans l' WHEN MATCHED clause et juste avec un WHEN NOT MATCHED clause ne sorte de travail pour ce cas également. Exemple D'Utilisation.

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

En termes de coûts d'exécution les deux l'air à peu près égales lorsque l'Insert est à faire...

Lien vers le plan des images de la première exécution

mais dans la deuxième manche quand il n'y a pas d'insérer à faire de Matthieu réponse semble plus faible coût. Je ne sais pas si il existe un moyen d'améliorer cela.

Lien vers le plan des images pour la deuxième manche

Script De Test

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

12voto

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

1voto

Thomas Points 42973

En bref, vous avez besoin d'une table garantie pour vous fournir la possibilité de retourner une ligne:

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

J'ai également vu cette variation dans la nature:

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

0voto

egrunin Points 15356

Je vote pour l'ajout d'un CONSTRAINT. C'est le plus simple et le plus robuste de réponse. Je veux dire, en regardant comment compliqué autres réponses sont je dirais qu'ils sont beaucoup plus difficiles à obtenir le droit (et garder la droite).

Les inconvénients: [1] il n'est pas évident de lire le code que l'unicité est appliquée dans la base de données [2] le code client doit savoir pour intercepter une exception. En d'autres termes, le gars à venir après vous pourriez vous demander "comment cela fonctionne?"

À part de ça: j'ai utilisé à s'inquiéter de lancer/attraper l'exception a été un gain de performance, mais j'ai fait quelques tests (sur SQL Server 2005) et il n'était pas significative.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X