求实现循环A-Z拼接时间戳小数位的唯一字符串生成函数(含重试逻辑)
轮询字母+时间戳小数的唯一ID生成方案
一、字母轮询逻辑修正
你之前的代码会在超过Z后让字母长度增加,问题出在RPAD重复了当前字母。要实现单纯的A-Z循环,只需要根据序号取单个字母即可,修正后的SQL如下:
SELECT SUBSTR('ABCDEFGHIJKLMNOPQRSTUVWXYZ', CASE WHEN MOD(ROWNUM, 26) = 0 THEN 26 ELSE MOD(ROWNUM, 26) END, 1) AS cycle_letter FROM DUAL CONNECT BY LEVEL <= 130;
这段代码会生成A-Z循环的单个字母,第26行是Z,第27行回到A,以此类推,不会出现字母长度增加的情况。
二、带重试机制的唯一ID生成函数
要实现「保留当前字母、重试生成时间戳小数」的需求,可以编写PL/SQL函数,结合序列保证字母轮询顺序,用循环+存在性检查实现重试:
步骤1:先创建序列(用于跟踪轮询位置)
CREATE SEQUENCE id_cycle_seq START WITH 1 INCREMENT BY 1 CACHE 26;
步骤2:创建生成函数
CREATE OR REPLACE FUNCTION generate_unique_id RETURN VARCHAR2 IS v_letter CHAR(1); v_ts_fraction VARCHAR2(6); v_unique_id VARCHAR2(7); v_current_seq NUMBER; v_count NUMBER; BEGIN -- 获取当前序列值,确定要使用的字母 SELECT id_cycle_seq.NEXTVAL INTO v_current_seq FROM DUAL; -- 生成A-Z循环的单个字母 v_letter := SUBSTR('ABCDEFGHIJKLMNOPQRSTUVWXYZ', CASE WHEN MOD(v_current_seq, 26) = 0 THEN 26 ELSE MOD(v_current_seq, 26) END, 1); -- 循环重试:保留字母,重新生成时间戳小数,直到生成唯一ID LOOP -- 获取当前时间戳的6位微秒部分 v_ts_fraction := TO_CHAR(SYSTIMESTAMP, 'FF6'); v_unique_id := v_letter || v_ts_fraction; -- 检查ID是否已存在(替换为你的实际表名和字段名) SELECT COUNT(*) INTO v_count FROM your_id_table WHERE id_column = v_unique_id; IF v_count = 0 THEN EXIT; -- 唯一ID生成成功,退出循环 END IF; -- 短暂延迟,避免高频重试占用资源 DBMS_LOCK.SLEEP(0.001); END LOOP; RETURN v_unique_id; END; /
三、关于NOT EXISTS校验的可行性
用NOT EXISTS(或上述示例中的COUNT(*))校验唯一性是可行的,但要注意高并发场景下的潜在问题:
- 竞态条件:两个会话可能同时生成相同的ID,都通过了存在性检查,随后插入时导致重复。解决办法是给存储ID的字段添加唯一约束,即使存在性检查漏过,数据库也会抛出唯一约束异常,你可以在函数中捕获该异常并重试。
- 时间戳微秒级的重复概率极低,但高并发下仍有可能性,因此「存在性检查+唯一约束+重试」是最可靠的组合。
内容的提问来源于stack exchange,提问作者Pugzly
相关产品推荐
相关产品推荐

