【发布时间】:2017-08-23 13:26:17
【问题描述】:
假设我的 Postgres 数据库中有两个表:
create table transactions
(
id bigint primary key,
doc_id bigint not null,
-- lots of other columns...
amount numeric not null
);
-- same columns
create temporary table updated_transactions
(
id bigint primary key,
doc_id bigint not null,
-- lots of other columns...
amount numeric not null
);
两个表都只有一个主键,没有唯一索引。
我需要使用以下规则将行从updated_transactions 插入transactions:
-
transactions和updated_transactions中的 id 列值不匹配 -
doc_id等其他列(amount除外)应匹配 - 找到匹配行时,更新
amount和id列 - 当没有找到匹配的行时,插入它
updated_transactions 中的id 值取自序列。
一个业务对象只是填充updated_transactions 然后合并
使用 upsert 查询将新的或更新的行从它添加到 transactions。
所以我的旧交易保持不变ids 和更新的
被分配了新的ids。
在 MSSQL 和 Oracle 中,这将是一个类似于此的 merge 语句:
merge into transactions t
using updated_transactions ut on t.doc_id = ut.doc_id, ...
when matched then
update set t.id = ut.id, t.amount = ut.amount
when not matched then
insert (t.id, t.doc_id, ..., t.amount)
values (ut.id, ut.doc_id, ..., ut.amount);
在 PostgreSQL 中,我想应该是这样的:
insert into transactions(id, doc_id, ..., amount)
select coalesce(t.id, ut.id), ut.doc_id, ... ut.amount
from updated_transactions ut
left join transactions t on t.doc_id = ut.doc_id, ....
on conflict
on constraint transactions_pkey
do update
set amount = excluded.amount, id = excluded.id
问题在于do update 子句:excluded.id 是旧值
来自transactions 表,而我需要来自updated_transactions 的新值。
ut.id 值对于 do update 子句是不可访问的,我唯一能做的
使用的是excluded 行。但是excluded 行只有coalesce(t.id, ut.id)
返回现有行的旧 id 值的表达式。
是否可以使用 upsert 查询同时更新 id 和 amount 列?
【问题讨论】:
-
other columns like doc_id, etc (except of the amount) should match听起来像是我的候选键。 -
doc_id 和其他列的值,除了数量,不是唯一的。我简化了问题中的设置,使我的示例查询更易于理解。在我的真实案例中,我必须添加一个
row_number() over (partition by doc_id, ... order by id)来匹配行。 -
在这种情况下,如果不回退到
id,您将无法执行更新。 -
我仍然希望有办法做到这一点。实际上,我看到了一个丑陋的解决方法,将一个未使用的
new_id列添加到transactions并使用excluded.new_id从updated_transactions获得id。但我相信这是可以避免的。 -
when a matching row is found, update both amount and id columns如果多个记录匹配怎么办? (你没有唯一匹配,因为你没有其他唯一键 thaid)
标签: sql postgresql merge upsert