Home >Database >Mysql Tutorial >How to Handle Missing Rows in PostgreSQL UPSERTs with Concurrent Transactions?

How to Handle Missing Rows in PostgreSQL UPSERTs with Concurrent Transactions?

Linda Hamilton
Linda HamiltonOriginal
2025-01-21 18:27:08704browse

How to Handle Missing Rows in PostgreSQL UPSERTs with Concurrent Transactions?

Handling missing result rows in PostgreSQL UPSERT operation

In PostgreSQL 9.5, using RETURNING and ON CONFLICT together sometimes resulted in missing rows when concurrent transactions updated specified conflicting targets.

Disadvantages of the currently accepted answer

The currently accepted answer is prone to problems when concurrent transactions update multiple rows in the target table. While it prevents missing rows, it introduces other side effects and performance penalties associated with empty updates.

Alternatives

No concurrent write load:

<code class="language-sql">WITH input_rows(usr, contact, name) AS (
   VALUES
      (text 'foo1', text 'bar1', text 'bob1')  -- 第一行中的类型转换
    , ('foo2', 'bar2', 'bob2')
    -- 更多?
   )
, ins AS (
   INSERT INTO chats (usr, contact, name) 
   SELECT * FROM input_rows
   ON CONFLICT (usr, contact) DO NOTHING
   RETURNING id  --, usr, contact              -- 返回更多列?
   )
SELECT 'i' AS source                           -- 'i' 代表'插入'
     , id  --, usr, contact                    -- 返回更多列?
FROM   ins
UNION  ALL
SELECT 's' AS source                           -- 's' 代表'选择'
     , c.id  --, usr, contact                  -- 返回更多列?
FROM   input_rows
JOIN   chats c USING (usr, contact);           -- 唯一索引的列</code>

Having concurrent write load

To resolve race conditions for multiple concurrent transactions:

<code class="language-sql">WITH input_rows(usr, contact, name) AS ( ... )  -- 如上所示
, ins AS (
   INSERT INTO chats AS c (usr, contact, name) 
   SELECT * FROM input_rows
   ON     CONFLICT (usr, contact) DO NOTHING
   RETURNING id, usr, contact                   -- 我们需要唯一的列来进行后续连接
   )
, sel AS (
   SELECT 'i'::"char" AS source                 -- 'i' 代表'插入'
        , id, usr, contact
   FROM   ins
   UNION  ALL
   SELECT 's'::"char" AS source                 -- 's' 代表'选择'
        , c.id, usr, contact
   FROM   input_rows
   JOIN   chats c USING (usr, contact)
   )
, ups AS (                                      -- 罕见的极端情况
   INSERT INTO chats AS c (usr, contact, name)  -- 另一个UPSERT,不仅仅是UPDATE
   SELECT i.*
   FROM   input_rows i
   LEFT   JOIN sel   s USING (usr, contact)     -- 唯一索引的列
   WHERE  s.usr IS NULL                         -- 缺失!
   ON     CONFLICT (usr, contact) DO UPDATE     -- 我们第一次已经礼貌地请求了……
   SET    name = c.name                         -- ……这次我们用旧值覆盖
   RETURNING 'u'::"char" AS source              -- 'u' 代表更新
           , id  --, usr, contact               -- 返回更多列?
   )
SELECT source, id FROM sel
UNION  ALL
TABLE  ups;</code>

Key points:

  • sel CTE returns inserted rows and selected rows, providing the complete output result set.
  • ups CTE handles missing rows, updating them with existing values.
  • The output result set excludes any rows updated by concurrent transactions unless they are ups processed.

The above is the detailed content of How to Handle Missing Rows in PostgreSQL UPSERTs with Concurrent Transactions?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn