网络环境下的复杂编号
普通用户可以忽略本节内容。
本节内容可以参考CaseStudy目录下的文件:网络环境下的复杂编号.table。
提醒:由于OpenQQ的出现,本节的内容已经过时,请参考:用OpenQQ实现网络环境下的编号
使用自动增量主键,例如_Identify列,能够有效解决编号的问题,即使是多人同时向一个表中增加行,也不会出现重复的编号。
但是自动增量主键只是一个数字,无法实现复杂的含有特殊信息的编号。
如果自己编码生成编号,如何避免在多人同时操作时产生重复的编号,是一个难题。
本节用一个简单的例子,来探讨如何解决这个问题。
本节的例子使用外部数据源,数据源文件为CaseStudy目录下的文件:编号.mdb
该文件只包括两个表,一个订单表,一个编号表。
希望订单表的编号能够自动生成,编号的格式为:0000-0000,左边四位为前缀,两位表示年,两位表示月,右边四位表示顺序号。
编号表并非常规的数据表,不加载到Foxtable中,而是直接通过SQL语句操作。
编号表包括两列,分别是:前缀和顺序号,前者为字符型,由两位年两位月组成,后者为整数型,用于保存此前缀的当前顺序号。
设计步骤:
1、因为订单表的编号列是主键列,所以在任何时候都不能为空,必须在增加行的时候,为新增行生成一个临时的编号,为此我们将订单表的DataRowAdding事件设置为:
Static Index
As
Integer =
99999
e.DataRow("编号")
= Format(Date.Today(),"yyMM")
& "-" & Index
Index = Index - 1
和正式的编号不同,临时编号的顺序号为5位,而且是从99999开始倒序编号,这样用户一看就知道这是临时的编号。
2、在保存的时候,为新增的行生成正式的编号,BeforeSaveDataRow事件代码设置为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
Dim dr
As
DataRow = e.DataRow Dim pf As String If dr.RowState <> DataRowState.Added Then '如果不是新增行 Return '那么返回 ElseIf dr.IsNull("日期") Then '如果没有输入日期 e.Cancel = True '取消保存此行 MessageBox.Show("必须输入日期!","提示",MessageBoxButtons.OK,MessageBoxIcon.Information) Return Else pf = Format(dr("日期"),"yyMM") '否则获得编号的前缀,两位年,两位月 End If Dim cmd1 As New SQLCommand Dim cmd2 As New SQLCommand Dim Key As Integer cmd1.ConnectionName = "编号" '设置数据源名称 cmd2.ConnectionName = "编号" cmd1.commandText = "Select Count(*) From [编号] Where [前缀] = '" & pf & "'" If cmd1.ExecuteScalar = 0 Then '如果编号表不存在前缀的行,那么增加一行 cmd1.commandtext = "Insert Into 编号 (前缀, 顺序号) Values('" & pf & "',1)" cmd1.ExecuteNonQuery End If cmd1.commandText = "Select [顺序号] From [编号] Where [前缀] = '" & pf & "'" Do Key = cmd1.ExecuteScalar() '从后台获得顺序号 cmd2.commandText = "Update [编号] Set [顺序号] = " & (Key + 1) & " Where [顺序号] = " & Key & " And [前缀] = '" & pf & "'" If cmd2.ExecuteNonQuery() > 0 Then '更新顺序号 Exit Do '更新成功则退出循环 End If Loop e.DataRow("编号") = pf & "-" & Format(Key,"0000") |
第10行代码获得编号的前缀(由两位年两位月组成),第17、18行代码判断编号表是否存在此前缀的记录,如果不存在,第19、20行代码则增加此前缀的记录,并将顺序号设置为1。
最关键的是第22到第30行代码,第24行代码首先从后台的编号表获得此前缀的顺序号,第26行代码尝试将该前缀的顺序号加1,如果操作成功则退出循环,并在第30行将前缀和顺序号组合成编号写入到要保存的行中。
那么在多人同时使用的时候,是如何避免重复的呢;假定A和B几乎同时执行了第24行代码,获得的顺序号相同,同样又几乎同时执行第26行代码,假定A用户抢先了一点,成功将该前缀的顺序号增加了1,随后B用户执行的时候,由于顺序号已经加1,所以Update语句的条件不符合,导致第26行的ExecuteNonQuery方法返回0,
无法退出循环,于是B用户只能重复上述操作:获得顺序号,将顺序号加1,直到成功为止。
多表编号
上述的代码只是对一个订单表进行自动编号,如果一个项目中有多个表需要进行类似的编号,可以修改编号表的结构,包括三列,分别是:表名、前缀和顺序号,表名和前缀是字符型,顺序号是整数型。
所有需要自动编号的表的DataRowAdding事件代码依然为:
Static Index
As
Integer =
99999
e.DataRow("编号")
= Format(Date.Today(),"yyMM")
& "-" & Index
Index = Index - 1
而BeforeSaveDataRow事件代码需要稍微调整一下,加上表名的条件:
Dim
dr As DataRow = e.DataRow