cómo emular & # 8220; insertar ignorar & # 8221; y & # 8220; en actualización de clave duplicada & # 8221; (fusión de sql) con postgresql?

StackOverflow https://stackoverflow.com/questions/1009584

  •  06-07-2019
  •  | 
  •  

Pregunta

Algunos servidores SQL tienen una función en la que se omite INSERT si viola una restricción de clave principal / única. Por ejemplo, MySQL tiene INSERT IGNORE .

¿Cuál es la mejor manera de emular INSERT IGNORE y ON DUPLICATE KEY UPDATE con PostgreSQL?

¿Fue útil?

Solución

Intenta hacer una ACTUALIZACIÓN. Si no modifica ninguna fila, eso significa que no existía, entonces haga una inserción. Obviamente, haces esto dentro de una transacción.

Por supuesto, puede envolver esto en una función si no desea poner el código adicional en el lado del cliente. También necesita un bucle para la condición de carrera muy rara en ese pensamiento.

Hay un ejemplo de esto en la documentación: http: / /www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html , ejemplo 40-2 justo en la parte inferior.

Esa suele ser la forma más fácil. Puedes hacer algo de magia con las reglas, pero es probable que sea mucho más complicado. Recomendaría el enfoque de función envolvente sobre eso cualquier día.

Esto funciona para una sola fila, o pocas filas, valores. Si está tratando con grandes cantidades de filas, por ejemplo, de una subconsulta, es mejor dividirla en dos consultas, una para INSERTAR y otra para ACTUALIZAR (como una combinación / subselección apropiada, por supuesto, no es necesario escribir su principal filtrar dos veces)

Otros consejos

Con PostgreSQL 9.5, ahora es funcionalidad nativa (como MySQL ha tenido durante varios años):

  

INSERTAR ... EN CONFLICTO NO HAGA NADA / ACTUALIZAR (" UPSERT ")

     

9.5 trae soporte para "UPSERT" operaciones   INSERT se extiende para aceptar una cláusula ON CONFLICT DO UPDATE / IGNORE. Esta cláusula especifica una acción alternativa a tomar en caso de una posible violación duplicada.

...

  

Otro ejemplo de nueva sintaxis:

INSERT INTO user_logins (username, logins)
VALUES ('Naomi',1),('James',1) 
ON CONFLICT (username)
DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins;

Editar: en caso de que se haya perdido la respuesta de warren , PG9.5 ahora tiene esto de forma nativa; hora de actualizar!


Basándose en la respuesta de Bill Karwin, para explicar cómo sería un enfoque basado en reglas (transferir desde otro esquema en el mismo DB y con una clave principal de varias columnas):

CREATE RULE "my_table_on_duplicate_ignore" AS ON INSERT TO "my_table"
  WHERE EXISTS(SELECT 1 FROM my_table 
                WHERE (pk_col_1, pk_col_2)=(NEW.pk_col_1, NEW.pk_col_2))
  DO INSTEAD NOTHING;
INSERT INTO my_table SELECT * FROM another_schema.my_table WHERE some_cond;
DROP RULE "my_table_on_duplicate_ignore" ON "my_table";

Nota: La regla se aplica a todas las operaciones INSERT hasta que se descarta la regla, por lo que no es muy ad hoc.

Para obtener la lógica insertar ignorar puede hacer algo como a continuación. Descubrí que simplemente insertar desde una instrucción select de valores literales funcionó mejor, luego puede enmascarar las claves duplicadas con una cláusula NOT EXISTS. Para obtener la actualización de la lógica duplicada, sospecho que sería necesario un bucle pl / pgsql.

INSERT INTO manager.vin_manufacturer
(SELECT * FROM( VALUES
  ('935',' Citroën Brazil','Citroën'),
  ('ABC', 'Toyota', 'Toyota'),
  ('ZOM',' OM','OM')
  ) as tmp (vin_manufacturer_id, manufacturer_desc, make_desc)
  WHERE NOT EXISTS (
    --ignore anything that has already been inserted
    SELECT 1 FROM manager.vin_manufacturer m where m.vin_manufacturer_id = tmp.vin_manufacturer_id)
)

Para aquellos de ustedes que tienen Postgres 9.5 o superior, la nueva sintaxis EN CONFLICTOS NO HACE NADA debería funcionar:

INSERT INTO target_table (field_one, field_two, field_three ) 
SELECT field_one, field_two, field_three
FROM source_table
ON CONFLICT (field_one) DO NOTHING;

Para aquellos de nosotros que tenemos una versión anterior, esta combinación correcta funcionará en su lugar:

INSERT INTO target_table (field_one, field_two, field_three )
SELECT source_table.field_one, source_table.field_two, source_table.field_three
FROM source_table 
LEFT JOIN target_table ON source_table.field_one = target_table.field_one
WHERE target_table.field_one IS NULL;
INSERT INTO mytable(col1,col2) 
    SELECT 'val1','val2' 
    WHERE NOT EXISTS (SELECT 1 FROM mytable WHERE col1='val1')

Parece que PostgreSQL admite un objeto de esquema llamado regla .

http://www.postgresql.org/docs/current/ static / rules-update.html

Podría crear una regla ON INSERT para una tabla determinada, haciendo que NADA si existe una fila con el valor de clave principal dado, o haciendo que haga un UPDATE en lugar de INSERT si existe una fila con el valor de clave principal dado.

No lo he intentado yo mismo, así que no puedo hablar por experiencia ni ofrecer un ejemplo.

Como @hanmari mencionó en su comentario. cuando se inserta en una tabla de postgres, el conflicto on (..) no hacer nada es el mejor código para no insertar datos duplicados .:

query = "INSERT INTO db_table_name(column_name)
         VALUES(%s) ON CONFLICT (column_name) DO NOTHING;"

La línea de código ON CONFLICT permitirá que la instrucción de inserción aún inserte filas de datos. El código de consulta y valores es un ejemplo de fecha insertada desde un Excel en una tabla db de postgres. Tengo restricciones agregadas a una tabla de postgres que uso para asegurarme de que el campo ID sea único. En lugar de ejecutar una eliminación en filas de datos que son iguales, agrego una línea de código sql que renumera la columna ID comenzando en 1. Ejemplo:

q = 'ALTER id_column serial RESTART WITH 1'

Si mis datos tienen un campo de ID, no lo uso como ID principal / ID en serie, creo una columna de ID y la configuro en serie. Espero que esta información sea útil para todos. * No tengo título universitario en desarrollo / codificación de software. Todo lo que sé sobre codificación lo estudio por mi cuenta.

Esta solución evita el uso de reglas:

BEGIN
   INSERT INTO tableA (unique_column,c2,c3) VALUES (1,2,3);
EXCEPTION 
   WHEN unique_violation THEN
     UPDATE tableA SET c2 = 2, c3 = 3 WHERE unique_column = 1;
END;

pero tiene un inconveniente en el rendimiento (consulte PostgreSQL.org ):

  

Un bloque que contiene una cláusula EXCEPTION es significativamente más costoso   para entrar y salir de una cuadra sin una. Por lo tanto, no use   EXCEPCIÓN sin necesidad.

En masa, siempre puede eliminar la fila antes de la inserción. La eliminación de una fila que no existe no causa un error, por lo que se omite de forma segura.

Para los scripts de importación de datos, para reemplazar " IF NOT EXISTS " ;, en cierto modo, hay una formulación un poco incómoda que, sin embargo, funciona:

DO
$do$
BEGIN
PERFORM id
FROM whatever_table;

IF NOT FOUND THEN
-- INSERT stuff
END IF;
END
$do$;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top