的(最可能)在PostgreSQL 9.5及更高版本中處理此問題的最佳方法是使用INSERT ... ON CONFLICT ... DO UPDATE
。
假設這是你的原始表(很簡單,這個例子的目的):
CREATE TABLE tbl
(
tbl_id INTEGER,
payload JSONB,
CONSTRAINT tbl_pk
PRIMARY KEY (tbl_id)
) ;
我們與起始數據填充:
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}') ;
現在我們執行非衝突插入(即:ON CONFLICT ... DO將不會執行):
-- 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
現在我們執行o NE INSERT
這將產生一個PRIMARY KEY
衝突,這將是由ON CONFLICT
條款進行處理,並會進行更新
-- 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 ;
而現在,兩行插入,將在一行上發生衝突,並插入其他:
-- 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 ;
我們現在檢查表:
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"}
您的代碼應循環處理您的傳入數據,並獲取它,並使用多行VALUES
一次一行或分批執行所有INSERT/UPDATE
(有時稱爲MERGE
或UPSERT
)。
你可以在所有的代碼dbfiddle here
還有一個選擇,如果你在工作分批這是更適合。使用WITH
聲明,有一個UPDATE
從句,後面是一個INSERT
:
-- 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 ;
你會得到相同的結果,因爲你可以在看到dbfiddle here。
在這兩種情況下,準備好錯誤處理,並準備重試您交易如果他們發生衝突因併發行動也修改你的數據庫。你交易可以明確的(如在第二種情況下),或隱含,如果你有某種自動提交的每一個INSERT
什麼是您的實際* *的問題? – joanolo
我只是徘徊,如果這是一種好或壞的方式 –
您使用的是哪個版本的PostgreSQL?如果使用更新的(9.5+)版本,您可能希望使用'INSERT ... ON CONFLICT DO UPDATE ...;'並且只需一步即可完成所有操作。請參閱['INSERT'](https://www.postgresql.org/docs/current/static/sql-insert.html) – joanolo