【SQL-自动生成编号】按规则自动生成单据编号 以及并发问题_使用触发器、函数 等
描述:每种单据新建时,自动生成它的单据编号。
规则如:固定码+日期+流水号 (ABC1603180001)
方法一:触发器
触发器的缺点是,执行了sql之后才看到编码。
测试:流水号不能超过最大数,否则后面的号码全是0
--有两张表,客户表和项目表,要求:新建项目时自动生成项目编号,每个不同的客户的项目的编号从1开始
--项目编号格式为PJ+"-"+"客户编号"+"-"+"日期"+"-"+"流水号" create table TestAccount --创建测试客户表
(
ClientNum nvarchar(32), --客户编号
ClientName varchar(100) --客户姓名
) create table TestProject --创建测试项目表
(
ProID int primary key identity(1,1),
ProNum nvarchar(32), --项目编号
ProName nvarchar(100), --项目名称
ClientNum nvarchar(100), --客户编号
) create trigger T_AutoNumber
on TestProject after insert
as
begin
declare @one nvarchar(8), --PJ
@two nvarchar(32), --客户编号
@three nvarchar(8), --日期
@four int, --流水号
@id int --ID set @one='PJ'
set @three=CONVERT(varchar(8),GETDATE(),112)
--从inserted副本表里获取当前数据插入
select @two=ClientNum,@id=ProID from inserted
--获取编号最后4位
select @four=MAX(CAST(right(ProNum,4) as int)) from TestProject where ClientNum=@two
--对每个新客户的流水号都是从1开始,已存在客户为最大流水号加1
if @four is null
set @four=0
else
set @four=CAST(@four as int)
set @four=@four+1 update TestProject set ProNum=@one+'-'+@two+'-'+@three+'-'+RIGHT('0000'+CAST(@four as varchar),4) where ProID=@id
end
go --生成测试表数据
insert into TestAccount values('小小鸭','XXY')
insert into TestAccount values('丑小鸭','CXY') insert into TestProject(ProName,ClientNum) values('小小鸭游泳项目','XXY')
insert into TestProject(ProName,ClientNum) values('小小鸭成长项目','XXY')
insert into TestProject(ProName,ClientNum) values('丑小鸭造型项目','CXY')
insert into TestProject(ProName,ClientNum) values('小小鸭游泳项目','XXY')
insert into TestProject(ProName,ClientNum) values('小小鸭成长项目','XXY')
insert into TestProject(ProName,ClientNum) values('丑小鸭造型项目','CXY')
--订单编号自动编号:固定码+日期+流水号
create table salesOrder
(
id int primary key identity(1,1),
orderno varchar(50),
goods varchar(50),
qty int
)
CREATE trigger [dbo].[T_AutoNumByOrder]
on [dbo].[salesOrder] after insert
as
begin
declare @one varchar(10), --固定编号
@two varchar(32), --日期
@three int, --流水号
@id int --id set @one='ABCD'
set @two=right(CONVERT(varchar(8),GETDATE(),112),6) --当天没有记录的从1开始
select @three=MAX(CAST(right(orderno,4) as int)) from salesOrder where substring(orderno,5,6)=right(CONVERT(varchar(8),GETDATE(),112),6)
if not exists (select top 1 id from salesOrder where substring(orderno,5,6)=right(CONVERT(varchar(8),GETDATE(),112),6))
set @three=0
else
set @three=CAST(@three as int)
set @three=@three+1 --从inserted表获得主键的自动编号
select @id=id from inserted update salesOrder set orderno=@one+@two++RIGHT('0000'+CAST(@three as varchar),4) where id=@id
end
方法二:函数
在数据上写个函数做,在一个表里记录一个最后的值,这个函数被调用了就把最后的这个值+1
测试:此方法插入数据慢,在批量插入数据表现更明显
--创建表
create table CostBill
(
BillID int primary key identity(1,1),
CostBillNo nvarchar(50),
Client nvarchar(50),
Goods nvarchar(50),
QTY int
) --创建函数
create function GetCostBillNo(
@headStr nvarchar(10), --固定码
@date datetime) --日期
returns nvarchar(50)
begin
declare @oid2 nvarchar(50), --单据编号,临时值
@oid nvarchar(50), --单据编号,返回值
@month nvarchar(2), --月
@year nvarchar(2), --年
@ym nvarchar(4) --年月 set @month=MONTH(@date) --月
if LEN(@month)=1 --如果月只有一位
set @month='0'+@month --使月为2位长
set @year=RIGHT(CONVERT(nvarchar,year(@date)),2) --取年的后两位
set @ym=@year+@month --组成年月字符 --格式CB16030001
if exists(select * from CostBill) --如果CostBill有数据
begin
select top 1 @oid2=CostBillNo from CostBill order by BillID desc
end
else
begin
set @oid2=@headStr+@ym+'0000' --没有数据,单号流水号归0
end if CONVERT(nvarchar,LEFT(@oid2,6))<>@headStr+@ym
begin
set @oid2=@headStr+@ym+'0000'
end declare @str nvarchar(50) set @str=convert(nvarchar,(convert(int,right(@oid2,4))+1)) --订单号加一
while (4-len(@str)>0)
begin
set @str='0'+@str
end
set @oid2=@headStr+@ym+@str while exists(select * from CostBill where CostBillNO=@oid2) --如果存在该订单号
begin
set @str=convert(nvarchar,(convert(int,right(@oid2,4))+1)) --订单号加一
while (4-len(@str)>0)
begin
set @str='0'+@str
end
set @oid2=@headStr+@ym+@str
--print @oid2
end set @oid=convert(nvarchar,@oid2)
--print 'HP'+convert(nvarchar,year(getdate()))+convert(nvarchar,month(getdate()))+@str RETURN @oid
END --测试数据
insert into CostBill(CostBillNo,Client,Goods,QTY) values (dbo.GetCostBillNo('VISO','2016-04-01'),'C001','G001',100)
方法三:存储过程
方法四:程序
并发处理
当两个并发用户同时创建或保存一张同样的业务单据时,系统会返回两个相同的单据编码,产生了并发问题。
A 方案
打开业务功能时,立即为当前单据创建单据编码,比如产生单据编码SO15080004,在单据保存时,发现这张单据编码被其它的用户使用过,则重新产生一个新的业务单据编码SO15080005,如有发现此编码仍然被占用,依此向下搜寻,直到找到可以保存的单据编码。
这种方案的优点是总是可以保存单据,缺点是界面中看到的单据编码,不一定是最终保存的单据编码。
B 方案
打开业务功能时,不产生单据编码,只有在单据保存时才产生单据编码。避免了单据并发冲突。
这种方案优点是没有并发冲突,缺点是只有单据保存之后才可以看到单据编码。
C
在产生编码的同时,绑定了业务数据的ID,这样可以保证不重号不跳号
D
并发的时候不管你是什么时机生成编码都可能存在重复编码的机遇,这个的解决办法必须使用代码锁定,同时只允许一个线程允许就可以了
E
生成编码的时候,要用个lock 来保证同一时刻只执行一次。
销售单是有主表和子表的,程序添加完主表以后就可以解锁了。速度是非常非常快的。
F
在数据库中把单号做成唯一的键,在程序里面再做判断。
G
1、每个帐号生成自己属性的编号
2、每个帐号分配不同的号段,号段用完需要再向管理员申请新的可用号段。
3、编号不在窗口上显示,保存的时候再由触发器生产编号。
4、设置一个编号表,就一个字段记录当前最大编号,新增记录时从此表中取号并加1,然后更新这个表。如果用户又取消新增,则这个编号就会被跳过,形成跳号。
H
当然把生成的新的编号放入一个只有一个字段且该字段为主键的表中也可以防止同时生成2个相同的编号,不过在保存数据时候最好将该编号从该表中删除。
建立一个种子表,这个种子表用来存放你各种单据的编号
生成一个编号就将生成的最大编号放入。
如
调拨单 PO 0001
配送单 PS 0100
其实有个最简单的办法就是在保存时产生单据编号就可以了
最好不要有查询最大的记录号这个动作。容易产生重复。
设置一个编号表,就一个字段记录当前最大编号,新增记录时从此表中取号并加1,然后更新这个表。如果用户又取消新增,则这个编号就会被跳过,形成跳号。
或者保存后显示单据号
1) 获取自增的字段是可以的.
set @id=SCOPE_IDENTITY();
2) 直接采用max(id)这种是不行的,并发有重复号码.
3) 自己处理,update 这种可以,但是高并发容易跳号
create table Tb_NO(
name char(20) primary key,--待产生编号的表名
head nvarchar(10)not null default'',--编号的前缀,默认值为空
currentNo int not null default 0,--当前的编号数据
BHlen int not null default 6, --编号数字部分的长度
descript nvarchar(40))--对编号的描述
--向编号表Tb_NO中添加记录,记录客户投诉表中的编号信息
insert into tb_no select ' tbl Cus_com’,'TS',0,4,'客户投诉表编号'
--创建存储过程,产生新编号
create proc pro_nextNO
@Name char(20),--待产生编号的表名
@NO nvarchar(20) output --返回新编号
as
begin tran
update tb_no with(rowlock)
set @NO=head+right(power(10,BHlen)+currentNo+1,BHlen),
currentno=currentno+1
where name=@Name
commit tran
go
====
【SQL-自动生成编号】按规则自动生成单据编号 以及并发问题_使用触发器、函数 等的更多相关文章
- RookeyFrame Bug 编号显示 系统自动生成 的问题,有时候依旧会显示text文本框
编号显示 系统自动生成 的问题,有时候依旧会显示text文本框 1.在线新建model -> 启用编码规则 -> 新建字段Code(主键) 2.跟Code字段 创建编码规则 3.新增菜单 ...
- SpringMVC学习系列-后记 结合SpringMVC和Hibernate-validator,根据后台验证规则自动生成前台的js验证代码
在SpringMVC学习系列(6) 之 数据验证中我们已经学习了如何结合Hibernate-validator进行后台的数据合法性验证,但是通常来说后台验证只是第二道保险,为了更好的用户体验会现在前端 ...
- Microsoft Dynamics CRM4.0 创建单据的时候,自动生成单据编号的通用方法
一.新建两个实体,具体如下: 单据流水号(new_maxbillcode) 显示名称 名称 类型 格式 最大长度 需求级别 IME模式 备注 名称 new_name nvarchar 文本 100 业 ...
- odoo10如何自定义自动生成单据编号
1.在已有的model中穿件一个字段name class qingjiadan(models.Model): _name = 'qingjia.qingjiadan' name = fields.Ch ...
- 让Hibernate生成的DDL脚本自动增加注释
我们知道可以通过Hibernate对象自动生成DDL建表语句,通过PowerDesigner工具可以反向工程生成数据字典,但是在生成的DDL中一直不能写上中文的注释,这就使我们生成的数据字典不具有可用 ...
- 基于数据库的代码自动生成工具,生成JavaBean、生成数据库文档、生成前后端代码等(v6.0.0版)
TableGo v6.0.0 版震撼发布,此次版本更新如下: 1.UI界面大改版,组件大调整,提升界面功能的可扩展性. 2.新增BeautyEye主题,界面更加清新美观,也可以通过配置切换到原生Jav ...
- jenkins自动打包生成docker镜像后自动发布并nginx代理访问
之前曾写过docker及jenkins基础使用 https://www.cnblogs.com/xiaochangwei/category/816943.html 现在搭建环境的功能为: 1.jen ...
- openerp学习笔记 单据自动编号(编码规则)
说明: 单据自动编码允许定义 单据前缀+按当前年.月.日.时.分.秒+流水号+单据后缀 单据自动编号允许按所有公司统一编号或按分公司单独编号 单据自动编号中的流水号部分未按月重新编号,不断累计,当超出 ...
- 看数据库的文件大小 MySQL Binlog日志的生成和清理规则
小结: 1.避免并行大大事务对磁盘.内存的消耗: MySQL数据文件导致实例空间满的解决办法_空间/内存_常见问题_云数据库 RDS 版-阿里云 https://help.aliyun.com/kno ...
随机推荐
- 解析之Nginx解析
- java学习-3
输入语句Scanner的使用方法 1.导包 import java.util.Scanner 2.创建 从键盘输入:Scanner sc = new Scanner(System.in); 3.使用 ...
- Shell初学(五)bash shell的基本功能
记住,所谓的bash shell 并不单纯指的是shell脚本,其实是Linux系统的所有指令集. shell脚本 只是汇总了指令集到文件,然后按流程和顺序执行. [1]如何查看我们的预设shell ...
- 错误:编码GBK的不可映射字符解决办法
今天在cmd测试java代码的时候遇到了一个错误 解决办法: 输入javac -encoding utf-8 文件名.java 原因: 由于JDK是国际版的,我们在用javac编译时,编译程序首 ...
- spark教程(11)-sparkSQL 数据抽象
数据抽象 sparkSQL 的数据抽象是 DataFrame,df 相当于表格,它的每一行是一条信息,形成了一个 Row Row 它是 sparkSQL 的一个抽象,用于表示一行数据,从表现形式上看, ...
- redis 学习(7) -- 有序集合
redis 学习(7) -- 有序集合 zset 结构 有序集合:有序.不能包含重复元素 每个节点包含:score和value两个属性,根据score进行排序 如图: zset 重要 API 含义 命 ...
- 剑指offer-扑克牌顺子-知识迁移能力-python
题目描述 LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决 ...
- JVM内存模型入门
JVM内存模型入门 本文是学习笔记,原文地址在:https://www.bilibili.com/video/av62009886 综述 其实没有太多新东西 JVM主要分为五个区域:栈区.堆区.本地方 ...
- Java中字符串排序
package com.fs.test; import java.util.ArrayList; import java.util.Collections; import java.util.List ...
- 冒泡排序(java可直接跑,算法思想等小儿科不多说直接上代码)
import java.util.Arrays; /** *冒泡排序:时间复杂度O(N^2),空间复杂度O(1),稳定的排序 * 每趟确定一个元素的位置,所以需要arr.length趟排序, */pu ...