2009-07-16 30 views

Respuesta

13

Aunque mi consejo enfático es estructurar su sql para no intentar insertos duplicados (el fragmento de Philip Kelley es probablemente lo que necesita), quiero mencionar que un error en una declaración no necesariamente causa un retroceso .

A menos que XACT_ABORT sea ON, una transacción no se retrotraerá automáticamente si se produce un error a menos que sea lo suficientemente grave como para matar la conexión. XACT_ABORT se predetermina a OFF.

Por ejemplo, el siguiente código SQL inserta con éxito tres valores en la tabla:

create table x (y int not null primary key) 

begin transaction 
insert into x(y) 
values(1) 
insert into x(y) 
values(2) 
insert into x(y) 
values(2) 
insert into x(y) 
values(3) 
commit 

a menos que configure XACT_ABORT, se produce un error en el cliente y causando la cancelación. Si por algún motivo horrible no puede evitar insertar duplicados, debe poder atrapar el error en el cliente e ignorarlo.

0

Las claves deben ser exclusivas. No hagas eso. Rediseña según sea necesario.

(si está intentando insertar, luego eliminar, y la inserción falla ... simplemente haga la eliminación primero. Revertir al error en cualquiera de las instrucciones).

+0

No, no creo que haya aclarado mi pregunta. VALORES INSERT INTO X (Y, Z) INSERT en valores X2 (Y2, Z2) INSERT en valores X3 (Y3, Z3) Digamos que la segunda instrucción provoca un error duplicado. Quiero ignorarlo y continuar ejecutando la tercera declaración. El comportamiento en una transacción es que arroja un error y existe (retrocede). – Kingamoon

8

Si por "Ignorar statments de error duplicados", para abortar la instrucción actual y continuar con la siguiente declaración sin abortar el trnsaction continuación, sólo hay que poner COMENZAR TRY .. END TRY alrededor de las sentencias:

BEGIN TRY 
    INSERT ... 
END TRY 
BEGIN CATCH /*required, but you dont have to do anything */ END CATCH 
... 
+0

Desafortunadamente, estamos usando SQL Server 2000 ahora mismo, así que no creo que las declaraciones Try .. Catch estén disponibles en esa versión. – Kingamoon

55

pienso Está buscando la opción IGNORE_DUP_KEY en su índice. Eche un vistazo a la opción IGNORE_DUP_KEY ON documentada en http://msdn.microsoft.com/en-us/library/ms186869.aspx que causa intentos de inserción duplicados para producir una advertencia en lugar de un error.

+3

Mala idea: no desea duplicar los datos en un índice único. –

+32

@Jonathan Leffler: 'IGNORE_DUP_KEY' no causará datos duplicados. Hace que SQL Server ignore la clave duplicada: no la inserta en la base de datos. –

+0

@Ian Boyd: OK - vivir y aprender es parte de la cultura SO. Gracias por la información.(No estoy seguro de si podría escribir código confiable si un INSERT tuvo éxito sin insertar lo que le pedí que inserte y sin decirme; al menos, hay una advertencia pasada, al menos. Sin embargo, veo lo que hace la función.) –

19

Ampliando su comentario a la respuesta del SquareCog, se podría hacer:

INSERT INTO X VALUES(Y,Z) WHERE Y NOT IN (SELECT Y FROM X) 
INSERT INTO X2 VALUES(Y2,Z2) WHERE Y2 NOT IN (SELECT Y FROM X2) 
INSERT INTO X3 VALUES(Y3,Z3) WHERE Y3 NOT IN (SELECT Y FROM X3) 

Aquí, supongo que la columna Y está presente en las tres tablas. Tenga en cuenta que el rendimiento será pobre si las tablas no se indexan en Y.

Oh sí, Y tiene una restricción única en él - por lo que están indexados, y esto debe llevar a cabo de manera óptima.

+2

Me gusta: nuestros scripts de implementación están diseñados para ejecutarse muchas veces de forma segura, por lo que INSERT siempre tiene criterios para evitar colisiones. – n8wrl

+2

Intenté este enfoque, pero mi servidor MSSQL (2008 R2) se queja acerca de "Sintaxis incorrecta cerca de la palabra clave 'DONDE'" - no parece gustarle WHERE después de INSERTAR. ¿Estás seguro de que tu solución es correcta? – Dai

+0

Sí, lo soy. Publique su código, preferiblemente como una nueva pregunta, y alguien lo ayudará a resolver el problema. –

4

OK. Después de probar el manejo de errores, descubrí cómo resolver el problema que estaba teniendo.

Aquí hay un ejemplo de cómo hacer este trabajo (que me haga saber si hay algo que me falta):

SET XACT_ABORT OFF ; -- > really important to set that to OFF 
BEGIN 
DECLARE @Any_error int 
DECLARE @SSQL varchar(4000) 
BEGIN TRANSACTION 
    INSERT INTO Table1(Value1) VALUES('Value1') 
    SELECT @Any_error = @@ERROR 
    IF @Any_error<> 0 AND @Any_error<>2627 GOTO ErrorHandler 

    INSERT INTO Table1(Value1) VALUES('Value1') 
    SELECT @Any_error = @@ERROR 
    IF @Any_error<> 0 AND @Any_error<>2627 GOTO ErrorHandler 

    INSERT INTO Table1(Value1) VALUES('Value2') 
    SELECT @Any_error = @@ERROR 
    IF @Any_error<> 0 AND @Any_error<>2627 GOTO ErrorHandler 

    ErrorHandler: 
     IF @Any_error = 0 OR @Any_error=2627 
     BEGIN 
      PRINT @ssql 
      COMMIT TRAN 
     END 
     ELSE 
     BEGIN 
      PRINT @ssql 
      ROLLBACK TRAN 
     END 
END 

Como resultado de la operación anterior, la Tabla 1 tendrá los siguientes valores valor1, valor2 .

2627 es el código de error para Clave duplicada por cierto.

Gracias a todos por la pronta respuesta y sugerencias útiles.

7

Me gustaría comunicarme con lo siguiente: Si el 99% de sus datos se va a insertar sin error al hacer una selección de antemano resulta en una caída de rendimiento enorme (como, en mi caso, de 200 líneas/seg. a 20 líneas/segundo en mi caso) en comparación con las inserciones "tontas" y detectar el error ocasional.

Después de ignorar los errores de "Violación de la restricción PRIMARY KEY", las cosas volvieron a ser embotelladas por otros recursos (el margen se define como "lo que no tienen los recursos de embotellamiento").

Cuál es la razón por la que llegué a esta discusión en primer lugar.

1
INSERT INTO KeyedTable(KeyField, Otherfield) 
SELECT n.* FROM 
    (SELECT 'PossibleDupeLiteral' AS KeyField, 'OtherfieldValue' AS Otherfield 
    UNION ALL 
    SELECT 'PossibleDupeLiteral', 'OtherfieldValue2' 
    ) 
LEFT JOIN KeyedTable k 
    ON k.KeyField=n.KeyField 
WHERE k.KeyField IS NULL 

Esto indica al servidor que busque los mismos datos (esperemos que de la misma manera que lo hace rápido para comprobar si hay duplicados de las llaves) e inserte nada si lo encuentra.

También me gusta la solución IGNORE_DUP_KEY, pero cualquiera que confíe en errores para detectar problemas se desconcertará cuando el servidor ignore silenciosamente sus errores de duplicación.

La razón por la que elegí este sobre solución de Philip Kelley es que se puede proporcionar varias filas de datos y sólo tienen los que faltan en realidad consiguen en:

0

Vine aquí porque yo estaba tratando de hacer lo mismo; Sabía que tenía engaños en los datos de origen, pero solo quería actualizar los datos de destino y no agregar los engaños.

Creo que un MERGE funciona bien aquí porque puede ACTUALIZAR o ELIMINAR cosas que son diferentes e INSERTAR las cosas que faltan.

Terminé haciendo esto y funcionó muy bien. Utilizo SSIS para recorrer los archivos de Excel y cargarlos en una tabla SQL "RAW" con engaños y todo. Luego ejecuto un MERGE para fusionar la tabla "raw" con la tabla de producción. Luego TRONCO la tabla "en bruto" y paso al siguiente archivo de Excel.

+2

¿Algún código de ejemplo de esa solución? – ForceMagic

+0

Si carga mucho en esta tabla, ¿por qué no simplemente limpia los duplicados cargando la tabla actual en una nueva tabla con un índice único con IGNORE_DUP_KEY = ON? Elimine su tabla original y cambie el nombre de la nueva tabla. Podrías cargar directamente en la mesa libre de impuestos. Parece que ahorraría una gran cantidad de tiempo en el futuro por un mínimo esfuerzo. – Mark

0

Use IGNORE_DUP_KEY = OFF durante la definición de la clave primaria para ignorar los duplicados durante la inserción. por ejemplo

create table X(col1.....) 

CONSTRAINT [pk_X] PRIMARY KEY CLUSTERED 
(
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 70) ON [PRIMARY] 
) ON [PRIMARY] 
0

Para el servidor SQL 2000:

 INSERT INTO t1 (ID, NAME) 
     SELECT valueid, valuename 
     WHERE NOT EXISTS 
       (SELECT 0 
       FROM t1 as t2 
       WHERE t2.ID = valueid AND t2.name = valuename) 
0

bien se podría resolver esto con una tabla temporal ..

DECLARE @RoleToAdds TABLE 
([RoleID] int, [PageID] int) 

INSERT INTO @RoleToAdds ([RoleID], [PageID]) 
    VALUES 
    (1, 2), 
    (1, 3), 
    (1, 4), 
    (2, 5) 

INSERT INTO [dbo].[RolePages] ([RoleID], [PageID]) 
    SELECT rta.[RoleID], rta.[PageID] FROM @RoleToAdds rta WHERE NOT EXISTS 
     (SELECT * FROM [RolePages] rp WHERE rp.PageID = rta.PageID AND rp.RoleID = rta.RoleID) 

Esto podría no funcionar para grandes cantidades de datos, pero por unas pocas filas debería funcionar!

Cuestiones relacionadas