PosgreSQL如果不存在则插入,存在则更新(UPSET 特性)

一、业务场景

某客户上线一段时间后,日志中出现大量主键冲突的日志,并且出现数据库繁忙。
ERROR: duplicate key value violates unique constraint “visit_workrecord_pkey”
Detail: Key (recordid)=(1105661718883) already exists.
问题: 此类日志会大量出现。


二、UPSET 场景演示

如果出现这种约束冲突后,该如何处理,在PostgreSQL中UPSET特性是指INSERT ... ON CONFLICT UPDATE,专门用来处理这种数据冲突的情况。

素材准备

--  自定义一张用户登录日志表
CREATE TABLE  user_logins (
	user_name VARCHAR(200) PRIMARY KEY,
	login_cnt INT4,
	last_login_time TIMESTAMP(0) WITHOUT TIME ZONE 
);

插入重复数据后报错

INSERT INTO user_logins	 (user_name,login_cnt) VALUES 
('frances',1),('mate',2),('frances',1);

PosgreSQL如果不存在则插入,存在则更新(UPSET 特性)_第1张图片


2.1 冲突时更新

-- 先插入一条
INSERT INTO user_logins	 (user_name,login_cnt) VALUES 
('frances',1);

-- 重复插入
INSERT INTO user_logins	 (user_name,login_cnt) VALUES 
('mate',2),('frances',1)
ON conflict(user_name)
DO UPDATE SET
login_cnt = user_logins.login_cnt + EXCLUDED.login_cnt,last_login_time = now();
  • ON CONFLICT() 定义冲突
  • DO UPDATE SET 冲突动作
  • 内置表 EXCLUDED

批量操作,将三条数据批量操作时,会报错
PosgreSQL如果不存在则插入,存在则更新(UPSET 特性)_第2张图片

在SQL标准中,sql merge也有同样的问题,因为一次请求中对行的处理,顺序是不固定的。数据库不知道应该以哪条为最后需要保留的

标签 PostgreSQL , insert into on conflict , merge insert 背景 使用insert into on conflict 合并插入,如果一条SQL语句中,对一个KEY(冲突键,或冲突约束)多次发生冲突时,会报错。


2.2 冲突时不做任何操作

在发生冲突的时候不做任何处理。

select * from user_logins

-- TRUNCATE TABLE user_logins;
-- INSERT INTO user_logins	 (user_name,login_cnt) VALUES  ('frances',1);
INSERT INTO user_logins	 (user_name,login_cnt) VALUES 
('mate',2),('frances',1)
ON conflict(user_name)
DO NOTHING ;

三、如果不存在则插入,存在则更新

需要有一个唯一键

3.1 MySQL 版本

MySQL语法支持如果数据存在则更新,不存在则插入,首先判断数据存在还是不存在的那个字段要设置成unique索引。

SELECT * FROM user_logins;
CREATE TABLE  user_logins (
	user_name VARCHAR(200) PRIMARY KEY,
	login_cnt INT4,
	last_login_time  date
);

-- 执行下面语句两次查看结果
INSERT INTO user_logins	(user_name,login_cnt,last_login_time) VALUES ('frances',3,now()) 
ON DUPLICATE KEY UPDATE login_cnt= 4,last_login_time= '2019-04-13';

3.2 PostgreSQL版本

SELECT * FROM user_logins;
CREATE TABLE  user_logins (
	user_name VARCHAR(200) PRIMARY KEY,
	login_cnt INT4,
	last_login_time  date
);
-- 执行两次看结果
INSERT INTO user_logins	(user_name,login_cnt,last_login_time) VALUES ('frances',3,now()) 
ON conflict(user_name)
DO UPDATE SET
login_cnt = 4,last_login_time = now();

参考

《PostgreSQL实战》

你可能感兴趣的:(PostgreSQL)