Pregunta

Tengo un SP en SQL Server que se ejecuta cientos de veces por minuto, y tiene que comprobar el tráfico de entrada contra una base de datos. Por el momento se hace lo siguiente

INSERT INTO table
SELECT @value1,@value2 WHERE NOT EXISTS 
(SELECT * FROM table WHERE value1 = @value1 AND value2 = @value2);

Sin embargo, yo también podría ir con

IF NOT EXISTS(SELECT * FROM table WHERE value1 = @value1 AND value2 = @value2)    
   INSERT INTO table (value1,value2) VALUES (@value1,@value2);

Lo que sería más rápido? Tengo la sensación de que no hay mucha diferencia entre ellos, pero históricamente no soy muy bueno en TSQL ... = /

ACTUALIZACIÓN: ¡Vaya ... la intención de afirmar que la EXISTE utiliza más de 1 Valor de encontrar si existe un registro, por lo que una restricción única no funcionará. Editado la muestra para reflejar que ...

¿Fue útil?

Solución

Después de la adición de un tropecientos comentarios sobre esta pregunta y sus respuestas, voy a tener mi propio movimiento en la respuesta a la misma.

No esperaría que cualquier diferencia en el rendimiento entre los dos propuestos propuesto en la pregunta original. Por un lado, como se ha señalado por Ray, el segundo enfoque podría ahorrarle de hacer algunos preparativos para la inserción, pero por otro lado, un RDBMS por lo general funciona mejor con las declaraciones de lotes, como en la primera solución.

KM y DVK sugieren que la adición de una restricción UNIQUE, lo que hará que la prueba singularidad implícita, pero será necesario que añada algún tipo de control de errores en torno a su estado de INSERT. Se me hace difícil detectar qué esto debería añadir ningún rendimiento adicional, asumiendo que ya tiene un índice que cubre las dos columnas . Si usted no tiene dicho índice, agrega, y reconsiderar su necesidad de mayor rendimiento.

Si el control de la unicidad se realiza explícita o implícita no debería importar que yo sepa. Si hay algo que se gana por tener el cheque a cabo "dentro" del estómago del DBMS, que el aumento de sólo podría ser comido por sobrecarga asociada con la crianza y manejo de errores cuando existe duplicados.


El resultado final: Suponiendo un índice ya está en su lugar, si usted todavía se encuentra lusting para el rendimiento, mi recomendación es que realice pruebas empíricas sobre las tres soluciones sugeridas. Cocinar un pequeño programa que simula los datos de entrada esperados, y soplar cada una de las tres soluciones de distancia con unos mil millones de filas, incluyendo una cantidad plausible de duplicados. hacer esto, asegúrese de publicar sus resultados: -)

Otros consejos

Ambas variantes son incorrectos. Se va a insertar pares de duplicados @ valor1, valor2 @, garantizada .

La forma correcta de manejar esto es para hacer cumplir una restricción única en dos columnas e insertar siempre y manejar la violación de restricción:

ALTER TABLE Table ADD CONSTRAINT uniqueValue1Value UNIQUE (value1, values2);

e insertar:

BEGIN TRY
   INSERT INTO Table (value1, value2) VALUES (@value1, @value2);
END TRY
BEGIN CATCH
   DECLARE @error_number int, @error_message NVARCHAR(4000), @xact_state INT;
   SET @error_number = ERROR_NUMBER();
   SET @error_message = ERROR_MESSAGE();
   SET @xact_state = XACT_STATE();
   IF (@xact_state = -1)
   BEGIN
     ROLLBACK TRANSACTION;
   END
   IF (@error_number != 2627) /* 2627 is ' Cannot insert duplicate key in object ...' */
   BEGIN
      RAISERROR(N'Error inserting into Table: %i %s', 16,1, @errror_number, @error_message);
   END
ENd CATCH

Si bien estos pueden parecer complicados, hay que tener en cuenta un pequeño detalle llamado corrección . Esto es, con mucho más simple en comparación con una solución basada sugerencias de bloqueo. Esta es también la solución mas potente: ¿Tiene un solo buscan. Todas las demás soluciones necesitan por lo menos dos busca (uno para validar que se puede insertar, uno para insertar).

En un entorno casi simultánea, una INSERT concurrente puede ocurrir en el medio IF NOT EXISTS y INSERT en su segunda consulta.

Su primera consulta se colocan los bloqueos compartidos en el expediente que se examina, que no será levantada hasta el final de la consulta, por lo que será imposible insertar un nuevo registro hasta que se ejecuta la consulta.

Sin embargo, no se debe confiar únicamente en este comportamiento. Colocar una restricción adicional en el UNIQUE value.

No sólo hará que la base de datos más consistente, pero creará un índice que hará que la primera consulta de forma más rápida.

Si desea que los valores sean únicos, ¿por qué no crear una restricción única en el valor, hacer un INSERT SELECT y sin manejar con gracia error violación de restricción?

Eso sería más rápido que cualquiera de estos enfoques.

Además, su primer enfoque no funciona - por el momento de llegar a seleccionar, ya insertó el valor de modo de selección, obviamente, encontrar lo que acaba de insertar

.

acaba de hacerlo, e ignorar cualquier error (supone una restricción única en valor) ...

BEGIN TRY
    INSERT INTO Table (value) VALUES (@value);
END TRY
BEGIN CATCH
    PRINT 'it was already in there!'
END CATCH

Desde Esto va en cientos de veces por minuto , sugerencias de bloqueo debe añadirse a los selecciona y una transacción a evitar una condición de carrera

(SELECT * FROM Table WITH (UPDLOCK, HOLDLOCK)  WHERE value = @value);

Sin embargo, mi idea propuesta de basta con insertar y hacer caso omiso de cualquier error de restricción duplicado evitaría una condición de carrera también.

Si tuviera que adivinar, diría que la segunda opción sería más rápido. SQL Server no tendría que hacer ningún tipo de configuración para la inserción si la falla existe, mientras que en el primero, podría buscar algunos nombres de tabla y de campo y prepararse para un inserto que nunca sucede. Sin embargo, me gustaría probar en el analizador de consultas y ver lo que dice el plan.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top