有这样的场景,本系统(系统A)有一张自行维护的用户信息表,这张表的主键是id,会自增(用户在本系统上的id不会重复,id是用户在本系统内的唯一标识)。在这张用户信息表中,还有一列记录用户在企业内的唯一标识,我们把它叫做"unique_id"。

现在一个企业内的用户带着自身信息(如token)首次登录系统A,系统A会给这个企业内用户创建本系统的用户(用户信息落表)。

先定义下面这样一张用户信息表

CREATE TABLE `user_info_d` (
    `id` INT NOT NULL AUTO_INCREMENT COMMENT '(本系统内的)用户id',
    `unique_id` CHAR(8) NOT NULL COMMENT '(本企业内的)用户唯一标识',
    `name` VARCHAR(16) COMMENT '通知创建人name',
    `age` INT COMMENT '通知创建人id',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='用户信息表';

用户信息落表,就是insert语句了。
在创建用户场景,如果连续的两条请求都是同一个用户发起(如: 两次连续点击),就有可能会出现连续的两条insert语句,且都被执行成功。
INSERT INTO user_info_d (unique_id, name, age) VALUES ('001', 'Alice', 25);
INSERT INTO user_info_d (unique_id, name, age) VALUES ('001', 'Alice', 25);

在这样的场景下。一个企业用户(同一个人)在系统A内就会有两个账户出现。也就是说会存在user_info_d表内的两行,id不同,unique_id相同。

这样的Insert语句,自增的primary key (id)只能保证id不重复,不能罩的住unique_id重复但多次插入的情况。

为了解决这个问题有两种处理思路:
1.在应用层进行处理 (redis锁)
2.在表操作层面进行处理 (修改insert语句)

本文讲修改insert语句的方式

sql语句变更: 形式1

INSERT INTO `user_info_d` (`unique_id`, `name`, `age`)
SELECT * FROM (
  SELECT '001', 'Alice', 25
) AS tmp
WHERE NOT EXISTS (
  SELECT 1 FROM `user_info_d` WHERE `unique_id` = '001'
) LIMIT 1;

上面的sql,因为有重复的unique_id 001,它的执行结果是:
插入失败,不报错,变更行数为0。

对形式1,再来一条语句

INSERT INTO `user_info_d` (`unique_id`, `name`, `age`)
SELECT * FROM (
  SELECT '006', '006Test', 26
) AS tmp
WHERE NOT EXISTS (
  SELECT 1 FROM `user_info_d` WHERE `unique_id` = '006'
) LIMIT 1;

上面的sql,没有重复的unique_id 006 它的执行结果是:
插入成功,变更行数为1。

sql语句变更: 形式2
sql语句变更也可以变更为 形式2 (更推荐 形式2)

INSERT INTO `user_info_d` (`unique_id`, `name`, `age`)
SELECT '007', '007Test', 27
FROM DUAL
WHERE NOT EXISTS (
  SELECT 1 FROM `user_info_d` WHERE `unique_id` = '007'
) LIMIT 1;

上面的sql,因为有重复的unique_id 001,它的执行结果是:
插入失败,不报错,变更行数为0
形式2和形式1 两种形式的语句执行效果相同。形式2更短。

注:
1、
在MySQL中,DUAL是一个虚拟的系统表,通常用于在没有实际表的情况下执行一些特定的操作,例如在INSERT INTO ... SELECT语句中使用DUAL来生成一行数据。

  DUAL表只有一列和一行,因此可以用来执行一些简单的计算或产生一些固定值。在实际使用中,它通常用于产生一个或多个虚拟行,或者执行一些不依赖于表的查询。

  在上面的查询中,FROM DUAL用于从DUAL表中选择一个虚拟行。因为DUAL表只有一行,所以这个查询实际上只是从DUAL表中选择了一个虚拟行,然后根据WHERE子句检查是否需要执行插入操作。

2、下面这段语句是错误的(INSERT语句不支持WHERE子句)
/*
INSERT INTO user_info_d (unique_id, name, age)
VALUES ('006', '006Test', 25)
WHERE NOT EXISTS (
  SELECT 1 FROM user_info_d WHERE unique_id = '006'
) LIMIT 1;
*/

同步更新到自己的语雀:
https://www.yuque.com/dirackeeko/vedqpf/uuk1z12txgiqaqp1


DiracKeeko
125 声望2 粉丝