Select

MERGE/UPSERT 的 SELECT 或 set-returning 版本?

  • February 18, 2018

我正在學習MERGEUPSERTUPDATE+ INSERT),根據維基百科,它具有以下形式:

MERGE INTO tablename USING table_reference ON (condition)
  WHEN MATCHED THEN
  UPDATE SET column1 = value1 [, column2 = value2 ...]
  WHEN NOT MATCHED THEN
  INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...]);

, 和:

如果源中的 ON 欄位與目標中的 ON 欄位匹配,則更新

如果源中的 ON 欄位與目標中的 ON 欄位不匹配,則 INSERT

我想知道是否有一個SELECT版本(沒有副作用)返回更新後的目標表的可能結果而不實際更改目標表?

以 PostgreSQL 9.5 的INSERT INTO ... ON CONFLICT [ conflict_target ] conflict_action聲明(在維基百科中指出)為例。給定一個目標表distributors和一個更新表distributors_upd如下:

DROP TABLE IF EXISTS distributors;
DROP TABLE IF EXISTS distributors_upd;

CREATE TABLE distributors (did INTEGER PRIMARY KEY, dname VARCHAR);
INSERT INTO distributors
VALUES (5, 'Gizmo Transglobal'), (6, 'Associated Computing, Inc');

CREATE TABLE distributors_upd (did INTEGER PRIMARY KEY, dname VARCHAR);
INSERT INTO distributors_upd
VALUES (5, 'Gizmo Transglobal*');

是否有一個有效的查詢來獲得結果UPSERT而不實際更改目標表distributors

我試圖模仿這種效果:

WITH distributors_updated AS (
INSERT INTO distributors (did, dname)
   (TABLE distributors_upd)
   ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname
   RETURNING *
)
--SELECT * FROM distributors;
SELECT * FROM distributors_updated;

最後兩行都沒有給出預期的結果:

did |           dname           
-----+---------------------------
  6 | Associated Computing, Inc
  5 | Gizmo Transglobal*

第一個... SELECT * FROM distributors顯示原始表格(按設計)。

-----+---------------------------
  5 | Gizmo Transglobal
  6 | Associated Computing, Inc
(2 rows)

第二個... SELECT * FROM distributors_updated僅顯示更改行。

-----+--------------------
  5 | Gizmo Transglobal*
(1 row)

的內容以distributors任何一種方式更改。是否有一個有效的查詢返回“合併”/“聯合”而不實際更改目標distributors

如果您想在不實際執行的情況下查找“UPSERT”的結果,可以使用以下命令執行查詢FULL JOIN

SELECT 
   did,
   COALESCE(s.dname, t.dname) AS dname   -- in case of conflict, 
                                         -- give precedence to source table
FROM 
   distributors AS t
   FULL JOIN distributors_upd AS s
   USING (did) ;

在**dbfiddle.uk**進行測試(感謝 McNets進行設置)

將通過合併更新衝突的列將在COALESCE()函式中首先具有源列,然後是目標列。如果更新更複雜,則必須使用更複雜的COALESCE()orCASE表達式。例子:

ON CONFLICT UPDATE 
   SET counter = counter + EXCLUDED.counter

會變成

SELECT
   COALESCE(t.counter + s.counter, t.counter, s.counter) AS counter

引用自:https://dba.stackexchange.com/questions/198199