【问题标题】:How to find next free unique 4-digit number如何找到下一个免费的唯一 4 位数号码
【发布时间】:2011-10-19 20:23:28
【问题描述】:

在我的数据库应用程序中,我需要为每个客户提供一个唯一的 4 位数字字段。在 9999 之前,我只能使用自动增量,但在那之后,我将不得不重用已删除的客户数量(在给定时间不会超过 5000 个客户,但在整个生命周期内可能会有超过 9999 个客户系统)。

问题一:有没有(My)SQL语句来查找下一个可重用的空闲号?

问题2:如果我得到号码,将它分配给一个新客户并将客户全部保存在一次交易中,同时发生的类似交易将由数据库进行排序,因此号码不会发生冲突,对吧?

【问题讨论】:

  • 这听起来像是一个非常脆弱的系统。为什么限制使用四位数的 ID?
  • (诚然我不知道具体情况,但我倾向于只使用 5 位数字 - 这样您就不必担心“当他说 3403 时,确实他是指拥有那个号码的当前客户,还是这个东西以某种方式从以前的客户身上挂了出来?”)
  • 这是一个可怕的想法。滚动你自己的 ID 系统永远不会很好地工作。这就是增加了 4 位数字的限制,这对我来说也意味着您不会将其存储为数值,这也是一个可怕的想法
  • 除了@JNK 所说的 - 重复使用旧客户号码听起来也是一个可怕的想法。如果老客户有投诉并提交了他们的旧(现在重复使用的)客户编号怎么办?
  • 当我从客户那里听到这个要求时,我的第一反应也是“Ewww”;-) 但在我的情况下,系统不会有很多客户,并且删除的客户 ID 来自创建的客户错误地通过自动化流程。我被限制为 4 位数字,因为 5 位唯一数字用于其他用途(双 eww,我知道)。

标签: mysql sql auto-increment unique-key


【解决方案1】:

您最好存储一个表,其中定义了所有 10,000 个可能的值,并在每个表上都有一个“正在使用”标志。这样一来,释放号码以供重复使用是设置“inuse=false”的简单更新。

还使查找最低可用值变得简单

SELECT idstring
FROM idstringtable
ORDER BY idstring ASC
WHERE (available = 1)
LIMIT 1

使用适当的锁/事务执行此操作将防止两个或多个请求获得相同的 ID,并且由于它是一个小表,执行全局表锁不会显着影响性能。

否则,您将被困在您的用户表周围,试图找到编号序列中的第一个“空白”。

【讨论】:

    【解决方案2】:

    如果您必须使用此模型(我建议不要使用此模型),那么我将创建一个“可用”号码池,并在创建帐户时从中获取 TOP 1。然后,当删除用户时,将号码返回到池中。

    【讨论】:

      【解决方案3】:

      这是寻找第一个可用的插槽:

      select i1.id + 1 as FirstAvailable
      from issues i1 left join issues i2 on (i1.id = i2.id - 1)
      where i2.id is null
      limit 1
      

      这是针对生产 Redmine 实例运行的,以查找第一个丢失的 id。根据您的需要进行调整。

      【讨论】:

      • @user384706 如果 Adrian 将连接写为 i2.id = i1.id + 1 可能会更清楚,这意味着它会查找比表中现有 id 大一个的 id 并返回第一个没有的 id没有找到。编写它的另一种方式是WHERE NOT EXISTS (SELECT id FROM issues i2 WHERE i2.id = i1.id + 1),尽管您必须对语句进行分析以查看哪个执行得更好。
      【解决方案4】:

      使用单独的表来跟踪正在使用的 ID 的建议将起作用,但如果您不想使用单独的表来跟踪使用的 ID,您可以进行自联接以查找 ID 编号中的间隙.自联接非常简单:

      select top 1 t1.id + 1
        from table t1
        left join table t2 on t1.id = t2.id - 1
       where t1.id < 10000
         and t2.id is null
      

      在 MS SQL Server 中,我使用 TOP 1 来获得最高结果,但在 MySQL 中可能是不同的语法。

      【讨论】:

        【解决方案5】:

        上述答案(由 Adrian Carneiro 提供)非常棒,除非表使用不同的字段作为主键并且没有“id”键,否则它可以正常工作。

        给定一个主键为 userid 的表:-

        MariaDB [unixua]> select userid, uid from accounts;
        +---------+----------+
        | userid  | uid      |
        +---------+----------+
        | acc0001 | 89814678 |
        | acc0002 | 38000474 |
        | acc0005 | 38000475 |
        | acc0017 | 38000478 |
        +---------+----------+
        4 rows in set (0.00 sec)
        

        我们预计最低的免费号码是 38000476。

        MariaDB [unixua]> SELECT t1.uid +1 FROM accounts t1 
        LEFT JOIN accounts t2 ON (t1.uid +1 = t2.uid) 
        WHERE t2.uid IS NULL AND t1.uid>38000474 LIMIT 1;
        +-----------+
        | t1.uid +1 |
        +-----------+
        |  89814679 |
        +-----------+
        1 row in set (0.00 sec)
        

        但是,因为 MySQL / MariaDB 是按主键顺序选择它们,所以这会失败,并给出“acc001”之后的下一个最高值。 通过向 uid 列添加键并仅对“uid”列执行 SELECT,MySQL/MariaDB 将使用索引来检索数据(而不是读取表)。由于索引是“有序的”,因此结果不同:-

        MariaDB [unixua]> alter table accounts add unique index (uid);
        Query OK, 0 rows affected (0.01 sec)
        Records: 0  Duplicates: 0  Warnings: 0
        
        MariaDB [unixua]> SELECT t1.uid +1 FROM accounts t1 
        LEFT JOIN accounts t2 ON (t1.uid +1 = t2.uid) 
        WHERE t2.uid IS NULL AND t1.uid>38000474 LIMIT 1;
        +-----------+
        | t1.uid +1 |
        +-----------+
        |  38000476 |
        +-----------+
        1 row in set (0.00 sec)
        

        确保您的表具有客户 ID 字段的键(并且该客户 ID 字段是数字)。

        这是可行的,因为优化器可以从索引(又名 accounts.myi,而不是 accounts.myd)中检索所有必要的选择数据,而不是表数据。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-01-15
          • 2022-11-28
          • 1970-01-01
          • 2017-10-14
          • 1970-01-01
          • 1970-01-01
          • 2017-12-03
          • 1970-01-01
          相关资源
          最近更新 更多