0

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

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

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

4

5 回答 5

7

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

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

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

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

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

于 2011-10-19T20:28:02.247 回答
2

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

于 2011-10-19T20:28:30.573 回答
1

这是为了找到第一个可用的插槽:

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。根据您的需要进行相应调整。

于 2011-10-19T20:34:08.933 回答
1

使用单独的表来跟踪正在使用的 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 中它可能是不同的语法。

于 2011-10-19T21:53:57.660 回答
0

上面的答案(由 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)中检索所有必要的选择数据,而不是表数据。

于 2018-06-05T13:12:00.323 回答