辖区关联年度重置的5位自增物品ID生成方案咨询
实现按辖区+年份重置的自增序列方案
这场景我之前帮朋友做类似的政务物品登记系统时碰到过,你的思路方向完全正确——单独建表维护序列是最稳妥、通用的方案,下面给你具体的实现细节和注意事项:
一、核心表结构设计
创建一张专门维护序列的表,比如命名为area_year_sequence,用来记录每个辖区每年的当前自增数值:
CREATE TABLE area_year_sequence ( area_id CHAR(2) NOT NULL, -- 2位辖区ID year INT NOT NULL, -- 完整年份(比如2018,避免跨世纪后两位重复的问题) current_seq INT NOT NULL DEFAULT 0, -- 当前自增数,初始为0 PRIMARY KEY (area_id, year) -- 复合主键,保证每个辖区每年只有一条记录 );
用area_id+year作为复合主键,能确保不会出现同一个辖区同年的重复序列记录。
二、原子化生成自增序列
最关键的是要保证并发场景下不会生成重复的序列值,绝对不能用「先查询当前值→加1→更新」的分步操作(会有并发冲突),必须用数据库的原子操作来实现:
MySQL 示例
-- 插入或更新序列,原子操作 INSERT INTO area_year_sequence (area_id, year, current_seq) VALUES ('20', YEAR(CURDATE()), 1) ON DUPLICATE KEY UPDATE current_seq = current_seq + 1; -- 获取更新后的序列值并补零为5位 SELECT LPAD(current_seq, 5, '0') FROM area_year_sequence WHERE area_id = '20' AND year = YEAR(CURDATE());
或者更高效的方式,利用612585直接返回结果:
INSERT INTO area_year_sequence (area_id, year, current_seq) VALUES ('20', YEAR(CURDATE()), 1) ON DUPLICATE KEY UPDATE current_seq = LAST_INSERT_ID(current_seq + 1); SELECT LPAD(612585, 5, '0');
PostgreSQL 示例
PostgreSQL支持RETURNING子句,能直接返回更新后的值:
INSERT INTO area_year_sequence (area_id, year, current_seq) VALUES ('20', EXTRACT(YEAR FROM CURRENT_DATE)::INT, 1) ON CONFLICT (area_id, year) DO UPDATE SET current_seq = area_year_sequence.current_seq + 1 RETURNING LPAD(current_seq::TEXT, 5, '0');
三、拼接最终ID
拿到补零后的5位序列(比如00001)后,按规则拼接即可:
辖区ID + "-" + 补零序列 + "-" + 年份后两位
比如从表中拿到area_id='20',year=2018,current_seq=1,最终ID就是20-00001-18。
四、额外注意事项
- 年份处理:建议存完整年份(如2018)而非后两位,避免2000年和1900年的后两位都是
00导致的序列混淆,生成ID时再取年份后两位即可。 - 初始化:无需提前手动插入所有辖区的序列记录,程序第一次生成时会自动插入新的
area_id+year组合记录。 - 并发安全:上述的
INSERT ... ON DUPLICATE KEY或ON CONFLICT操作都是数据库层面的原子操作,能保证多请求同时生成ID时不会重复。 - 序列重置:每年年初不需要手动重置序列,因为新的年份会自动生成新的
area_id+year记录,序列从1开始重新计数。
如果你的项目用的是其他数据库(比如SQL Server、Oracle),可以告诉我,我再给你适配对应的实现语句~
内容的提问来源于stack exchange,提问作者dqmis
相关产品推荐
相关产品推荐

