2012-05-22 9 views
19

Tengo las siguientes tablas:salto sobre/ignoran las filas duplicadas en el inserto

DataValue

DateStamp ItemId Value 
---------- ------ ----- 
2012-05-22 1  6541 
2012-05-22 2  12321 
2012-05-21 3  32 

tmp_holding_DataValue

DateStamp ItemId Value 
---------- ------ ----- 
2012-05-22 1  6541 
2012-05-22 4  87 
2012-05-21 5  234 

DateStamp y ItemId son las columnas de clave principal.

que estoy haciendo un inserto que se ejecuta periódicamente durante todo el día (en un procedimiento almacenado):

insert into DataValue(DateStamp, ItemId, Value) 
select DateStamp, ItemId, Value from tmp_holding_DataValue; 

Esto mueve los datos desde la mesa de recogida (tmp_holding_DataValue) a través en la tabla de datos principal (DataValue). La mesa de espera se trunca.

El problema es que, como en el ejemplo, la tabla de espera podría contener elementos que ya existen en la tabla principal. Como la clave no permitirá valores duplicados, el procedimiento fallará.

Una opción sería poner una cláusula WHERE en el proceso de inserción, pero la tabla de datos principal tiene 10 millones + filas, y esto podría llevar mucho tiempo.

¿Hay alguna otra manera de obtener el procedimiento para omitir/ignorar los duplicados mientras se intenta insertar?

+0

¿Qué ocurre si la columna 'Valor' en la tabla de espera es diferente, p. para la primera fila, ¿es '3253' en lugar de' 6541'? ¿Es eso todavía un duplicado? De lo contrario, ¿es algo que desea actualizar (por ejemplo, agregue '6541 + 3253' en la tabla fuente) o simplemente reemplace? –

+0

La columna de valor no importa, si se ignora su diferente, lo que está en el DataValue ya para esa marca de fecha debe dejarse como está – finoutlook

+0

También es muy útil etiquetar su pregunta con la * versión * mínima de SQL Server que usted Necesito apoyarNo ofrecí una solución 'MERGE' porque inicialmente no tenía ni idea de qué versión estaba usando. –

Respuesta

22
INSERT dbo.DataValue(DateStamp, ItemId, Value) 
SELECT DateStamp, ItemId, Value 
FROM dbo.tmp_holding_DataValue AS t 
WHERE NOT EXISTS (SELECT 1 FROM dbo.DataValue AS d 
WHERE DateStamp = t.DateStamp 
AND ItemId = t.ItemId); 
+3

Esto funcionaría, pero me preguntaba si había algo más rápido en el caso de que la tabla DataValue finalmente termine con 100 millones de filas – finoutlook

+0

Si la clave principal está agrupada y la tabla de espera tiene un índice equivalente, entonces no debería ser un problema (o al menos no más un problema que cualquier otra solución que busque duplicados). ¿Alguna vez la tabla de espera tiene datos "antiguos", o añades siempre datos algo nuevos? Podría agregar cláusulas where que limiten la fecha a algo razonable, como hace dos días, y si 'DateStamp' es la columna principal en la clave principal, esto debería ayudar un poco. Pero solo si siempre tienes nuevos datos en la mesa de espera. –

+1

Gracias fui con esta solución – finoutlook

15

En SQL Server 2008+:

MERGE 
INTO dataValue dv 
USING tmp_holding_DataValue t 
ON  t.dateStamp = dv.dateStamp 
     AND t.itemId = dv.itemId 
WHEN NOT MATCHED THEN 
INSERT (dateStamp, itemId, value) 
VALUES (dateStamp, itemId, value) 
/* 
WHEN MATCHED THEN 
UPDATE 
     value = t.value 
*/ 
-- Uncomment above to rewrite duplicates rather than ignore them 
+0

Pensé en usar una fusión, pero con DataValue siendo 10m + filas, y tmp_holding_DataValue siendo alrededor de 2m filas, pensé que tomaría mucho tiempo, ya que estaría revisando todos los datos al inicio de la tabla. – finoutlook

+0

@finoutlook: en otras palabras, ¿se optimizó prematuramente? Solo inténtalo. – Quassnoi

+1

Siempre planeo lo peor y espero lo mejor ..! Le daré una oportunidad – finoutlook

15

puede asignar el PK como Ignorar clave duplicada = Sí. Luego solo ignorará una clave duplicada de advertencia y continuará. No estoy adivinando. Probé esto.

Lo que encontré es que no puedo hacer esto es SMSS. Tiene que soltar y volver a crear el índice a través de secuencia de comandos. Pero puede hacer clic con el botón derecho en el índice, seleccionar soltar y volver a crear, y luego simplemente cambiar Ignorar clave duplicada = Sí. Para mí, SMSS no mostró el cambio de inmediato.

IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[PKallowDup]') AND name = N'PK_PKallowDup') 
ALTER TABLE [dbo].[PKallowDup] DROP CONSTRAINT [PK_PKallowDup] 
GO 

USE [test] 
GO 

/****** Object: Index [PK_PKallowDup] Script Date: 05/22/2012 10:23:13 ******/ 
ALTER TABLE [dbo].[PKallowDup] ADD CONSTRAINT [PK_PKallowDup] PRIMARY KEY CLUSTERED 
(
    [PK] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = ON, IGNORE_DUP_KEY = ON, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 

O creo que se podría utilizar una combinación externa

INSERT dbo.DataValue(DateStamp, ItemId, Value) 
SELECT t.DateStamp, t.ItemId, t.Value 
    FROM dbo.tmp_holding_DataValue AS t 
    left join dbo.DataValue AS d 
    on d.DateStamp = t.DateStamp 
    AND d.ItemId = t.ItemId 
WHERE d.DateStamp is null 
    and d.ItemId in null 
+0

Vi esto sugerido en otro lugar, pero quería mantener la clave principal como estaba. Es bastante crítico que no haya duplicados en la tabla final de 'DataValue'. – finoutlook

+4

¿Por qué es esto complicado? 'WITH (IGNORE_DUP_KEY = ON);' También @finoutlook ¿probó esta opción en una tabla simple? Todavía es una clave principal, y los duplicados aún no están permitidos. La configuración 'IGNORE_DUP_KEY' simplemente controla cómo SQL Server maneja violaciones de claves (con una excepción o con un mensaje de estado simple que dice' Se ignoró la clave duplicada'). –

+2

Todavía hay un PK y se aplica. La diferencia es que una violación PK es solo una advertencia y continúa insertando filas cuando Ignore Duplicate Key = Yes. – Paparazzi

0

me encontré con un requisito similar que terminó lanzando el mismo error de clave duplicada, y luego la idea era seleccionar varias columnas que son distintos (primario), mientras que regresan también otras columnas, check:

INSERT INTO DataValue(DateStamp, ItemId, Value) 
SELECT DISTINCT DateStamp, ItemId, MAX(Value) AS Value 
FROM tmp_holding_DataValue 
GROUP BY DateStamp, ItemId 

De hecho, el objetivo podría lograrse sin la distinto, así ya que la diversión agregada ction MAX elegirá un único valor.

Cuestiones relacionadas