哥们儿最近搞项目,又碰上老问题了——数据库里那自增主键,每次都得琢磨怎么搞定。以前用别的数据库,大多有那种自带的IDENTITY属性,一设就完事儿。可咱用的是Oracle,这家伙就得有点自己的脾气和玩法。一开始也头疼,心想咋弄才能像其他数据库那样,插入数据时不用管主键,它自己就给我填上。
本站为89游戏官网游戏攻略分站,89游戏每日更新热门游戏,下载请前往主站地址:www.gm89.icu
琢磨了好几天,查了不少资料,也问了些前辈,才搞明白Oracle也有自己的套路,而且还挺灵活的,那就是用序列(Sequence)。
我怎么一步步把自增主键搞定的
这事儿还得从头说起,我当时要建一个用户表,叫USER_INFO,里面肯定得有个用户ID作为主键。想达到自增的目的,我就决定分两步走:先创建一个序列,再把这个序列和我的表主键关联起来。
第一步:创建序列
我得给我的用户ID搞个自动生成器。这个生成器就是序列。当时我是这么敲命令的:
CREATE SEQUENCE USER_INFO_SEQ
START WITH 1
INCREMENT BY 1
NOMAXVALUE
NOCACHE
NOCYCLE;
这里面每一个参数,我当时都是有考量的。
CREATE SEQUENCE USER_INFO_SEQ:这句就是说,我要创建一个序列,名字叫USER_INFO_SEQ。这个名字我一般会和表名或者字段名有点关联,方便后面自己看着不糊涂。START WITH 1:这个简单,就是让序列从1开始数。我的第一个用户ID就是1。INCREMENT BY 1:每次自增1。也就是说,1后面是2,2后面是3,很符合自增主键的需求。NOMAXVALUE:这个很重要,就是不设最大值。如果设了最大值,万一哪天数据量大了,序列跑完了就歇菜了,到时候还得手动修改,很麻烦。不设最大值,它就能一直数下去,除非数据库存不下。NOCACHE:这个也是个小技巧。CACHE的意思是序列会预先生成一批号码放在内存里,下次需要的时候直接从内存拿,速度快。但问题是,如果数据库突然挂了或者重启,这些还没用出去的预存号码就丢了,下次再启动,序列会接着上次已经用过的最大值继续生成,这就导致中间ID有“断号”的情况。对普通数据没但对自增主键,我个人是喜欢它连续的,所以就用了NOCACHE,每次都去数据库里取新的,虽然慢一点点,但能保证ID的连贯性,没有断号的顾虑。NOCYCLE:不循环。如果序列数到头了,不让它从头再来。这个和NOMAXVALUE是搭档,因为我压根就没设最大值,所以也不存在循环的问题。
第二步:把序列和表主键关联起来
序列建好了,它自己还不能给表的主键赋值。这就需要一个触发器(Trigger)来帮忙。
我先把我那用户表建起来:
CREATE TABLE USER_INFO (
USER_ID NUMBER(10) PRIMARY KEY,
USER_NAME VARCHAR2(100) NOT NULL,
EMAIL VARCHAR2(100)
注意看,USER_ID这里我没写什么AUTO_INCREMENT之类的,因为Oracle不支持那样直接写。它只是一个普通的NUMBER类型,并且是主键。
重头戏来了,就是写触发器。这个触发器会在每次有新数据插入USER_INFO表之前触发,然后把序列生成的下一个值赋给USER_ID。
CREATE OR REPLACE TRIGGER TRG_USER_INFO_BI
BEFORE INSERT ON USER_INFO
FOR EACH ROW
BEGIN
SELECT USER_INFO_* INTO :*_ID FROM DUAL;
END;
我来解释下这个触发器:
CREATE OR REPLACE TRIGGER TRG_USER_INFO_BI:我创建一个触发器,名字叫TRG_USER_INFO_BI。OR REPLACE是为了后面万一要修改触发器内容,可以直接替换掉旧的,不用先删除再新建。BEFORE INSERT ON USER_INFO:这个是触发器触发的时机。意思是在每次向USER_INFO表插入数据之前。因为我要在插入前就把主键值准备FOR EACH ROW:这个也很关键。表示每次插入一行数据,都会触发一次这个操作。而不是一次插入多行,只触发一次。BEGIN ... END;:这里面就是触发器要干的活儿。SELECT USER_INFO_* INTO :*_ID FROM DUAL;:这句是核心。USER_INFO_*就是获取USER_INFO_SEQ这个序列的下一个值。:*_ID代表的是即将插入的新行(:NEW)的USER_ID字段。FROM DUAL是Oracle里的一个虚拟表,通常用来执行一些不需要从实际表获取数据的查询,比如获取系统时间或者序列值。这句命令的含义就是把序列的下一个值塞给新行的USER_ID。
搞定后的实用小技巧
这样一套搞下来,我的USER_INFO表就有了自增主键了。我往表里插入数据的时候,根本不用管USER_ID,它自己就给我填好了。
INSERT INTO USER_INFO (USER_NAME, EMAIL) VALUES ('张三', 'zhangsan@*');
INSERT INTO USER_INFO (USER_NAME, EMAIL) VALUES ('李四', 'lisi@*');
当我查询表的时候,就能看到USER_ID分别是1、2,完美。
这里面还有几个我当时也学到的小技巧,顺便分享出来。
NEXTVAL和CURRVAL的区别:NEXTVAL是获取序列的下一个值,每次调用都会让序列前进一位。而CURRVAL是获取序列的当前值,前提是你必须先调用过一次NEXTVAL。也就是说,在一个会话里,NEXTVAL至少得先跑一次,你才能用CURRVAL。我一般是直接用NEXTVAL,因为是给新数据赋值,肯定需要新值。- 如果需要查看当前序列的下一个值:可以
SELECT USER_INFO_* FROM DUAL;,但是要注意,这会消耗掉一个序列值!所以通常不建议没事儿就去查。 - 如果需要重置序列:这个情况比较少,但万一测试环境需要,或者数据清理了想从头开始,那就得先
DROP SEQUENCE USER_INFO_SEQ;,然后重新CREATE SEQUENCE。这个操作得小心,因为一旦删了,之前缓存或者用过的序列值就可能乱套,生产环境要慎重。
这套组合拳下来,Oracle的自增主键问题就算彻底解决了。虽然比别的数据库多走了两步路,但胜在灵活,而且搞懂了背后的逻辑,用起来也踏实多了。我把这个实践记录下来,希望也能帮到有同样困惑的朋友们。