Suponga una estructura de tabla de MyTable(KEY, datafield1, datafield2...)
.
A menudo quiero actualizar un registro existente o insertar un nuevo registro si no existe.
Esencialmente:
IF (key exists)
run update command
ELSE
run insert command
¿Cuál es la mejor manera de escribir esto?
Respuestas:
No te olvides de las transacciones. El rendimiento es bueno, pero el enfoque simple (SI EXISTE ...) es muy peligroso.
Cuando varios subprocesos intenten realizar la inserción o actualización, puede obtener fácilmente la violación de la clave principal.
Las soluciones proporcionadas por @Beau Crawford y @Esteban muestran una idea general pero propensa a errores.
Para evitar puntos muertos y violaciones de PK, puede usar algo como esto:
o
fuente
Vea mi respuesta detallada a una pregunta anterior muy similar
@Beau Crawford's es una buena manera en SQL 2005 y versiones posteriores, aunque si está otorgando representante, debería ir al primer tipo en SO . El único problema es que para las inserciones siguen siendo dos operaciones de E / S.
MS Sql2008 introduce
merge
desde el estándar SQL: 2003:Ahora es realmente solo una operación de E / S, pero un código horrible :-(
fuente
upsert
que casi todos los demás proveedores de bases de datos decidieron admitir. Laupsert
sintaxis es una forma mucho más agradable de hacer esto, por lo que al menos MS también debería haberla admitido. No es como si fuera la única palabra clave no estándar en T-SQLMERGE
sintaxis.HOLDLOCK
operación de fusión en situaciones de alta concurrencia.Haz un UPSERT:
http://en.wikipedia.org/wiki/Upsert
fuente
Mucha gente le sugerirá que use
MERGE
, pero le advierto que no lo haga. Por defecto, no lo protege de concurrencia y condiciones de carrera más que múltiples declaraciones, e introduce otros peligros:http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/
Incluso con esta sintaxis "más simple" disponible, sigo prefiriendo este enfoque (el manejo de errores se omite por brevedad):
Mucha gente sugerirá de esta manera:
Pero todo lo que se logra es garantizar que deba leer la tabla dos veces para ubicar las filas que se actualizarán. En la primera muestra, solo necesitará ubicar las filas una vez. (En ambos casos, si no se encuentran filas de la lectura inicial, se produce una inserción).
Otros sugerirán de esta manera:
Sin embargo, esto es problemático si no es por otra razón que dejar que SQL Server capture excepciones que podría haber evitado en primer lugar es mucho más costoso, excepto en el raro escenario en el que casi cada inserción falla. Aquí lo pruebo:
fuente
UPDATE target SET col = tmp.col FROM target INNER JOIN #tmp ON <key clause>; INSERT target(...) SELECT ... FROM #tmp AS t WHERE NOT EXISTS (SELECT 1 FROM target WHERE key = t.key);
Editar:
Por desgracia, incluso en mi propio detrimento, debo admitir que las soluciones que hacen esto sin una selección parecen ser mejores, ya que logran la tarea con un paso menos.
fuente
Si desea UPSERTAR más de un registro a la vez, puede usar la declaración ANSI SQL: 2003 DML MERGE.
Consulte la declaración Mimicking MERGE en SQL Server 2005 .
fuente
Aunque es bastante tarde para comentar sobre esto, quiero agregar un ejemplo más completo usando MERGE.
Dichas declaraciones Insert + Update generalmente se llaman declaraciones "Upsert" y se pueden implementar usando MERGE en SQL Server.
Aquí se da un muy buen ejemplo: http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
Lo anterior explica los escenarios de bloqueo y concurrencia también.
Citaré lo mismo para referencia:
fuente
Reemplace los nombres de tabla y campo por lo que necesite. Tenga cuidado con el uso de la condición ON . Luego establezca el valor apropiado (y el tipo) para las variables en la línea DECLARE.
Salud.
fuente
Puede usar la
MERGE
Declaración, esta declaración se utiliza para insertar datos si no existe o actualizar si existe.fuente
Si va a ACTUALIZAR si no hay filas actualizadas, luego INSERTAR ruta, considere hacer INSERTAR primero para evitar una condición de carrera (suponiendo que no haya DELETE interviniente)
Además de evitar una condición de carrera, si en la mayoría de los casos el registro ya existirá, esto hará que INSERT falle, desperdiciando la CPU.
Usar MERGE probablemente sea preferible para SQL2008 en adelante.
fuente
Eso depende del patrón de uso. Uno tiene que mirar el panorama general de uso sin perderse en los detalles. Por ejemplo, si el patrón de uso es 99% de actualizaciones después de que se haya creado el registro, entonces 'UPSERT' es la mejor solución.
Después de la primera inserción (hit), serán todas las actualizaciones de declaraciones individuales, sin ifs o buts. La condición 'dónde' en el inserto es necesaria; de lo contrario, insertará duplicados y no querrá lidiar con el bloqueo.
fuente
MS SQL Server 2008 presenta la declaración MERGE, que creo que es parte del estándar SQL: 2003. Como muchos han demostrado, no es un gran problema manejar casos de una fila, pero cuando se trata de grandes conjuntos de datos, se necesita un cursor, con todos los problemas de rendimiento que se presentan. La declaración MERGE será muy bienvenida cuando se trate de grandes conjuntos de datos.
fuente
Antes de que todos salten a HOLDLOCK-s por miedo de estos usuarios infames que ejecutan sus sprocs directamente :-) déjenme señalar que deben garantizar la unicidad de los nuevos PK-s por diseño (claves de identidad, generadores de secuencia en Oracle, índices únicos para ID-s externos, consultas cubiertas por índices). Ese es el alfa y omega del problema. Si no tiene eso, ningún HOLDLOCK-s del universo lo salvará y si lo tiene, entonces no necesita nada más que UPDLOCK en la primera selección (o para usar la actualización primero).
Las Sprocs normalmente se ejecutan en condiciones muy controladas y con la suposición de un llamador confiable (nivel medio). Lo que significa que si un patrón de inserción simple (actualización + inserción o combinación) alguna vez ve PK duplicada, eso significa un error en su diseño de tabla o nivel medio y es bueno que SQL grite una falla en tal caso y rechace el registro. Colocar un HOLDLOCK en este caso equivale a comer excepciones y tomar datos potencialmente defectuosos, además de reducir su rendimiento.
Dicho esto, el uso de MERGE o UPDATE y luego INSERT es más fácil en su servidor y menos propenso a errores, ya que no tiene que acordarse de agregar (UPDLOCK) a la primera selección. Además, si está haciendo inserciones / actualizaciones en pequeños lotes, necesita conocer sus datos para decidir si una transacción es apropiada o no. Es solo una colección de registros no relacionados, entonces la transacción "envolvente" adicional será perjudicial.
fuente
¿Importan realmente las condiciones de carrera si primero intentas una actualización seguida de una inserción? Digamos que tiene dos hilos que desean establecer un valor para la clave clave :
Hilo 1: valor = 1
Hilo 2: valor = 2
Ejemplo de escenario de condición de carrera
El otro hilo falla con la inserción (con clave duplicada de error) - hilo 2.
Pero; en un entorno multiproceso, el programador del sistema operativo decide el orden de ejecución del subproceso; en el escenario anterior, donde tenemos esta condición de carrera, fue el sistema operativo el que decidió la secuencia de ejecución. Es decir: es incorrecto decir que "hilo 1" o "hilo 2" fue "primero" desde el punto de vista del sistema.
Cuando el tiempo de ejecución es tan cercano para el hilo 1 y el hilo 2, el resultado de la condición de carrera no importa. El único requisito debería ser que uno de los hilos definiera el valor resultante.
Para la implementación: si la actualización seguida de la inserción produce el error "clave duplicada", esto debe tratarse como correcto.
Además, uno nunca debería asumir que el valor en la base de datos es el mismo que escribió el último.
fuente
En SQL Server 2008 puede usar la instrucción MERGE
fuente
Intenté la solución a continuación y funciona para mí, cuando se produce una solicitud concurrente para la instrucción de inserción.
fuente
Puedes usar esta consulta. Trabaja en todas las ediciones de SQL Server. Es simple y claro. Pero necesitas usar 2 consultas. Puedes usar si no puedes usar MERGE
NOTA: explique las respuestas negativas
fuente
Si usa ADO.NET, el DataAdapter maneja esto.
Si desea manejarlo usted mismo, esta es la forma:
Asegúrese de que haya una restricción de clave principal en su columna de clave.
Entonces tú:
También puede hacerlo al revés, es decir, primero realice la inserción y actualice si la inserción falla. Normalmente, la primera forma es mejor, porque las actualizaciones se realizan con más frecuencia que las inserciones.
fuente
Hacer un if existe ... más ... implica hacer dos solicitudes como mínimo (una para verificar, una para tomar medidas) El siguiente enfoque requiere solo uno donde existe el registro, dos si se requiere una inserción:
fuente
Usualmente hago lo que varios de los otros carteles han dicho con respecto a verificar que exista primero y luego hacer lo que sea la ruta correcta. Una cosa que debe recordar al hacer esto es que el plan de ejecución en caché por sql podría no ser óptimo para una ruta u otra. Creo que la mejor manera de hacer esto es llamar a dos procedimientos almacenados diferentes.
Ahora, no sigo mis propios consejos muy a menudo, así que tómalo con un grano de sal.
fuente
Haga una selección, si obtiene un resultado, actualícelo, si no, créelo.
fuente