簡體   English   中英

Postgres upsert 不增加序列號?

[英]Postgres upsert without incrementing serial IDs?

考慮下表:

CREATE TABLE key_phrase(
    id SERIAL PRIMARY KEY NOT NULL,
    body TEXT UNIQUE
)

我想做以下事情:

  1. 如果給定正文尚不存在,則使用給定body創建記錄。
  2. 返回新創建記錄的id ,如果沒有創建新記錄,則返回現有記錄的id
  3. 確保序列不會因沖突而增加。

我嘗試了一些方法,最簡單的包括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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM