2010-07-16 21 views

Respuesta

24

No dice qué versión de SQL Server. Si SQL Server 2008 se puede utilizar MERGE

NB: Es habitual utilizar la combinación de una Upsert que es lo que originalmente pensaba que la pregunta estaba pidiendo pero es válida sin la cláusula WHEN MATCHED y acaba con una cláusula WHEN NOT MATCHED lo mismo ocurre con el trabajo para este caso también 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 ejecución cuesta los dos parecen más o menos iguales cuando una inserción que hay que hacer ...

Link to plan images for first run

pero en la segunda pasada cuando no hay un inserto que ser hecho de Mateo la respuesta parece menor costo. No estoy seguro de si hay alguna manera de mejorar esto.

Link to plan images for second run

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 
+0

No estoy seguro de qué versión estoy usando actualmente. ¿Cómo funcionaría la fusión? –

+0

@Mega - He actualizado mi respuesta con un ejemplo de cómo 'MERGE' podría usarse para este caso. Revisaré las estadísticas de ejecución para ver si hay alguna diferencia entre los 2 enfoques. –

+3

El problema es la corrección, no el rendimiento. 'SI NO EXISTE (SELECCIONA ...) INSERT' causará errores duplicados bajo carga, garantizados. –

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

-1 ya que esta es ** la forma de sureshot ** de obtener errores duplicados bajo alta carga. SELECT e INSERT se ejecutan en momentos diferentes y no hay nada que evite que dos subprocesos concurrentes intenten insertar el mismo valor. MERGE, según lo publicado por Martin, es una solución adecuada –

1

En resumen, se necesita una mesa garantizada para ofrecerle la posibilidad 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 .... 
        ) 

que he visto en esta variación el salvaje también:

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

Tengo que votar para agregar un CONSTRAINT. Es la respuesta más simple y más robusta. Quiero decir, mirando lo complicadas que son las otras respuestas, diría que es mucho más difícil hacerlo bien (y mantenerse en lo cierto).

Las desventajas: [1] al leer el código, no es obvio que se aplica la exclusividad en el DB [2] que el código del cliente debe conocer para detectar una excepción. En otras palabras, el tipo que viene después podría preguntarse "¿cómo funcionó esto alguna vez?"

Aparte de eso: solía preocuparme de que lanzar/atrapar la excepción fuera un golpe de rendimiento, pero hice algunas pruebas (en SQL Server 2005) y no fue significativo.

Cuestiones relacionadas