Insertar registro solo si el registro no existe ya en la tabla


Me pregunto si hay una manera de insertar un registro en una tabla sólo si la tabla no contiene ese registro?

¿Hay una consulta que haga esto, o necesitaré un procedimiento almacenado?

Author: p.campbell, 2010-07-17

4 answers

Usted no dice qué versión de SQL Server. Si SQL Server 2008 puede usar MERGE

NB: Es habitual usar Merge para un Upsert que es lo que originalmente pensé que la pregunta estaba haciendo, pero es válida sin la cláusula WHEN MATCHED y solo con una cláusula WHEN NOT MATCHED, así que funciona también para este caso. Ejemplo de Uso.

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 términos de costos de ejecución, los dos se ven aproximadamente iguales cuando se debe hacer una inserción...

Enlace a las imágenes del plan para first ejecutar

Pero en la segunda ejecución cuando no hay inserción que hacer la respuesta de Mateo parece menor costo. No estoy seguro de si hay una manera de mejorar esto.

Enlace a las imágenes del plan para la segunda ejecución

Script de prueba

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
 24
Author: Martin Smith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-16 22:54:14
IF NOT EXISTS 
    (SELECT {Columns} 
    FROM {Table} 
    WHERE {Column1 = SomeValue AND Column2 = SomeOtherVale AND ...}) 
INSERT INTO {Table} {Values}
 12
Author: Matthew Jones,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-16 21:11:28

En resumen, necesita una tabla garantizada para proporcionarle la capacidad de devolver una fila:

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

También he visto esta variación en la naturaleza:

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 ....
                    ) 
 1
Author: Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-16 22:55:14

Tengo que votar por agregar un CONSTRAINT. Es la respuesta más simple y robusta. Quiero decir, mirando lo complicadas que son las otras respuestas, diría que son mucho más difíciles de acertar (y mantener la derecha).

Las desventajas: [1] no es obvio al leer el código que la unicidad se impone en la base de datos [2] el código del cliente tiene que saber para capturar una excepción. En otras palabras, el tipo que viene después de usted podría preguntarse "¿cómo funcionó esto?"

Eso aparte: Solía preocuparme que lanzar / atrapar la excepción fue un éxito de rendimiento, pero hice algunas pruebas (en SQL Server 2005) y no fue significativo.

 0
Author: egrunin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-17 03:22:42