唯一数字标识生成

3
我需要为数据库记录生成唯一编号的逻辑。在数据库中生成的id是一个单独的列。
目前,当用户调用“创建记录”操作时,我保存新记录,获取其数据库id,使用此id生成记录编号,然后将其放入编辑表单中。使用这种方式意味着所有实体字段都应该可为空,以保存记录到数据库。
我不喜欢这种方式。我知道应该有更好的方法。
是否有更好的实践方法来生成唯一编号?生成非唯一随机数的可能性是多少?
谢谢。

Jeff Atwood在他的博客上有一篇关于GUID生成的文章,你可以参考一下。http://www.codinghorror.com/blog/2005/10/equipping-our-ascii-armor.html - JKhuang
我有一段时间前遇到了类似的问题(https://dev59.com/clPTa4cB1Zd3GeqPkpvv),但是没有找到一个好的解决方案。在我的情况下,我们希望能够在填写表格时通过电话读取ID号码。一些答案建议使用GUID,这将是唯一的,但并不适合我的问题。 - Rup
5个回答

7
你目前使用的方法,即保存空记录以获取ID的方式,并不好。
标准做法是,在创建记录时只显示一个空表单(此时ID通常为0)。用户填写表单后,只有在用户点击保存按钮时才会将数据提交到数据库,并且ID应该是一个自增长列。
你的方法有一个问题,如果用户没有完成表单,你会在数据库中留下很多不完整的记录。而且,它使得处理数据验证和完整性更加困难。
如果您真的必须向用户显示ID,则替代方案是创建一个单独的表格,其中包含一行具有“下一个记录ID”列的内容。可以将此列递增并作为原子操作返回,然后用于填充新记录的ID。您仍然不会创建真正的记录,只需在Create Record操作中递增此“Next Record ID”。使用此方法,对于多个实体,可以在此“Record IDs”表中分别拥有单独的行来使用相同的方法。请注意,如果用户最终未将记录保存到数据库中,则ID仍将被“使用”,这些数字仍将是唯一的,并且将按时间顺序排列,但不一定是连续的。

我之前尝试过第二种方法。这意味着第一张表上的ID列不能再是自增的(因为据我所知,EF不支持SET IDENTITY INSERT),但这并不是太大的问题。 - Rup
但是,如果我不保存记录到数据库,我该如何递增下一个记录ID? - xwrs
@xwrs,下一个记录ID只是您实体中单独表格中的数字值。更新包含下一个记录ID的行以获取实体的ID没有问题 - 但在用户在数据输入屏幕上点击“保存”之前,请勿将实体插入表格中。 - Steve Morgan
@Rup,没错,您正在手动实现等同于标识列的功能,因此 ID 不应该是。 - Steve Morgan

1
表格代码...
CREATE TABLE TblTransactions(
    TId varchar(8),
    TName varchar(50)
)

C# 代码后端...

protected void Page_Load(object sender, EventArgs e)
{
    string id = GenerateId("TblTransactions", "TId", 8, "TRN");
// insert the id along with data in the table
    Response.Write(id);
}

public string GenerateId(string TableName, string ColumnName, int ColumnLength, string Prefix)
{
    SqlConnection con = new SqlConnection("server=.;integrated security=true;database=EBissCard");
    string Query, Id;
    int PrefixLength, PadLength;
    PrefixLength = Convert.ToInt32(Prefix.Length);
    PadLength = ColumnLength - PrefixLength;
    Query = "SELECT '" + Prefix + "' + REPLACE(STR(MAX(CAST(SUBSTRING(" + ColumnName + "," + Convert.ToString(PrefixLength + 1) + "," + PadLength + ") AS INTEGER))+1," + PadLength + "),' ',0) FROM " + TableName;

    SqlCommand com = new SqlCommand(Query, con);
    con.Open();
    if (com.ExecuteScalar().ToString() == "")
    {
        Id = Prefix;
        for (int i = 1; i <= PadLength - 1; i++)
        {
            Id += "0";
        }
        Id += "1";
    }
    else
    {
        Id = Convert.ToString(com.ExecuteScalar());
    }
    con.Close();
    return Id;
}

1
这是 Steve 第二个想法的实现吗?使用一个简单的表来记录新发行的 ID,并将实际数据存储在另一个表中? - Rup

1

我不是很明白,但如果你在数据库中使用uniqueidentifier数据类型,在C#中它的对应类型是Guid,因此你可以这样写:

public Guid CreateRecord(MyObject model) {

    Guid newId = Guid.NewGuid();

    MyTable tbl = new MyTable();
    tbl.guid = newId;

    // ... other columns

    db.MyTable.AddObject(tbl);
    db.SaveChanges();

    return newId;
}

虽然我通常的做法是将PrimaryKey设置为int,并添加一个名为guiduniqueidentifier字段(我在公共场合使用它而不是column_id),并记得对该列进行索引。


Guid不是一个数字,但在将其保存到数据库之前,我需要一个九位数字来进行附加记录标识。 - xwrs
Guid是一个随机数(当您使用Guid.NewGuid()时),它存在于C#和SQL中(使用uniqueidentifier数据类型)...为什么不直接使用它呢? - balexandre
@xwrs,在编程中,Guid实际上是一个128位的数字。 - kenny

0

尝试使用 .net 自带的 Random 类。


问题在于它需要与数据库中已有的任何内容以及其他可能同时打开网络表单的所有用户保持唯一。你需要的不仅仅是一个随机数来保证这一点。 - Rup

0

为记录生成唯一编号的一个想法是使用毫秒级时间戳(自某个参考时间点,例如2010年1月1日)。

然而,如果有两条记录同时被更新,这可能会导致问题。为了解决这个问题,可以在创建用户ID时为每个用户分配一个数字,将该“用户编号”和毫秒级时间戳连接起来,就可以得到所需的唯一编号。


实际上,在将记录保存到数据库之前,我必须向用户显示记录的ID。 - xwrs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接