SQL Server中的数据库范围内唯一而简单的标识符

47

首先,我知道这个问题,但使用 GUID 的建议并不适用于我的情况。

我需要简单的唯一标识符,以便我的用户可以轻松地通过电话交流这些信息:

你好,我的订单号是1584

而不是

你好,我的订单号是4daz33-d4gerz384867-8234878-14

我希望这些标识符在整个数据库中是唯一的,因为我有几种不同类型的“对象”...有订单ID、送货ID和账单ID等等。由于它们之间没有一对一的关系,我无法猜测一个ID是指哪种对象。

有了数据库范围内的唯一标识符,我可以立即知道客户所指的对象是什么。我的用户只需在搜索工具中输入一个ID,我就可以为他保存额外的点击以进一步细化搜索内容。

我的当前想法是使用带有不同种子 (seed) 1、2、3 等和增量值为100的身份列 (identity column)。

然而,这引出了一些问题:

  • 如果我最终获得超过100个对象类型怎么办?我可以使用1000或10000,但这种不良扩展性的做法有点可疑。

  • 种子可能会“丢失”吗(在复制、数据库问题等情况下)?

  • 更一般地说,还有其他问题需要注意吗?

  • 是否可以将非整数(我目前使用 bigint)用作身份列,以便我可以在ID前缀中添加表示对象类型的内容(例如 varchar 列)?

  • 是否使用一个只包含标识列和对象类型的“主表”是一个好主意呢?这样我每当需要新的想法时,就可以在其中插入一行。但我觉得这可能有点过度设计,而且我担心它会复杂化所有的插入请求。另外,我无法在不查看数据库的情况下确定对象类型。

  • 还有其他聪明的方法来解决我的问题吗?


  • 1
    我曾经遇到过向用户公开IDENTITY值的问题。在2000年,对于像我这样的软件工程师来说是一个重要的年份,我加入了一家公司,并被分配了员工编号2000。我一直与人事部门进行类似于Paddington Bear和地铁售票员之间的对话:“……我问,“你叫什么名字?”而不是“你想去哪里?”……” - onedaywhen
    11个回答

    56
    为什么不在所有表上使用标识符,但每次向用户呈现时,只需为类型添加一个单个字符?例如,O1234是一个订单,D123213是一个交付等等。这样,您就不必设计一些疯狂的方案...

    4
    我们以前常说“双喜”,我的版本几乎一模一样,只是我建议使用小写字母,以避免将“O”和“D”读成“0”,将“B”读成“8”等。在这种情况下,我用小写字母的成功率要高得多(除了“l”)。 - MarkusQ
    4
    我建议使用相同的解决方案,但为每个实体使用一个代码,如Josh所述,例如001-1584,其中顺序为001。 - Jhonny D. Cano -Leftware-
    1
    001- 请翻译以下与程序相关的内容。只返回翻译后的文本: - Brann

    13

    在用户界面上处理它--向ID号码添加前缀字母(或字母)在向用户报告时。因此,o472将是一个订单,b531将是一张账单,以此类推。人们在通过电话报告“数字”时,混合字母和数字更加自如,而且比纯数字更准确。


    这很有道理。你不会得到“我有一个关于对象#435的问题”。因此,“Order#435”被翻译为ID / PK“O435”,“Payment#435”被翻译为ID / PK“P435”。 - blispr

    12

    您可以使用自增列生成唯一的 ID。接着,创建一个计算列,将该列的值与反映实体类型的固定标识符相连,并形成一个新的列,例如 OR1542 和 DL1542 分别代表订单#1542 和交付#1542。您可以根据需要扩展前缀并安排格式,以帮助区分具有相同自增值的项目,例如 OR011542 和 DL021542,其中前缀为 OR01 和 DL02。


    3
    我会通过定义一个通用的根表来实现。暂且叫它Entity吧。Entity表应该至少有一个Identity列。您还可以包含其他在所有对象中都常见的字段,甚至是元数据,告诉您这行是一个订单,例如。
    每个实际的Order、Delivery......表都将有一个FK引用回Entity表。这将给您一个单一的唯一ID列。
    在我看来,使用种子是一个糟糕的主意,可能会导致问题。
    编辑
    你已经提到了一些问题。我也认为这很麻烦,需要跟踪和确保正确设置所有新实体。想象一下,两年后开发人员更新系统。
    在写完这篇答案后,我对您为什么要这样做进行了更深入的思考,并得出了Matt的结论。

    @josh:你能详细说明一下你心中的问题吗? - Brann
    这基本上是数据库中实现OID的含义。当地理中心数据库是您所需的全部时,它们很好用。如今,UUID或COMB GUID更好,它们可以在本地生成,并使用简单的键/值设备在地理中心服务器上进行比较。更好的是,您可以在每个国家中放置一个这样的服务器,并附加/后缀该键/值服务器的两个字符国家名称,例如550e8400-e29b-41d4-a716-446655440000fr。如果您想要更小,可以将其设置为base64 + fr。 - Dennis

    3

    微软的有意编程项目采用了GUID转换成单词的系统,可以从随机ID中生成可读的名称。


    4
    你有这方面的例子吗?听起来非常有趣。 - Mladen Mihajlovic

    3

    请确保剥离元音字母! - Brian Laframboise
    @Jonathan:我不明白这个怎么解决问题;你能详细说明一下吗? - Brann

    1

    我使用高低算法来实现这个功能。虽然我在网上找不到相关的描述,但我必须写一篇博客来介绍它。

    在我的数据库中,我有一个ID表和一个计数器字段。这是高位部分。在我的应用程序中,我有一个从0到99的计数器。这是低位部分。生成的键是100 * 高位 + 低位。

    要获取一个键,我执行以下操作:

    initially high = -1
    initially low = 0
    
    method GetNewKey()
    begin
      if high = -1 then
        high = GetNewHighFromDatabase
    
      newkey = 100 * high + low.
      Inc low
      If low = 100 then
        low = 0
        high = -1
    
      return newKey
    end
    

    实际代码更加复杂,包括锁等,但这是一般意思。

    有许多方法可以从数据库中获取高值,包括自动增量键、生成器等。最好的方法取决于您使用的数据库。

    该算法提供简单的键,同时避免了大部分数据库查找新键的负载。在测试中,我发现它与 GUID 的性能相似,比每次检索自动增量键的性能要好得多。


    1
    我们在一个项目中遇到了类似的问题。我们通过首先创建一个简单的表来解决它,该表只有一行:作为自动递增标识设置的 BIGINT。 然后,我们创建了一个存储过程,在事务内使用默认值插入新行到该表中。然后将 SCOPE_IDENTITY 存储到变量中,回滚事务,然后返回存储的 SCOPE_IDENTITY
    这样我们就能够在数据库中获得唯一的ID而不会填充表格。
    如果您想知道ID所指代的对象类型,我建议您取消事务回滚,并将对象类型与ID一起存储。这样,只需要一个 select(或 inner join)操作,就可以找到ID所指代的对象类型。

    0

    我曾经遇到过一个类似的项目情况。

    我的解决方案是:默认情况下,用户只能看到GUID的前7个字符。

    这足够随机,碰撞极不可能发生(268百万分之1),而且在口语和打字方面也很高效。

    当然,在内部,我使用整个GUID。


    0
    你可以创建一个主 UniqueObject 表,其中包含你的身份和子类型字段。子表(订单、用户等)将具有对 UniqueObject 的外键。INSTEAD OF INSERT 触发器应该尽量减少痛苦。

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