重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
您好,oracle数据库的话可以利用rownum取当前记录的顺序,然后赋值给流水号就可以了。
创新互联网络公司拥有10余年的成都网站开发建设经验,上千客户的共同信赖。提供网站制作、成都网站建设、网站开发、网站定制、外链、建网站、网站搭建、响应式网站、网页设计师打造企业风格,提供周到的售前咨询和贴心的售后服务
sqlserver可就没什么好办法了,只能alter table xxx add id nvarchar(10) indentity(1,1)了,然后再将id赋值流水号。
---定义当前的流水号,可以从数据库中获取,此处为测试值
DECLARE @CURR_LSH VARCHAR(7)
---新流水号
DECLARE @LSH VARCHAR(7)
---旧流水号第5位
DECLARE @FIRSTCHAR VARCHAR(1)
---旧流水号最后2位的数值
DECLARE @LASTCHAR2 INT
---旧流水号第5位的asc码
DECLARE @FIRSTCHARASCII INT
---新的后3位流水号
DECLARE @LASTCHAR3NEW VARCHAR(3)
---测试的旧流水号
SET @CURR_LSH = 'B307Z98'
---定义当前年份最后1位和两位的月份
DECLARE @YEAR VARCHAR(1)
DECLARE @MONTH VARCHAR(2)
SET @YEAR = SUBSTRING(CONVERT(VARCHAR,DATEPART(YEAR,GETDATE())),4,1)
SET @MONTH = RIGHT('0' + CONVERT(VARCHAR,DATEPART(MONTH,GETDATE())), 2)
SET @FIRSTCHAR = SUBSTRING(@CURR_LSH, 5,1)
SET @FIRSTCHARASCII = ASCII(@FIRSTCHAR)
---@FIRSTCHARASCII 48-57表示0-9,65-90表示A-Z
SET @LASTCHAR2 = CONVERT(INT,SUBSTRING(@CURR_LSH,6,2))
IF @LASTCHAR2 99
BEGIN
SET @LASTCHAR3NEW = @FIRSTCHAR + RIGHT('00' + CONVERT(VARCHAR, @LASTCHAR2 + 1),2)
END
ELSE
BEGIN
IF @FIRSTCHARASCII 57
SET @LASTCHAR3NEW = CONVERT(VARCHAR, CONVERT(INT,@FIRSTCHAR + '00') + CONVERT(INT, RIGHT('00' + CONVERT(VARCHAR, @LASTCHAR2 + 1),3)))
ELSE IF @FIRSTCHARASCII = 57
SET @LASTCHAR3NEW = 'A00'
ELSE
SET @LASTCHAR3NEW = CHAR(@FIRSTCHARASCII + 1) + '00'
END
---输出你需要的新流水号
SET @LSH = 'B' + @YEAR + @MONTH + @LASTCHAR3NEW
---打印出来
我没太看明白你的需求,,,
大致给你个思路
流程单据表(这里不知道你这个表是干什么用的,按我的理解来给你说)
流程定义 流程编号
内容:请假 QJ
借款 JK
然后你的其他几个单据流水号,应该就是单据号
都以流程单据中的流程编号开头
例如请假单据流水号就为QJ20140422,,,,这样为结构
这样的话,你的凭证界面就能和你的流程单据有关联了;
当然也有其他的很多方式,也可以再给你提供一种
流程定义 流程编号 凭证号
内容:请假 QJ 001
借款 JK 002
加入凭证号,然后每种单据也加入凭证号字段,
例如请假单,就默认他的凭证号都为001,这样一样是可以有关联
你可以具体根据实际情况,考虑我给你的方式
SQL Server 2012 版本, 新增了 序列号的功能。
可是实现类似的处理。
如果是 SQL Server 2008 或者以下版本的, 可以使用 identity + 计算列的处理机制。
oracle:
select 'P'||trim(to_char(patient_new_id.nextval,'009999999')) patient_id from dual
'P' 是 固定字符串.
|| 是 连接字符串
trim 是去除 字符串前后的空格
to_char 是把数字类型的, 转换为 字符类型
patient_new_id.nextval 这里的 patient_new_id 应该是一个序列号, nextval 是获取序列号的下一个值。
'009999999' 是 TO_CHAR 函数的 格式化字符串, 0 意味这如果数字不到这一位,那么也要显示0。
patient_id 是前面那个 'P' || ...... 查询结果的 别名。
from dual 是因为 Oracle 不允许你像 sql server 那样 select 1 这样的处理。 必须要 from 一个表, 因此只好 from 一个 只有1行数据的系统表。
目前是在.net中使用的这个方法,使用sqlserver的时间戳来控制并发情况下容易产生重复序列号的问题。
原理类似hibernate的主键生成机制,在系统启动时从数据库中读出最大的流水号,赋给一个类的静态变量,需要时,从该静态变量中取得流水号,加1后,就是你要的值。
代码:
public class testA {
public static Long maxNo;
static {
synchronized(maxNo){
maxNo = select max(maxNo) from DB;
}
}
public synchronized static long getMaxNo(){
return(++maxNo);
}
}
优点:保证同步,不会取到相同的值,同时避免了数据库的反复读取。
缺点:在cluster环境下
还是用上面的方法,但是只能在一台机器上发布并且绑定到JNDI,别的机器可以通过配置文件得知去哪里产生maxNo(实际上,每台机器都可以发布,但是所有机器只能去访问指定的一台机器),这样的效率虽然低了点,但是还是觉得比每次去数据库里取好点。当然,可以改进,就是再加一个本地类,每次去JNDI那里的类去取数据时,一次取20个回来(就是JNDI上面的类一次要加20),以后本地就用这20个值,用完了,再去JNDI上面的类去取。
当然,前面的这两个例子都比较复杂,甚至还有人提出用单态的方法。而我在.net中用的就比较简单了。方法如下:
在数据库(sqlserver)中新建一张表(sequence_num),专门用来生成流水号。
字段1:s_id,varchar(50),notnull
字段2:tmstmp,timestamp,notnull
代码如下:
public string getOderid()
{
private SqlConnection conn = null;
private SqlCommand comm = null;
private string s_id = "";
private byte [] tmstmp;//时间戳类型在.net中对应为一个byte数组
private int s_idplus = 0;
private string orderid = "";
try
{
conn = SqlConn.getConn();
conn.Open();
do
{
string sqlQuery = "select s_id,tmstmp as tmstmp from sequence_num";
comm = conn.CreateCommand();
comm.CommandText = sqlQuery;
SqlDataAdapter da = new SqlDataAdapter(comm);
DataTable dt = new DataTable();
dt.Clear();
da.Fill(dt);
comm.Parameters.Clear();
if (!dt.Rows[0]["s_id"].ToString().Substring(0,12).Equals(DateTime.Now.ToString("yyyyMMddHHmm")))//每一分钟重置一次计数,实际上我认为每天重置一次比较好
{
s_id = DateTime.Now.ToString("yyyyMMddHHmm")+"0001";
tmstmp = (byte[])dt.Rows[0]["tmstmp"];
}
else
{
s_id = dt.Rows[0]["s_id"].ToString();
tmstmp = (byte[])dt.Rows[0]["tmstmp"];
s_idplus = Convert.ToInt32(s_id.Substring(12)) + 1;
s_id = s_id.Substring(0,12)+Convert.ToString(s_idplus).PadLeft(4,'0');
}
string sqlUpdate = "update sequence_num set s_id = @s_id where tmstmp = @tmstmp";//保证在记录未被修改的情况下更新,如果更新不成功,则重走一遍生成序列号的流程
comm.CommandText = sqlUpdate;
comm.Parameters.Clear();
comm.Parameters.Add("@s_id",SqlDbType.VarChar,50);
comm.Parameters["@s_id"].Value = s_id;
comm.Parameters.Add("@tmstmp",SqlDbType.Timestamp);
comm.Parameters["@tmstmp"].Value = tmstmp;
}
while(comm.ExecuteNonQuery()=0);
}
catch(SqlException ex)
{
string aaa = ex.Message;
}
finally
{
if(conn!=null)
{
conn.Close();
}
}
return s_id;
}
这个流程都是在.net里实现的,实际使用中可以通过存储过程来实现。当然,这种方法对数据库的访问会比较频繁,另外,数据库表建立的时候必须插入一条初始值,这是代码不完善的地方,有时间的话我会去完善它。只是判断如果表内没有记录的情况下就插入一条初始记录,这个时候如何处理并发问题还没想明白。
oracle数据库的话可以利用rownum取当前记录的顺序,然后赋值给流水号就可以了。
sqlserver可就没什么好办法了,只能alter
table
xxx
add
id
nvarchar(10)
indentity(1,1)了,然后再将id赋值流水号吧
正好前几天干过这事儿。
首先需要定义一张流水号表:
--serialNumber是流水号表,每个type每个prefix只有一条记录,保存最新的流水号
create table serialNumber ([type] varchar(20),[prefix] varchar(20),SN int);
其次定义一个存储过程,根据指定的type和prefix,获得最新的流水号:
create procedure proc_getSN(@type varchar(20),@prefix varchar(20),@sn int output)
as
begin
set transaction isolation level serializable;
begin tran
--更新流水号
update serialNumber set SN=SN+1 where [type]=@type and [prefix]=@prefix;
if @@rowcount=0
begin
insert into serialNumber([type],[prefix],[SN]) values (@type,@prefix,1);
select @sn=1;--这里不要select from serialNumber了,否则会死锁
end
else
begin
select @sn=isnull(SN,0) from serialNumber where [type]=@type and [prefix]=@prefix;
end
commit;
set transaction isolation level read committed;
end
需要获取流水号的时候,像这样:
--[order]是订单表,生成订单号的规则是'C'+8位日期+4位流水号
declare @sn int;
exec proc_getSN 'contract','C20140105',@sn output;
insert into [order]
select 'C20140105'+right('0000'+cast(@sn as varchar),4),'contract',...