简易发号SQL,可用于生成指定前缀自增序列,如订单号,生成优惠券码等

Posted wpycs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简易发号SQL,可用于生成指定前缀自增序列,如订单号,生成优惠券码等相关的知识,希望对你有一定的参考价值。

需求1:订单号要求唯一、长度不太长、自增、但不能通过早上订单号和晚上订单号相减推算出平台大概一天的单量

需求2:要求生成10w张优惠券,要求券码唯一、不能太长,不能轻易猜测出其他券码

根据这些需求提供一个简单的数据库发号的sql来满足上面的需求,介绍如下

Increments表设计两个字段,Prefix和MaxNum,不需要设计主键和聚集索引,在Prefix上建立唯一索引,可Include[MaxNum]字段,这样查询时可以保证索引覆盖

将前缀和数据拼接的服务由应用来提供,数据库自负责拿到增加后的数字,建议封装成存储过程,切记此脚本本身已包含事务,不要在应用程序中再使用事务嵌套(另外本人非dba,技术水平有限,不能保证下面sql不会出现死锁)

DECLARE @Prefix nvarchar(32)=C --输入前缀
DECLARE @Qty int=1 --输入增长步长
SET TRANSACTION ISOLATION LEVEL READ COMMITTED --设置隔离级别
BEGIN TRAN --开启事务
    DECLARE @maxNum bigint=0 --声明目前指定前缀的当前最大数字
    reTry: --goto的label
    SELECT
    @maxNum = MaxNum + @Qty
    FROM Increments WITH (UPDLOCK)
    WHERE Prefix = @Prefix --使用更新锁查询指定前缀的目前种子数+步长,并发时,如果存在记录,只有一个连接可以查询到此记录,其他连接会被阻塞至事务提交后
    IF (@maxNum = 0) --如果未查到此前缀
    BEGIN
        BEGIN TRY
            SET @maxNum = @maxNum + @Qty
            INSERT Increments
              VALUES (@Prefix, @maxNum) --插入前缀记录,设置目前种子数等于步长,并发情况下同样的前缀可能会同时会执行插入操作,通过在Prefix设置唯一索引使只有一个插入成功,其他重新reTry
        END TRY
        BEGIN CATCH
            GOTO reTry --插入失败时reTry
        END CATCH
    END
    ELSE
    BEGIN
        UPDATE Increments
        SET MaxNum = MaxNum + @Qty
        WHERE Prefix = @Prefix --查到种子数的情况下,更新记录
    END
    SELECT
      @maxNum --返回目前种子数+步长的结果
COMMIT

订单号:短日期作为前缀+随机(1-9)步长+随机数字,如

20180108 00016 037 --示例,正常情况中间没有空格
20180108 00019 233

券码:批量生成10w张,单字母作为前缀(如T),步长设置为100W,获取数字(如2000000),生成1000000-2000000的数组,通过洗牌算法获取10w个元素,将每个元素转换为36进制

TSM9H   --对应T1335221
TSMC9   --对应T1335321

然后类似身份证校验码,自己设计一个算法增加1-2位校验码在开始或者结束,如所有数字*自身相加取最后一位放在最后,例

--T转换为数字为29

TSM9H8  --对应TSM9H 2*2+9*9+1*1+3*3+3*3+5*5+2*2+2*2+1*1=138  
TSMC93  --对应TSMC9 2*2+9*9+1*1+3*3+3*3+5*5+3*3+2*2+1*1=143

 

以上是关于简易发号SQL,可用于生成指定前缀自增序列,如订单号,生成优惠券码等的主要内容,如果未能解决你的问题,请参考以下文章

系统中生成编号/单号问题的实现方案讨论

基于mysql的单据号生成(前缀+日期+自增id+后缀)

使用Redis中的incr实行自增,来实现订单号

简易发短信

Oracle间接实现自增主键

雪花算法