Estoy usando Python para escribir en una base de datos postgres:
sql_string = "INSERT INTO hundred (name,name_slug,status) VALUES ("
sql_string += hundred + ", '" + hundred_slug + "', " + status + ");"
cursor.execute(sql_string)
Pero debido a que algunas de mis filas son idénticas, aparece el siguiente error:
psycopg2.IntegrityError: duplicate key value
violates unique constraint "hundred_pkey"
¿Cómo puedo escribir una instrucción SQL 'INSERT a menos que esta fila ya exista'?
He visto declaraciones complejas como esta recomendadas:
IF EXISTS (SELECT * FROM invoices WHERE invoiceid = '12345')
UPDATE invoices SET billed = 'TRUE' WHERE invoiceid = '12345'
ELSE
INSERT INTO invoices (invoiceid, billed) VALUES ('12345', 'TRUE')
END IF
Pero, en primer lugar, ¿es esto excesivo para lo que necesito y, en segundo lugar, cómo puedo ejecutar uno de esos como una cadena simple?
postgresql
sql-insert
upsert
AP257
fuente
fuente
Respuestas:
Postgres 9.5 (lanzado desde 2016-01-07) ofrece un comando "upsert" , también conocido como una cláusula ON CONFLICT para INSERTAR :
Resuelve muchos de los problemas sutiles con los que se puede encontrar al usar la operación concurrente, que algunas otras respuestas proponen.
fuente
INSERT INTO distributors (did, dname) VALUES (7, 'Redline GmbH') ON CONFLICT (did) DO NOTHING;
(2) INSERTAR si no existe más ACTUALIZAR -INSERT INTO distributors (did, dname) VALUES (5, 'Gizmo Transglobal'), (6, 'Associated Computing, Inc') ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname;
Estos ejemplos son del manual - postgresql.org/docs/9.5/static/sql-insert.htmlHay una buena manera de hacer INSERT condicional en PostgreSQL:
Sin embargo, este enfoque no es 100% confiable para operaciones de escritura concurrentes . Hay una condición de carrera muy pequeña entre
SELECT
elNOT EXISTS
anti-semi-join y elINSERT
propio. Se puede fallar bajo tales condiciones.fuente
RETURNS id
por ejemplo, para obtenerid
si se ha insertado o no?RETURNING id
al y de la consulta y devolverá una nueva identificación de fila o nada, si no se ha insertado ninguna fila.Un enfoque sería crear una tabla no restringida (sin índices únicos) para insertar todos sus datos y hacer una selección distinta de esa para hacer su inserción en su tabla cien.
Tan alto nivel sería. Supongo que las tres columnas son distintas en mi ejemplo, por lo que para el paso 3, cambie la unión NO SALIR para unir solo en las columnas únicas en la tabla cien.
Crear tabla temporal. Ver documentos aquí .
INSERTAR datos en la tabla temporal.
Agregue cualquier índice a la tabla temporal.
Hacer la inserción de la mesa principal.
fuente
SELECT name,name_slug,status
o*
SELECT DISTINCT name, name_slug, status FROM temp_data
?Por desgracia,
PostgreSQL
soportes niMERGE
tampocoON DUPLICATE KEY UPDATE
, por lo que tendrá que hacerlo en dos estados:Puedes envolverlo en una función:
y solo llámalo:
fuente
INSERT INTO hundred (name, name_slug, status) SELECT 'Chichester', 'chichester', NULL WHERE 'Chichester' NOT IN (SELECT NAME FROM hundred);
cualquier cantidad de veces y sigue insertando la fila.CREATE TABLE hundred (name TEXT, name_slug TEXT, status INT); INSERT INTO hundred (name, name_slug, status) SELECT 'Chichester', 'chichester', NULL WHERE 'Chichester' NOT IN (SELECT NAME FROM hundred); INSERT INTO hundred (name, name_slug, status) SELECT 'Chichester', 'chichester', NULL WHERE 'Chichester' NOT IN (SELECT NAME FROM hundred); SELECT * FROM hundred
. Hay un registroPuede hacer uso de VALUES - disponible en Postgres:
fuente
Sé que esta pregunta es de hace un tiempo, pero pensé que podría ayudar a alguien. Creo que la forma más fácil de hacerlo es a través de un disparador. P.ej:
Ejecute este código desde un indicador de psql (o como quiera ejecutar consultas directamente en la base de datos). Luego puede insertar de forma normal desde Python. P.ej:
Tenga en cuenta que, como @Thomas_Wouters ya mencionó, el código anterior aprovecha los parámetros en lugar de concatenar la cadena.
fuente
Hay una buena manera de hacer INSERT condicional en PostgreSQL usando la consulta WITH: como:
fuente
Este es exactamente el problema que enfrento y mi versión es la 9.5
Y lo resuelvo con la consulta SQL a continuación.
Espero que ayude a alguien que tiene el mismo problema con la versión> = 9.5.
Gracias por leer.
fuente
INSERTAR .. DONDE NO EXISTE es un buen enfoque. Y las condiciones de carrera se pueden evitar mediante la transacción "sobre":
fuente
Es fácil con las reglas:
Pero falla con escrituras concurrentes ...
fuente
El enfoque con la mayoría de los votos positivos (de John Doe) de alguna manera funciona para mí, pero en mi caso de las 422 filas esperadas, obtengo solo 180. No pude encontrar nada malo y no hay ningún error, así que busqué otro Enfoque simple.
Usar
IF NOT FOUND THEN
after aSELECT
just me funciona perfectamente.(descrito en la documentación de PostgreSQL )
Ejemplo de documentación:
fuente
La clase de cursor psycopgs tiene el atributo rowcount .
Por lo tanto, puede intentar ACTUALIZAR primero e INSERTAR solo si el recuento de filas es 0.
Pero dependiendo de los niveles de actividad en su base de datos, puede alcanzar una condición de carrera entre ACTUALIZAR e INSERTAR, donde otro proceso puede crear ese registro mientras tanto.
fuente
Su columna "cien" parece estar definida como clave principal y, por lo tanto, debe ser única, lo que no es el caso. El problema no es con sus datos.
Le sugiero que inserte una identificación como tipo de serie para manejar la clave primaria.
fuente
Si dice que muchas de sus filas son idénticas, finalizará la comprobación muchas veces. Puede enviarlos y la base de datos determinará si lo inserta o no con la cláusula ON CONFLICT de la siguiente manera
fuente
Estaba buscando una solución similar, tratando de encontrar SQL que funcione en PostgreSQL y HSQLDB. (HSQLDB fue lo que hizo esto difícil). Usando su ejemplo como base, este es el formato que encontré en otro lugar.
fuente
Aquí hay una función genérica de Python que, dado un nombre de tabla, columnas y valores, genera el equivalente upsert para postgresql.
importar json
fuente
La solución es simple, pero no inmediata.
Si desea utilizar esta instrucción, debe hacer un cambio en la base de datos:
después de estos cambios "INSERTAR" funcionará correctamente.
fuente