【问题标题】:Most efficient way of getting the next unused id获取下一个未使用 id 的最有效方法
【发布时间】:2011-04-03 21:24:19
【问题描述】:

(与Finding the lowest unused unique id in a listGetting unused unique values on a SQL table有关)

假设我有一个包含 id 列和其他一些列的表(它们在这里没有任何区别):

+-----+-----+
| id  |other|
+-----+-----+

id 有数值递增的值。我的目标是获得最低的未使用 id 并创建该行。所以当然我第一次运行它会返回0,并且该行的行将被创建。执行几次后,它将如下所示:

+-----+-----+
| id  |other|
+-----+-----+
|  0  | ... |
|  1  | ... |
|  2  | ... |
|  3  | ... |
|  4  | ... |
+-----+-----+

其中一些行经常会被删除。假设删除了 ID 为 13 的行。没有表格会是这样的:

+-----+-----+
| id  |other|
+-----+-----+
|  0  | ... |
|  2  | ... |
|  4  | ... |
+-----+-----+

如果我现在再次运行查询,它想取回 id 1 并且应该创建此行:

| id  |other|
+-----+-----+
|  0  | ... |
|  1  | ... |
|  2  | ... |
|  4  | ... |
+-----+-----+

下次运行查询时,它应该返回 id 的 356 等。

运行这些查询的最有效方法是什么,因为我需要在一秒钟内经常执行它们(假设 id 是表的唯一目的是公平的)?是否可以通过一个查询获得下一个未使用的行?还是通过引入另一个跟踪未使用 id 的表来更轻松、更快捷?

如果它明显更快,也有可能找到一种方法来重用表中的任何孔,前提是所有数字都在某个时间被重用。

额外问题:我计划使用 SQLite 来存储这种信息,因为除了存储这些 id 之外我不需要数据库。是否有任何其他免费(如语音)服务器可以显着更快地完成这项工作?

【问题讨论】:

  • 为什么需要重复使用已删除的 ID?
  • 这些 id 将显示给用户,然后他们需要做一些使用长 id 会更难的事情(比如通过电话拼写它们)。由于它们的生存时间很短,因此最好重复使用它们以缩短数量。
  • 您是否需要担心并发性,或者是一个应用程序/线程在执行插入操作?
  • @Martin Smith:它是一个 Web 应用程序,可以由几个用户(因此不止一个线程)同时访问。
  • @Martin Smith:我希望数据库不会两次创建该行并返回错误,以便我可以重试。

标签: sql scalability execution-time


【解决方案1】:

我想我会在删除时创建一个触发器,并将 old.id 插入一个单独的表中。 然后您可以从该表中选择 min(id) 以获取最低 id。

免责声明:我不知道您使用什么数据库引擎,所以我不知道您是否可以使用触发器。

【讨论】:

  • 正如我在长文中的某处所写,我打算使用 SQLite,但如果它在其他服务器上使用起来更容易,我会使用它。
  • 我认为这是一个保持高效的好主意。请注意,良好的事务查询对于避免竞争条件是必要的。
  • 获得不必要功能的黑客攻击,当有外键时不能删除带有子引用的记录的事实,以及面对延迟约束时的引用完整性问题......对于一个没有实际价值的脆弱系统。
  • @OMG Ponies:这些 id 永远不会在另一个表中重复使用(因此外键问题根本不存在)。
【解决方案2】:

就像Dennis Haarbrink说的;删除触发器和插入触发器:

delete 的触发器会将删除的 id 插入到 id 池表中(只有一列 id

插入前的触发器将检查是否提供了 id 值,否则它只是查询 id 池表(例如:SELECT MIN(id) FROM id_pool_table)并分配它(ig 将其从 id_pool_table 中删除)

【讨论】:

    【解决方案3】:

    数据库不关心这些值是否是连续的,只关心它们是唯一的。让您的 id 值连续的愿望纯粹是为了装饰,如果您将此值公开给用户 - 它不应该是您的主键,也不应该有任何基于该值的引用完整性,因为客户端可以更改如果需要,可以格式化。

    处理 id 值生成的最快和最安全的方法是依赖为您提供唯一整数值​​的本机功能(IE:SQLite 的自动增量)。使用触发器只会增加开销,使用 MAX(id) +1 风险极大...

    总结

    理想情况下,使用本机唯一整数生成器(SQLite/MySQL auto_increment、Oracle/PostgreSQL 序列、SQL Server IDENTITY)作为主键。如果您想要一个始终连续的值,请添加一个额外的列来存储该连续值并根据需要对其进行维护。 MySQL/SQLite/SQL Server 唯一整数生成只允许每列一个 - 序列更灵活。

    【讨论】:

    • 根据文档sqlite.org/autoinc.html,“正常”算法只使用max(id) + 1,直到使用最大可能的数字,然后重复使用这些数字。 autoincrement 从不重复使用数字
    • @neo:在后台使用 MAX() + 1 的数据库很好 - 运行的用户风险很高。我解释了为什么对序列号的需求只是装饰性的,以及如何在不影响参照完整性的情况下正确处理它......
    • 没有(也不会有)为该表存储的外键。但是,如果数据库使用两种 id(一种是内部的,不重复使用;一种显示的,重复使用),那对我来说就很好了。
    【解决方案4】:

    通常您会让数据库处理分配 ID。是否有特殊原因需要 id 的顺序而不是唯一的?相反,您可以给它们加上时间戳,并在显示它们时给它们编号吗?还是为顺序 ID 单独列一列,然后重新编号?

    或者,您不能删除行本身,而是在列中使用标志将它们标记为已删除,然后通过查找编号最小的“已删除”行来重新使用标记行的 id,并重新使用那个id。

    【讨论】:

      猜你喜欢
      • 2011-02-10
      • 2022-12-20
      • 1970-01-01
      • 1970-01-01
      • 2014-03-03
      • 2013-11-25
      • 1970-01-01
      • 2015-10-12
      • 2014-12-08
      相关资源
      最近更新 更多