[英]Alternative to Postgres SERIAL field to solve incrementing values when ON CONFLICT causes update
[英]Postgres upsert without incrementing serial IDs?
考慮下表:
CREATE TABLE key_phrase(
id SERIAL PRIMARY KEY NOT NULL,
body TEXT UNIQUE
)
我想做以下事情:
body
創建記錄。 我嘗試了一些方法,最簡單的包括DO NOTHING
的基本用法:
INSERT INTO key_phrase(body) VALUES ('example') ON CONFLICT DO NOTHING RETURNING id
但是,這只會在創建新記錄時返回一個id 。
我還嘗試了以下方法:
WITH ins AS (
INSERT INTO key_phrase (body)
VALUES (:phrase)
ON CONFLICT (body) DO UPDATE
SET body = NULL
WHERE FALSE
RETURNING id
)
SELECT id FROM ins
UNION ALL
SELECT id FROM key_phrase
WHERE body = :phrase
LIMIT 1;
這將返回新創建記錄的id或現有記錄的id 。 但是,它會導致串行主節點發生碰撞,從而在創建新記錄時造成間隙。
那么如何執行滿足前面提到的 3 個要求的條件插入(upsert)呢?
我懷疑您正在尋找類似的東西:
with
data as (select :phrase as body),
ins as (
insert into key_phrase (body)
select body
from data d
where not exists (select 1 from key_phrase kp where kp.body = d.body)
returning id
)
select id from ins
union all
select kp.id
from key_phrase kp
inner join data d on d.body = kp.body
與您的原始代碼的主要區別在於,它使用not exists
來跳過已插入on conflict
。 我將參數的聲明移至 CTE 以使事情更容易理解,但不必如此,我們可以這樣做:
with
ins as (
insert into key_phrase (body)
select body
from (values(:phrase)) d(body)
where not exists (select 1 from key_phrase where body = :phrase)
returning id
)
select id from ins
union all
select kp.id from key_phrase where body = :phrase
不使用on conflict
將減少被燒毀的序列數量。 然而,應該強調的是,沒有辦法保證連續劇始終是連續的。 由於其他原因可能存在差距。 這是設計使然; 連續劇的目的是保證唯一性,而不是“順序性”。 如果您真的想要一個沒有孔的自動增量,請考慮row_number()
和一個視圖。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.