PostgreSQL中的Upsert:获取不受影响的更新和恢复的数量

bcs8qyzn  于 2023-11-18  发布在  PostgreSQL
关注(0)|答案(2)|浏览(138)

使用postgres执行UPSERT语句时,例如

INSERT INTO tab1.summary(
table_name, target_field, check_n)
VALUES ('tab1', 'col1', 10),
('tab2', 'col2', 10)
ON CONFLICT(table_name, target_field, check_n)
DO UPDATE SET check_n = 20;

字符串
,作为数据输出不返回任何内容,但是,该消息显示“0”2,这意味着两行受到影响,没有任何错误。
但是,有没有办法将插入和更新的行数分别作为数据输出来检索呢?

lokaqttq

lokaqttq1#

您可以观察到系统列xmax
xmax删除事务的标识(事务ID),对于未删除的行版本为零。在可见行版本中,此列可能为非零。这通常表示删除事务尚未提交,或者尝试的删除已回滚。
你已经在所有的表中拥有了它,它已经被设置好了,你已经可以使用它了。通常,除非显式引用,它只是一个额外的隐藏字段,但是每个表的每一行都有它。
由upsert产生的结果将显示其xmax字段为0(插入)和非零(更新)。这是因为在幕后,update实际上是insertdelete的组合:它插入一个新元组并删除旧元组,将其标记为过时,并准备在没有其他事务使用更改前的表快照时由vacuum删除:demo

with cte as (
  INSERT INTO tab1.summary(table_name, target_field, check_n)
  VALUES ('tab1', 'col1', 10),
         ('tab2', 'col2', 20)
  ON CONFLICT(table_name, target_field, check_n)
  DO UPDATE SET check_n = 20
  RETURNING xmax<>0 as was_updated )
select count(*)filter(where not was_updated) as inserted_count,
       count(*)filter(where     was_updated) as updated_count from cte;

字符串
您可以将其与使用手动添加的reflector columndemo2的方法进行比较

select * from tab1.summary;


| 表名|目标场|检查_n|已更新|
| --|--|--|--|
| tab1| col1| 10 |F|
| tab2| col2| 10 |F|

INSERT INTO tab1.summary(table_name, target_field, check_n)
VALUES ('tab1', 'col1', 10),
       ('tab2', 'col2', 20)
ON CONFLICT(table_name, target_field, check_n)
DO UPDATE SET check_n = 20, was_updated=true
RETURNING was_updated,xmax;


| 已更新|XMAX|
| --|--|
| 不| 732 |
| F| 0 |

select * from tab1.summary;


| 表名|目标场|检查_n|已更新|
| --|--|--|--|
| tab2| col2| 10 |F|
| tab1| col1| 20 |不|
| tab2| col2| 20 |F|
与使用粘贴的was_updated列相比,xmax根本不必添加,因为它已经存在。

envsm3lx

envsm3lx2#

可以使用returning子句:demo

with cte as (
  INSERT INTO tab1.summary(table_name, target_field, check_n)
  VALUES ('tab1', 'col1', 10),
         ('tab2', 'col2', 10)
  ON CONFLICT(table_name, target_field, check_n)
  DO UPDATE SET check_n = 20
  RETURNING check_n=20 as was_updated)
select count(*)filter(where not was_updated) as inserted_count,
       count(*)filter(where     was_updated) as updated_count from cte;

字符串
| 插入计数|更新计数|
| --|--|
| 2 | 0 |
如果你再运行一次,强制冲突:
| 插入计数|更新计数|
| --|--|
| 0 | 2 |
当然,这个方法只在你的批处理不包含默认的check_n=20时才有效,在这种情况下,它会假设它是更新的,而不是插入的。

alter table tab1.summary add column was_updated boolean default false;

with cte as (
  INSERT INTO tab1.summary(table_name, target_field, check_n)
  VALUES ('tab1', 'col1', 10),
         ('tab2', 'col2', 10)
  ON CONFLICT(table_name, target_field, check_n)
  DO UPDATE SET check_n = 20, was_updated=true
  RETURNING was_updated)
select count(*)filter(where not was_updated) as inserted_count,
       count(*)filter(where     was_updated) as updated_count from cte;

相关问题