题外话:最近一工作的朋友和我讨论了自动生成编号问题,今天打算记录下,其实没有什么技术含量,只是用来解决问题的一种手段,就这样。

>>先看一种简单的示例

一些项目中需要根据当前时间或者什么标记自动生成编号,如订单号,发表的编号等, 要实现这一功能一般一个存储过程就OK了,先看下面的SQL语句

create table #a
(
m int,
n varchar(20)
)

declare @a int
declare @b datetime
declare @c varchar(20)
set @a=1
set @b='2007-01-02'
set @c='A2007012'
while(@a<10)
begin
insert into  #a values(@a,@c)
set @a=@a+1
set @b=dateadd(dd,1,@b)
set @c='A'+datename(yyyy,@b)+datename(mm,@b)+datename(dd,@b)
end

这是通常的的小Demo ,也就是拼接SQL ,但这样会有个问题,就是不通用,如现在要添加个订单号, 就需要与订单详情表(OrderDetail)内连接 ,维护起来不易。

>>表的设计(MaxIDTab)

要维护一个编号的自动生成,就需要一个记录当前编号值的地方,可能维护的对象不止一个,如现在我们需要这种格式 E0001, 这时我们就考虑到用一张表来维护。

1.要维护的实例不止一个 ,需要一个字段来维护,内连该表的编号对象(NoType)

2如表MaxIDTab中需要存当前的编号值,以方便下次自增(NoValue)

3.考虑到编号需要有一种格式,也可能不同的对象需要的编号格式一样,如上面所说的一种E0001 ,有的实例可能也需要关键字与时间来拼接,可以运用E{0}来根据不同的实例给予不同的格式,如我们需要2011-9-23 0001的格式,只需要修改下E就可以了。而{0}可以取得NoValue的值 (NoFormatText)

4.不难发现上面2011-9-23 0001的格式 ,需要用"0"来填充,我们也可以根据需要要来控制而字段(NoNumLength)就是来维护填充时个数问题

综上所述就可以用来维护多个表的对象不同格式的自动生成编号问题了

CREATE TABLE  MaxIDTab
(
    NoType VARCHAR (10)    PRIMARY KEY,--需要维护的对象
    NoValue INT ,--编号值
    NoFormatText VARCHAR (50),--编号的格式
    NoNumLength INT --要填充个数
)

 

>>代码的实现

如我们现在需要自动生成产品表与订单表的编号 只需要插入两条数据

insert into MaxIDTab (NoType,NoValue ,NoFormatText ,NoNumLength) values ('Product',0,'P--{0}',5)
insert into MaxIDTab (NoType,NoValue ,NoFormatText ,NoNumLength) values ('Order',0,'O--{0}',5)

如现在需要产品编号 P0001的格式,值需要取出NoType的值0,Int proNo = NoValue.Value.ToString(),这时我们需要在proNo前填充NoNum填充NoNummatText 个0(当然不一定是0,也可以是其他的标志格式)组成 000005的格式,填充函数自定义就可以,先 info.NoValue.Value.ToString().PadLeft(info.NoNumLength.Value, '0')组成000005的格式。

public string PadLeftFun(string oStr, int len, char addCha)
        {
while (oStr.Length < len)
            {
                oStr = oStr + addCha;
            }

return oStr;
        }

和需要很相似了只需要格式化下字符串 ,巧妙的用到{0}取值,组成E000000的格式

return string.Format(info.NoFormatText, info.NoValue.Value.ToString().PadLeft(info.NoNumLength.Value, '0'));

现在如果需要拼接时间也很好办,只需要把info.NoValue.Value.ToString().PadLeft(info.NoNumLength.Value, '0')中的0改成DataTime.Now 就Ok了,要改其他表的编号格式也可以,想要加一个表的编号,只需要在表中插入要维护表的参数就行了 ,这样一张表就维护了所有的自动编号列

上述还有一个事物并发的问题,完整代码如下

/// <summary>
/// 传入编号类型 得到此类型的新编号
/// </summary>
/// <param name="noType"></param>
/// <returns></returns>
        public string GetNewNo(string noType)
        {
using (SqlConnection conn = new SqlConnection(DBSetting.ConnText))
            {
            //取出编号数据
                conn.Open();
                SqlTransaction tran = conn.BeginTransaction();//事物 锁 行/表/数据库/排他锁
                string cmdText = "SELECT NoType,NoValue,NoFormatText,NoNumLength FROM MaxIDTab where NoType = @NoType ";
                MaxIDTabInfo info = new MaxIDTabInfo();
                SqlDataReader rdr = SqlHelper.ExecuteReader(tran, CommandType.Text, cmdText, new SqlParameter("@NoType", noType));
if (rdr.Read())
                {
                    info.NoValue = (int)rdr["NoValue"];
                    info.NoFormatText = rdr["NoFormatText"].ToString();
                    info.NoNumLength = (int)rdr["NoNumLength"];
                }
                rdr.Close();
                //更新最大编号//把NoValue加1
                string updateCmdText = "UPDATE MaxIDTab SET NoValue = @NoValue  WHERE NoType = @NoType";
                SqlHelper.ExecuteNonQuery(tran, CommandType.Text, updateCmdText , new SqlParameter("@NoValue", (info.NoValue + 1)) , new SqlParameter("@NoType", noType));
                tran.Commit();//解锁
                //把NoValue左侧补充0,长度为NoNumLengt
//把NoValue格式化为 NoFormatText
                return string.Format(info.NoFormatText, info.NoValue.Value.ToString().PadLeft(info.NoNumLength.Value, '0'));
              }
        }

>>改存储过程

实现的原理上面说的都很明白了,下面贴出代码

CREATE PROC sps_GetNoByNoType(@NoType varchar(50))
AS
begin
DECLARE @noValue INT,
@noFormatText VARCHAR(50),
@noNumLength INT,
@newNo VARCHAR(50)
BEGIN TRAN newNoTran--开始事务
        --得到值
        SELECT @noValue = NoValue,@noFormatText = NoFormatText,@noNumLength = NoNumLength FROM MaxIDTab WHERE NoType = @NoType    
--更新最大数
        UPDATE MaxIDTab SET NoValue = (@noValue + 1) WHERE NoType = @NoType
COMMIT TRAN--提交事务
    
SET @newNo = CAST(@noValue AS VARCHAR)--转字符串
    SET @newNo = dbo.PadLeft(@newNo,@noNumLength)--组成0001形式
    SET @newNo = REPLACE(@noFormatText,'{0}',@newNo)--拼接替换字符串E{0}
    --输出参数
    SELECT @newNo
end

调用代码

/// <summary>
/// 通过存储过程获得新编号
/// </summary>
/// <returns></returns>
        public string NewNo(string noType)
        {
string cmdText = "sps_GetNoByNoType";
       /*  
            using (SqlConnection conn = new SqlConnection(DBSetting.ConnText))
            {
                conn.Open();

                SqlCommand cmd = new SqlCommand(cmdText, conn);
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add(new SqlParameter("@noType", SqlDbType.VarChar, 50));
                cmd.Parameters["@noType"].Value = noType;

                object obj = cmd.ExecuteScalar();
                if (obj != null && !Convert.IsDBNull(obj))
                {
                    return obj.ToString();
                }

                throw new Exception("没有正确生成编号!");
            }
*/
object obj = SqlHelper.ExecuteScalar(DBSetting.ConnText, CommandType.StoredProcedure, cmdText, new SqlParameter("@noType", noType));

if (obj != null && !Convert.IsDBNull(obj))
            {
return obj.ToString();
            }

throw new Exception("没有正确生成编号!"); 
        }

>>总结

上述虽然说没有什么技术含量,但是一个好的实现思想,以上就是我的一些浅薄认识,个人水平有限,难免有不足之处 ,好了 ,就这样!