如何在一个语句中使用单个值对多行进行 UPSERT?

Kuk*_*ula 2 sql postgresql upsert

我创建了下表:

CREATE TABLE t1(
a   INT UNIQUE, 
b   varchar(100) NOT NULL,
c   INT, 
d   INT DEFAULT 0,  
PRIMARY KEY (a,b));
Run Code Online (Sandbox Code Playgroud)

在单行上,此 SQL 语句效果很好(SQL 是在代码中生成的):

INSERT INTO t1 (a, b, c, d)
VALUES($params.1, '${params.2}', $params.3, params.4) 
ON CONFLICT (a,b) DO 
UPDATE SET d=params.4
Run Code Online (Sandbox Code Playgroud)

是否可以一次更新插入多行?对于每次更新, 的值params.4都是不同的。

var sqlStr = 'INSERT INTO t1 (a, b, c, d) VALUES '
for(let i =0 i < params.length; i++){
   sqlStr += `(${params[i].1}, '${params[i].2}', ${params[i].3}, ${params[i].4}),`
   
}
sqlStr = sqlStr.substring(0, sqlStr .length - 2) +')';
sqlStr += 'ON CONFLICT (a,b) DO UPDATE SET **d=???**' <-- this is the problem
Run Code Online (Sandbox Code Playgroud)

params[i].4每行都有不同的值,并且该ON CONFLICT语句仅出现一次(不是每行)并且SET不支持WHERE.

例如,如果我的表有以下行:

 a | b | c | d 
---+---+---+---
 1 | 1 | 1 | 1
 2 | 2 | 2 | 2
Run Code Online (Sandbox Code Playgroud)

我的新输入是[(1,'1',1,11),(2,'2',2,22),(3,'3',3,3)].
有两个冲突 -(1,1)(2,2)。结果应该是:

 a | b | c | d 
---+---+---+---
 1 | 1 | 1 | 11
 2 | 2 | 2 | 22
 3 | 3 | 3 | 3
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 5

UPSERT ( INSERT ... ON CONFLICT ... DO UPDATE) 跟踪自动命名的特殊表中排除的行EXCLUDED手册:

请注意,特殊excluded表用于参考最初建议插入的值

所以这真的非常简单:

INSERT INTO t1 (a, b, c, d)
VALUES (...) 
ON     CONFLICT (a,b) DO UPDATE
SET    d = EXCLUDED.d;          -- that's all !!!
Run Code Online (Sandbox Code Playgroud)

除了更简单和更快之外,与您提出的解决方案还有细微的极端情况差异。手册:

请注意,所有每行BEFORE INSERT触发器的影响都反映在excluded值中,因为这些影响可能导致该行被排除在插入之外。

另外,列DEFAULT值已应用到EXCLUDED未给出输入的行中。(就像DEFAULT 0你的专栏一样d。)

两者通常都是您想要的。