近期有一个需求,向一张数据库表插入数据,如果是新数据则执行插入动作,如果插入的字段和已有字段重复,则更新该行对应的部分字段

1. 创建测试表

  1. create table meta_data (
  2. id serial,
  3. user_id varchar(128) DEFAULT NULL,
  4. file_name varchar(1024) DEFAULT NULL,
  5. file_path varchar(1024) DEFAULT NULL,
  6. update_time TIMESTAMP DEFAULT NULL,
  7. UNIQUE (user_id,file_name)
  8. );
  9.  
  10. postgres=# \d meta_data
  11. Table "public.meta_data"
  12. Column | Type | Modifiers
  13. -------------+-----------------------------+--------------------------------------------------------
  14. id | integer | not null default nextval('meta_data_id_seq'::regclass)
  15. user_id | character varying(128) | default NULL::character varying
  16. file_name | character varying(1024) | default NULL::character varying
  17. file_path | character varying(1024) | default NULL::character varying
  18. update_time | timestamp without time zone |
  19. Indexes:
  20. "meta_data_user_id_file_name_key" UNIQUE CONSTRAINT, btree (user_id, file_name)

2. 插入两条测试数据

  1. INSERT INTO meta_data (
  2. user_id,
  3. file_name,
  4. file_path,
  5. UPDATE_TIME )
  6. VALUES ( 'user_id01',
  7. 'file_name01',
  8. '/usr/local/file_name01',
  9. now())
  10. ON CONFLICT (user_id, file_name) DO UPDATE SET file_path = EXCLUDED.file_path, UPDATE_TIME = EXCLUDED.UPDATE_TIME;
  11.  
  12. INSERT INTO meta_data (
  13. user_id,
  14. file_name,
  15. file_path,
  16. UPDATE_TIME )
  17. VALUES ( 'user_id02',
  18. 'file_name02',
  19. '/usr/local/file_name02',
  20. now())
  21. ON CONFLICT (user_id, file_name) DO UPDATE SET file_path = EXCLUDED.file_path, UPDATE_TIME = EXCLUDED.UPDATE_TIME;
  1. postgres=# select * from meta_data;
  2. id | user_id | file_name | file_path | update_time
  3. ----+-----------+-------------+------------------------+----------------------------
  4. 1 | user_id01 | file_name01 | /usr/local/file_name01 | 2019-09-23 17:14:52.39878
  5. 2 | user_id02 | file_name02 | /usr/local/file_name02 | 2019-09-23 17:14:53.118192
  6. (2 rows)

3. 插入第三条测试数据,注意插入的字段user_id和file_name和第二条语句对应的字段是重复的

  1. INSERT INTO meta_data (
  2. user_id,
  3. file_name,
  4. file_path,
  5. UPDATE_TIME )
  6. VALUES ( 'user_id02',
  7. 'file_name02',
  8. '/usr/local/file_name03',
  9. now())
  10. ON CONFLICT (user_id, file_name) DO UPDATE SET file_path = EXCLUDED.file_path, UPDATE_TIME = EXCLUDED.UPDATE_TIME;
  1. postgres=# select * from meta_data;
  2. id | user_id | file_name | file_path | update_time
  3. ----+-----------+-------------+------------------------+----------------------------
  4. 1 | user_id01 | file_name01 | /usr/local/file_name01 | 2019-09-23 17:14:52.39878
  5. 2 | user_id02 | file_name02 | /usr/local/file_name03 | 2019-09-23 17:16:52.457696
  6. (2 rows)

可以看到新插入的第三条语句其实是更新了已存在的第二条记录

4.如何区分该条语句到底是执行了insert和update操作。

通过xmax字段的值是否为0,可以判断,如果是UPDATE,XMAX里面会填充更新事务号。注意直接用UPDATE语句更新的话,XMAX会写入0,因为是新版本,而老版本上XMAX会填入更新事务号。

我们重建表结构重新插入前面两条数据测试。

  1. postgres=# select ctid,xmin,xmax,* from meta_data;
  2. ctid | xmin | xmax | id | user_id | file_name | file_path | update_time
  3. -------+------+------+----+-----------+-------------+------------------------+----------------------------
  4. (0,1) | 3241 | 0 | 1 | user_id01 | file_name01 | /usr/local/file_name01 | 2019-09-23 17:31:27.360539
  5. (0,2) | 3242 | 0 | 2 | user_id02 | file_name02 | /usr/local/file_name02 | 2019-09-23 17:31:28.10752
  6. (2 rows)

再次插入第三条重复数据

  1. INSERT INTO meta_data (
  2. user_id,
  3. file_name,
  4. file_path,
  5. UPDATE_TIME )
  6. VALUES ( 'user_id02',
  7. 'file_name02',
  8. '/usr/local/file_name03',
  9. now())
  10. ON CONFLICT (user_id, file_name) DO UPDATE SET file_path = EXCLUDED.file_path, UPDATE_TIME = EXCLUDED.UPDATE_TIME;
  11.  
  12. postgres=# select ctid,xmin,xmax,* from meta_data;
  13. ctid | xmin | xmax | id | user_id | file_name | file_path | update_time
  14. -------+------+------+----+-----------+-------------+------------------------+----------------------------
  15. (0,1) | 3241 | 0 | 1 | user_id01 | file_name01 | /usr/local/file_name01 | 2019-09-23 17:31:27.360539
  16. (0,3) | 3243 | 3243 | 2 | user_id02 | file_name02 | /usr/local/file_name03 | 2019-09-23 17:33:53.459403
  17. (2 rows)

ctid表示行号, xmin表示INSERT该记录的事务号,xmax表示删除该记录(update实际上是删除老版本新增新版本,所以老版本上xmax有值)的事务号。

手动执行update

  1. postgres=# update meta_data set file_path='/usr/local/file_name02' where user_id='user_id02';
  2. UPDATE 1
  3. postgres=# select ctid,xmin,xmax,* from meta_data;
  4. ctid | xmin | xmax | id | user_id | file_name | file_path | update_time
  5. -------+------+------+----+-----------+-------------+------------------------+----------------------------
  6. (0,1) | 3241 | 0 | 1 | user_id01 | file_name01 | /usr/local/file_name01 | 2019-09-23 17:31:27.360539
  7. (0,4) | 3244 | 0 | 2 | user_id02 | file_name02 | /usr/local/file_name02 | 2019-09-23 17:33:53.459403
  8. (2 rows)

结论

1、insert into on conflict do update,返回xmax等于0表示insert,不等于0表示update,

2、直接update,并提交,提交的记录上xmax为0。

PostgreSQL INSERT ON CONFLICT不存在则插入,存在则更新的更多相关文章

  1. PostgreSQL 务实应用(二/5)插入冲突

    在项目中,有时会动态地按周期(如按月)封存统计数据,通常需要做这样的处理: 以按月封存为例,当月数据到达时,先需要检查该月是否有过记录,有则以更新的方式累加统计数字,无则添加一条记录. 假设我们创建以 ...

  2. oracle语句insert into select如何加后续插入条件

    oracle语句insert into select如何加后续插入条件 2014-01-21 10:48匿名  分类:其他编程语言 | 浏览 2746 次 oracle中有批量插入语句insert i ...

  3. SQL语句的使用,SELECT - 从数据库表中获取数据 UPDATE - 更新数据库表中的数据 DELETE - 从数据库表中删除数据 INSERT INTO - 向数据库表中插入数据

    SQL DML 和 DDL 可以把 SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL). SQL (结构化查询语言)是用于执行查询的语法. 但是 SQL 语言也包含用于更新. ...

  4. spring data jpa开启批量插入、批量更新

    spring data jpa开启批量插入.批量更新 原文链接:https://www.cnblogs.com/blog5277/p/10661096.html 原文作者:博客园--曲高终和寡 *** ...

  5. MySQL 避免重复数据的批量插入与批量更新

    [转发] 导读 我们在向数据库里批量插入数据的时候,会遇到要将原有主键或者unique索引所在记录更新的情况,而如果没有主键或者unique索引冲突的时候,直接执行插入操作. 这种情况下,有三种方式执 ...

  6. MySql快速插入以及批量更新

    MySql快速插入以及批量更新 插入: MySql提供了可以一次插入多条数据的用法: [sql] INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6), ...

  7. 在论坛中出现的比较难的sql问题:9(触发器专题 插入数据自动更新表数据)

    原文:在论坛中出现的比较难的sql问题:9(触发器专题 插入数据自动更新表数据) 最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了. 所 ...

  8. mybatis+mysql批量插入和批量更新、存在及更新

    mybatis+mysql批量插入和批量更新 一.批量插入 批量插入数据使用的sql语句是: insert into table (字段一,字段二,字段三) values(xx,xx,xx),(oo, ...

  9. oracle,uuid为主键,插入时直接更新id

    uuid为主键,插入时自动更新 -- Create table create table TECHNOLOGYCOMPANY ( ID VARCHAR2(32) default SYS_GUID() ...

随机推荐

  1. [Linux系统] (1)常用操作(CentOS 7.x)

    一.Linux系统配置 1.修改主机名 [/etc/hostname] vi /etc/hostname 在其中将旧名字修改为新主机名,保存,重启生效. 2.本地DNS映射 [/etc/hosts] ...

  2. BZOJ 2870: 最长道路tree 树的直径+并查集

    挺好的一道题. 把所有点都离线下来,一个个往里加入就行了. #include <cstdio> #include <algorithm> #define N 100003 #d ...

  3. 使用word2vec对中文维基百科数据进行处理

    一.下载中文维基百科数据https://dumps.wikimedia.org/zhwiki/并使用gensim中的wikicorpus解析提取xml中的内容 二.利用opencc繁体转简体 三.利用 ...

  4. STS插件_ springsource-tool-suite插件各个历史版本

    目前spring官网(http://spring.io/tools/sts/all)上可下载的spring插件只有:springsource-tool-suite-3.8.4(sts-3.8.4).但 ...

  5. oracle 获取时间

    1.获取当前时间的前24小时的各小时时间段 select to_char(to_date(to_char(sysdate ) ,'yyyy-mm-dd hh24') || ':00:00','yyyy ...

  6. C++入门经典-例5.5-空类型指针的使用

    1:代码如下: // 5.5.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> using ...

  7. Eclipse的JSON文件报错解决

    有时候我们后端老哥不想写HTML,偷懒去下载个前端页面,结果里面的JSON文件老报错 虽然可以正常运行,但红X看起来就是不爽,怎么解决呢 这是因为Eclipse认为JSON文件不需要注释,所以报的编译 ...

  8. TCP定时器 之 重传定时器

    注:这部分还没有完全分析透彻,先在此记录,后面回顾的时候再进行补充: 启动定时器: (1) 之前发送的数据段已经得到确认,新发出一个数据段之后设定: (2) 新建连接发送syn之后设定: (3) PM ...

  9. legend3---4、lavarel中session使用注意

    legend3---4.lavarel中session使用注意 一.总结 一句话总结: session('key',$value)不是存值,是设置默认值,session(['key'=>$val ...

  10. shell编程常用命令

    Linux中常用的命令 #nl  filename   使用nl命令打印文件内容并显示行号 #sed   '/nw/,$d'   filename     使用sed命令删除匹配nw至最后一行的内容 ...