Hace varios meses aprendí de una respuesta en Stack Overflow cómo realizar múltiples actualizaciones a la vez en MySQL usando la siguiente sintaxis:
INSERT INTO table (id, field, field2) VALUES (1, A, X), (2, B, Y), (3, C, Z)
ON DUPLICATE KEY UPDATE field=VALUES(Col1), field2=VALUES(Col2);
Ahora he cambiado a PostgreSQL y aparentemente esto no es correcto. Se refiere a todas las tablas correctas, así que supongo que se trata de diferentes palabras clave que se utilizan, pero no estoy seguro de en qué parte de la documentación de PostgreSQL se trata.
Para aclarar, quiero insertar varias cosas y, si ya existen, actualizarlas.
sql
postgresql
upsert
sql-merge
Teifion
fuente
fuente
Respuestas:
PostgreSQL desde la versión 9.5 tiene sintaxis UPSERT , con cláusula ON CONFLICT . con la siguiente sintaxis (similar a MySQL)
Buscar en los archivos del grupo de correo electrónico de postgresql "upsert" lleva a encontrar un ejemplo de hacer lo que posiblemente quiera hacer, en el manual :
Posiblemente hay un ejemplo de cómo hacer esto de forma masiva, utilizando CTE en 9.1 y superior, en la lista de correo de los hackers :
Vea la respuesta de a_horse_with_no_name para un ejemplo más claro.
fuente
excluded
refiere aquí en la primera solución?excluded
tabla especial le da acceso a los valores que intentaba INSERTAR en primer lugar.Advertencia: esto no es seguro si se ejecuta desde varias sesiones al mismo tiempo (ver las advertencias a continuación).
Otra forma inteligente de hacer un "UPSERT" en postgresql es hacer dos declaraciones secuenciales de ACTUALIZACIÓN / INSERCIÓN que están diseñadas para tener éxito o no tienen efecto.
La ACTUALIZACIÓN tendrá éxito si ya existe una fila con "id = 3"; de lo contrario, no tiene ningún efecto.
INSERT solo tendrá éxito si la fila con "id = 3" aún no existe.
Puede combinar estos dos en una sola cadena y ejecutarlos con una sola instrucción SQL ejecutada desde su aplicación. Ejecutarlos juntos en una sola transacción es muy recomendable.
Esto funciona muy bien cuando se ejecuta de forma aislada o en una tabla bloqueada, pero está sujeto a condiciones de carrera que significan que aún podría fallar con un error de clave duplicada si se inserta una fila al mismo tiempo, o podría terminar sin una fila insertada cuando una fila se elimina al mismo tiempo . Una
SERIALIZABLE
transacción en PostgreSQL 9.1 o superior lo manejará de manera confiable a costa de una tasa de falla de serialización muy alta, lo que significa que tendrá que volver a intentarlo mucho. Vea por qué es tan complicado upsert , que analiza este caso con más detalle.Este enfoque también está sujeto a actualizaciones perdidas de forma
read committed
aislada, a menos que la aplicación verifique los recuentos de filas afectadas y verifique que ya sea una filainsert
o laupdate
afectada .fuente
... where not exists (select 1 from table where id = 3);
read committed
el aislamiento a menos que su aplicación comprueba para asegurarse de que elinsert
o laupdate
tienen un recuento de filas que no sea cero. Ver dba.stackexchange.com/q/78510/7788Con PostgreSQL 9.1 esto se puede lograr utilizando un CTE grabable ( expresión de tabla común ):
Ver estas entradas de blog:
Tenga en cuenta que esta solución no evita una violación de clave única, pero no es vulnerable a las actualizaciones perdidas.
Vea el seguimiento de Craig Ringer en dba.stackexchange.com
fuente
UPDATE
filas afectadas.En PostgreSQL 9.5 y posteriores puedes usar
INSERT ... ON CONFLICT UPDATE
.Ver la documentación .
Un MySQL
INSERT ... ON DUPLICATE KEY UPDATE
se puede reformular directamente a aON CONFLICT UPDATE
. Tampoco es la sintaxis estándar de SQL, ambas son extensiones específicas de la base de datos. Hay buenas razonesMERGE
por las que no se usó para esto , no se creó una nueva sintaxis solo por diversión. (La sintaxis de MySQL también tiene problemas que significan que no se adoptó directamente).por ejemplo, configuración dada:
la consulta MySQL:
se convierte en:
Diferencias:
Usted debe especificar el nombre de la columna (o el nombre de restricción única) a utilizar para el control de la unicidad. Eso es
ON CONFLICT (columnname) DO
Se
SET
debe usar la palabra clave , como si fuera unaUPDATE
declaración normalTambién tiene algunas características agradables:
Usted puede tener una
WHERE
cláusula en suUPDATE
(lo que le permite girar con eficaciaON CONFLICT UPDATE
enON CONFLICT IGNORE
ciertos valores)Los valores propuestos para la inserción están disponibles como la variable de fila
EXCLUDED
, que tiene la misma estructura que la tabla de destino. Puede obtener los valores originales en la tabla utilizando el nombre de la tabla. Entonces, en este casoEXCLUDED.c
será10
(porque eso es lo que intentamos insertar) y"table".c
será3
porque ese es el valor actual en la tabla. Puede usar uno o ambos en lasSET
expresiones y laWHERE
cláusula.Para obtener más información sobre upsert, consulte ¿Cómo UPSERT (FUSIÓN, INSERCIÓN ... EN LA ACTUALIZACIÓN DUPLICADA) en PostgreSQL?
fuente
ON DUPLICATE KEY UPDATE
. Descargué Postgres 9.5 e implementé su código, pero extrañamente ocurre el mismo problema en Postgres: el campo en serie de la clave primaria no es consecutivo (hay espacios entre las inserciones y las actualizaciones). ¿Alguna idea de lo que está pasando aquí? ¿Esto es normal? ¿Alguna idea de cómo evitar este comportamiento? Gracias.SERIAL
/SEQUENCE
oAUTO_INCREMENT
no tener huecos. Si necesita secuencias sin espacios, son más complejas; necesita usar una mesa de mostrador por lo general. Google te dirá más. Pero tenga en cuenta que las secuencias sin espacios evitan toda concurrencia de inserción.BEGIN ... EXCEPTION ...
ejecuta en una subtransacción que se revierte en caso de error, su incremento de secuencia se revierte siINSERT
falla.Estaba buscando lo mismo cuando vine aquí, pero la falta de una función genérica "upsert" me molestó un poco, así que pensé que podría pasar la actualización e insertar sql como argumentos en esa función del manual.
eso se vería así:
y tal vez para hacer lo que inicialmente quería hacer, lote "upsert", podría usar Tcl para dividir sql_update y hacer un bucle de las actualizaciones individuales, el impacto de rendimiento será muy pequeño, consulte http://archives.postgresql.org/pgsql- rendimiento / 2006-04 / msg00557.php
el costo más alto es ejecutar la consulta desde su código, en el lado de la base de datos el costo de ejecución es mucho menor
fuente
DELETE
menos que bloquee la tabla o esté enSERIALIZABLE
aislamiento de transacción en PostgreSQL 9.1 o superior.No hay un comando simple para hacerlo.
El enfoque más correcto es usar la función, como la de los documentos .
Otra solución (aunque no es tan segura) es hacer una actualización con el retorno, verificar qué filas fueron actualizaciones e insertar el resto de ellas.
Algo en la línea de:
suponiendo que se devolvió id: 2:
Por supuesto, se rescatará tarde o temprano (en un entorno concurrente), ya que aquí hay una clara condición de carrera, pero por lo general funcionará.
Aquí hay un artículo más extenso y completo sobre el tema .
fuente
Personalmente, he configurado una "regla" adjunta a la declaración de inserción. Supongamos que tenía una tabla de "dns" que registraba los hits de dns por cliente por tiempo:
Quería poder volver a insertar filas con valores actualizados o crearlas si aún no existían. Tecleado el customer_id y la hora. Algo como esto:
Actualización: Esto tiene el potencial de fallar si están ocurriendo inserciones simultáneas, ya que generará excepciones de violencia únicas. Sin embargo, la transacción no terminada continuará y tendrá éxito, y solo necesita repetir la transacción terminada.
Sin embargo, si hay toneladas de inserciones sucediendo todo el tiempo, querrá poner un bloqueo de tabla alrededor de las instrucciones de inserción: el bloqueo SHARE ROW EXCLUSIVE evitará cualquier operación que pueda insertar, eliminar o actualizar filas en su tabla de destino. Sin embargo, las actualizaciones que no actualizan la clave única son seguras, por lo que si no realiza ninguna operación, utilice bloqueos de aviso.
Además, el comando COPIA no usa REGLAS, por lo que si está insertando con COPIA, deberá usar desencadenantes.
fuente
Yo uso esta función fusionar
fuente
update
primero y luego verificar el número de filas actualizadas. (Ver la respuesta de Ahmad)Personalizo la función "upsert" anterior, si desea INSERTAR Y REEMPLAZAR:
``
Y después de ejecutar, haga algo como esto:
Es importante poner una coma doble en dólares para evitar errores de compilación
fuente
Similar a la respuesta que más me gustó, pero funciona un poco más rápido:
(fuente: http://www.the-art-of-web.com/sql/upsert/ )
fuente
Tengo el mismo problema para administrar la configuración de la cuenta que los pares de nombre y valor. El criterio de diseño es que diferentes clientes podrían tener diferentes conjuntos de configuraciones.
Mi solución, similar a JWP es borrar y reemplazar en masa, generando el registro de fusión dentro de su aplicación.
Esto es bastante a prueba de balas, independiente de la plataforma y dado que nunca hay más de aproximadamente 20 configuraciones por cliente, esto es solo 3 llamadas db de carga bastante baja, probablemente el método más rápido.
La alternativa de actualizar filas individuales (buscar excepciones y luego insertarlas) o alguna combinación de ellas es un código horrible, lento y a menudo se rompe porque (como se mencionó anteriormente) el manejo de excepciones SQL no estándar cambia de db a db, o incluso de lanzamiento a lanzamiento.
fuente
REPLACE INTO
aINSERT INTO ... ON DUPLICATE KEY UPDATE
, lo que puede causar un problema si usa disparadores. Terminará ejecutando eliminar e insertar disparadores / reglas, en lugar de actualizar los.De acuerdo con la documentación de PostgreSQL de la
INSERT
declaración ,ON DUPLICATE KEY
no se admite el manejo del caso. Esa parte de la sintaxis es una extensión propietaria de MySQL.fuente
MERGE
también es realmente más una operación OLAP; consulte stackoverflow.com/q/17267417/398670 para obtener una explicación. No define la semántica de concurrencia y la mayoría de las personas que lo usan para upsert solo están creando errores.fuente
Para fusionar conjuntos pequeños, usar la función anterior está bien. Sin embargo, si está fusionando grandes cantidades de datos, le sugiero que investigue http://mbk.projects.postgresql.org
La mejor práctica actual que conozco es:
fuente
ACTUALIZAR devolverá el número de filas modificadas. Si usa JDBC (Java), puede verificar este valor contra 0 y, si no se han visto afectadas las filas, active INSERT en su lugar. Si usa algún otro lenguaje de programación, quizás aún pueda obtener el número de filas modificadas, consulte la documentación.
Puede que esto no sea tan elegante, pero tiene un SQL mucho más simple que es más trivial de usar desde el código de llamada. De manera diferente, si escribe la secuencia de comandos de diez líneas en PL / PSQL, probablemente debería tener una prueba unitaria de uno u otro tipo solo por ella.
fuente
Editar: esto no funciona como se esperaba. A diferencia de la respuesta aceptada, esto produce infracciones de clave únicas cuando dos procesos llaman repetidamente
upsert_foo
simultáneamente.Eureka! Descubrí una forma de hacerlo en una consulta: use
UPDATE ... RETURNING
para probar si alguna fila se vio afectada:Se
UPDATE
debe hacer en un procedimiento separado porque, desafortunadamente, este es un error de sintaxis:Ahora funciona como se desea:
fuente