如何在一个语句中使用单个值 UPSERT 多行?
How to UPSERT multiple rows with individual values in one statement?
我创建了以下 table:
CREATE TABLE t1(
a INT UNIQUE,
b varchar(100) NOT NULL,
c INT,
d INT DEFAULT 0,
PRIMARY KEY (a,b));
在一行中,这个 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
是否可以一次插入多行?对于每次更新,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
params[i].4
每行都有不同的值,ON CONFLICT
语句只出现一次(不是每行),SET
不支持 WHERE
。
例如,如果我的 table 有以下行:
a | b | c | d
---+---+---+---
1 | 1 | 1 | 1
2 | 2 | 2 | 2
我的新输入是 [(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
UPSERT (INSERT ... ON CONFLICT ... DO UPDATE
) 自动跟踪名为 EXCLUDED
的特殊 table 中排除的行。 The manual:
Note that the special excluded
table is used to reference values originally proposed for insertion
所以真的很简单:
INSERT INTO t1 (a, b, c, d)
VALUES (...)
ON CONFLICT (a,b) DO UPDATE
SET d = EXCLUDED.d; -- that's all !!!
除了更简单和更快之外,与您提出的解决方案在极端情况下存在细微差别。 The manual:
Note that the effects of all per-row BEFORE INSERT
triggers are
reflected in excluded
values, since those effects may have contributed
to the row being excluded from insertion.
此外,第 DEFAULT
列的值已应用到 EXCLUDED
行,其中未提供任何输入。 (为您的专栏 d
点赞 DEFAULT 0
。)
两者通常都是您想要的。
为将来处理相同问题的用户编写解决方案
第 1 步:创建临时 table(t1 的克隆)
CREATE TABLE t2(
a INT UNIQUE,
b varchar(100) NOT NULL,
c INT,
d INT DEFAULT 0,
PRIMARY KEY (a,b));
或
create table t2 as (select * from t1) with no data;
第 2 步:将新输入插入 t2
INSERT INTO t2 (a, b, c, d)
values (1,'1',1,1),(2,'2',2,2),(3,'3',3,3)`
第 3 步:在发生冲突的情况下从 t2
更新到 t1 select d
INSERT INTO t1 (a, b, c, d)
VALUES (1,'1',1,1),(2,'2',2,2),(3,'3',3,3)
ON CONFLICT(a,b) DO UPDATE
SET d=(SELECT d FROM t2 WHERE t2.a=t1.a);
第 4 步:删除 t2
DROP TABLE t2;
我创建了以下 table:
CREATE TABLE t1(
a INT UNIQUE,
b varchar(100) NOT NULL,
c INT,
d INT DEFAULT 0,
PRIMARY KEY (a,b));
在一行中,这个 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
是否可以一次插入多行?对于每次更新,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
params[i].4
每行都有不同的值,ON CONFLICT
语句只出现一次(不是每行),SET
不支持 WHERE
。
例如,如果我的 table 有以下行:
a | b | c | d
---+---+---+---
1 | 1 | 1 | 1
2 | 2 | 2 | 2
我的新输入是 [(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
UPSERT (INSERT ... ON CONFLICT ... DO UPDATE
) 自动跟踪名为 EXCLUDED
的特殊 table 中排除的行。 The manual:
Note that the special
excluded
table is used to reference values originally proposed for insertion
所以真的很简单:
INSERT INTO t1 (a, b, c, d)
VALUES (...)
ON CONFLICT (a,b) DO UPDATE
SET d = EXCLUDED.d; -- that's all !!!
除了更简单和更快之外,与您提出的解决方案在极端情况下存在细微差别。 The manual:
Note that the effects of all per-row
BEFORE INSERT
triggers are reflected inexcluded
values, since those effects may have contributed to the row being excluded from insertion.
此外,第 DEFAULT
列的值已应用到 EXCLUDED
行,其中未提供任何输入。 (为您的专栏 d
点赞 DEFAULT 0
。)
两者通常都是您想要的。
为将来处理相同问题的用户编写解决方案
第 1 步:创建临时 table(t1 的克隆)
CREATE TABLE t2(
a INT UNIQUE,
b varchar(100) NOT NULL,
c INT,
d INT DEFAULT 0,
PRIMARY KEY (a,b));
或
create table t2 as (select * from t1) with no data;
第 2 步:将新输入插入 t2
INSERT INTO t2 (a, b, c, d)
values (1,'1',1,1),(2,'2',2,2),(3,'3',3,3)`
第 3 步:在发生冲突的情况下从 t2
更新到 t1 select dINSERT INTO t1 (a, b, c, d)
VALUES (1,'1',1,1),(2,'2',2,2),(3,'3',3,3)
ON CONFLICT(a,b) DO UPDATE
SET d=(SELECT d FROM t2 WHERE t2.a=t1.a);
第 4 步:删除 t2
DROP TABLE t2;