Le (très probablement La meilleure façon de gérer cela dans PostgreSQL 9.5 et les versions ultérieures est d'utiliser INSERT ... ON CONFLICT ... DO UPDATE
.
Supposons que ceci est votre table d'origine (très simple, pour des raisons de cet exemple):
CREATE TABLE tbl
(
tbl_id INTEGER,
payload JSONB,
CONSTRAINT tbl_pk
PRIMARY KEY (tbl_id)
) ;
Nous remplissons avec les données de départ:
INSERT INTO tbl
(tbl_id, payload)
VALUES
(1, '{"a":12}'),
(2, '{"a":13, "b": 25}'),
(3, '{"a":15, "b": [12,13,14]}'),
(4, '{"a":12, "c": "something"}'),
(5, '{"a":13, "x": 1234.567}'),
(6, '{"a":12, "x": 1234.789}') ;
Maintenant, nous effectuons un non -conflicting insert (ie: la CONFLICT ... ne seront pas exécutées):
-- A normal insert, no conflict
INSERT INTO tbl
(tbl_id, payload)
VALUES
(7, '{"x": 1234.56, "y": 3456.78}')
ON CONFLICT ON CONSTRAINT tbl_pk DO
UPDATE
SET payload = excluded.payload ; -- Note: the excluded pseudo-table comprises the conflicting rows
Et maintenant que nous réalisons o ne INSERT
qui générerait un conflit PRIMARY KEY
, qui sera gérée par la clause ON CONFLICT
et effectuera une mise à jour
-- A conflicting insert
INSERT INTO tbl
(tbl_id, payload)
VALUES
(3, '{"a": 16, "b": "I don''t know"}')
ON CONFLICT ON CONSTRAINT tbl_pk DO
UPDATE
SET payload = excluded.payload ;
Et maintenant, un insert à deux rangs qui conflit sur une ligne, et insérez l'autre:
-- Now one of each
-- A conflicting insert
INSERT INTO tbl
(tbl_id, payload)
VALUES
(4, '{"a": 18, "b": "I will we updated"}'),
(9, '{"a": 17, "b": "I am nuber 9"}')
ON CONFLICT ON CONSTRAINT tbl_pk DO UPDATE
SET payload = excluded.payload ;
Nous contrôlons maintenant la table:
SELECT * FROM tbl ORDER BY tbl_id ;
tbl_id | payload
-----: | :----------------------------------
1 | {"a": 12}
2 | {"a": 13, "b": 25}
3 | {"a": 16, "b": "I don't know"}
4 | {"a": 18, "b": "I will we updated"}
5 | {"a": 13, "x": 1234.567}
6 | {"a": 12, "x": 1234.789}
7 | {"x": 1234.56, "y": 3456.78}
9 | {"a": 17, "b": "I am nuber 9"}
Votre code doit parcourir vos données entrantes, l'obtenir, et exécuter tous les INSERT/UPDATE
(parfois appelé MERGE
ou UPSERT
) une ligne à la fois, ou par lots, avec plusieurs lignes VALUES
.
Vous pouvez obtenir tout le code à dbfiddle here
Il y a aussi une alternative, ce qui est mieux adaptée si vous travaillez dans des lots. Utilisez une déclaration WITH
, qui a une clause UPDATE
, suivie d'une INSERT
un:
-- Avoiding (most) concurrency issues.
BEGIN TRANSACTION ;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ;
WITH data_to_load (tbl_id, payload) AS
(
VALUES
(3, '{"a": 16, "b": "I don''t know"}' :: jsonb),
(4, '{"a": 18, "b": "I will we updated"}'),
(7, '{"x": 1234.56, "y": 3456.78}'),
(9, '{"a": 17, "b": "I am nuber 9"}')
),
update_existing AS
(
UPDATE
tbl
SET
payload = data_to_load.payload
FROM
data_to_load
WHERE
tbl.tbl_id = data_to_load.tbl_id
)
-- Insert the non-existing
INSERT INTO
tbl
(tbl_id, payload)
SELECT
tbl_id, payload
FROM
data_to_load
WHERE
data_to_load.tbl_id NOT IN (SELECT tbl_id FROM tbl) ;
COMMIT TRANSACTION ;
Vous obtiendrez les mêmes résultats, que vous pouvez voir à dbfiddle here.
Dans les deux cas, être prêt pour le traitement des erreurs et être prêt à recommencer vos transactions si elles entrent en conflit en raison des actions simultanées modifier également votre base de données. Vos transactions peuvent être explicites (comme dans le second cas), ou implicite, si vous avez une sorte d'auto-commit chaque INSERT
Quelle est votre question * * réelle? – joanolo
Je suis juste en train de me promener si c'est une bonne ou une mauvaise façon de faire –
Quelle version de PostgreSQL utilisez-vous? Si vous utilisez les versions plus récentes (9.5+), vous voudrez probablement utiliser 'INSERT ... ON CONFLICT DO UPDATE ...;' et effectuer tout en une seule étape. Voir ['INSERT'] (https://www.postgresql.org/docs/current/static/sql-insert.html) – joanolo