【问题标题】:Strategies for Handling non auto incrementing IDs处理非自动递增 ID 的策略
【发布时间】:2023-03-24 05:47:02
【问题描述】:

最近有人建议我删除一个仅用于存储 ID 的自动递增表。我还没有这样做,我只是在探索它是否真的是a better solution than what I currently have。这会给我留下这样一张桌子:

create table tag_translations (
  tag_id int not null,
  language_id int not null,
  tag_name varchar(255),
  primary key (tag_id, language_id)
);

我将复制 tag_id,存储其他语言的标签翻译。

添加新标签时,我需要放弃在 tag_id 上使用自动增量,而是手动分配新 ID。除非它只是对现有标签的翻译,否则新一批翻译插入的 ID 必须是唯一的。

谁能用简单的英语向我解释一下这通常是如何完成的?我想到了这一点,但如果我的想法正确的话,它似乎并不比我以前的方法更干净。以下是我假设的过程:

  • 从 tag_translations 中选择 tag_id
  • 选择结果集中最大的数字+1
  • 进行新查询(用于插入)
  • 定义一些额外的策略,以确保在几乎相同的微秒内创建标签时,新 tag_id 记录的 ID 不会重复

如果这是这个过程,我认为我最好还是坚持现有的架构,即拥有一个额外的表来自动递增 id。我仍然需要做一个额外的查询来首先检查一个唯一的 id(我今天正在用一个单一的连接来换取一个插入)。如果在我的 ID 需要唯一的时候保持唯一的麻烦是我认为的那样,我可能想放弃这种方法并坚持我所拥有的。我的想法合理吗?

【问题讨论】:

    标签: mysql increment


    【解决方案1】:

    对于生成新的标签 ID,有比select max(tag_id) + 1 更好的选择。您可以使用单个字段模拟 MySQL 中的序列/生成器,并使用 last_insert_id() 接受参数的能力。以下创建序列:

    create table tag_id_seq (id int not null default 0);
    insert into tag_id_seq values (0);
    

    创建序列后,您可以使用以下 2 条语句从中获取下一个 id:

    update tag_id_seq set id = last_insert_id(id + 1);
    select last_insert_id();
    

    last_insert_id() 是特定于连接的,因此第一条语句基本上仅用于捕获执行它的连接的值,并更新序列。第二条语句只是检索值。如果 2 个不同的连接执行的更新语句彼此非常接近,那么它们仍然会在 last_insert_id() 中隐藏不同的 id。

    您可以将其包装在一个函数中,您只需将序列名称传递给该函数,然后在您真正创建新标签时调用它。这将比您的具有单个 auto_increment 列的 tags 表更好。

    不用从0 开始,下一个ID 是1,即使使用当前使用的ID,您也可以提出来:

    update tag_id_seq set id = (select coalesc(max(tag_id),0) from tag_translations);
    

    update 语句也有一些变化:

    • 有些人喜欢让id 字段表示下一个ID 应该是什么,而不是最后一个给出的ID。在这种情况下,如果它是一个新序列,您可以将 id 开头为 1 而不是 0,并使用 set id = last_insert_id(id) + 1(在外部添加)。
    • 此外,在某些情况下需要“保留”多个新 ID,而不是一次保留 1 个。在这种情况下,您可以添加所需的数量。基于上述变化,假设序列位于11,这意味着检索到的最后一个id10,而11 是下一个id。如果你需要 7 个新的 id,你可以使用set id = last_insert_id(id) + 711select last_insert_id() 一起检索,这意味着您将使用 ids 1117(包括)。序列将更新为 18,即将检索的下一个 id。

    序列在许多情况下都有优势,以下是其中的几个:

    • 复合键不能包含auto_increment 列,但您仍然需要一种方法来生成 ID。
    • 这种在 MySQL 中执行序列的方式的重要之处在于字段。这意味着您几乎可以在任何地方拥有包含当前/下一个序列值的字段。

      例如,您可以创建一个序列表,其中 1 列用于序列名称,1 列用于当前/下一个值字段,而不是有几十个表,每个序列 1 个。一张表的几十行更整齐:

      create table sequences (
        seqname varchar(50) primary key,
        id int not null default 0);
      

      (如果表是 InnoDB,则使用行级锁定而不是表锁定。)

    • auto_increment in InnoDB 可能不会以您喜欢的方式运行。在启动时,它相当于select max(id)+1 来重置计数器。这可能会产生倒带和重新使用以前使用过的 id 的效果。

    【讨论】:

    • 嗨乔尔,非常感谢。但是,问题是,您不是在此处创建表(tag_id_seq)吗?
    • @Calvin:是的,在我的示例中创建了一个表,但它只有 1 列和 1 行。序列的重要部分不是将整个表用于它,而是更新单个字段。您可以将序列字段隐藏在其他地方,例如在这样的表中,每个序列有一行:create table (seq_name varchar(20) not null, id int not null)(InnoDB 表使用行级锁定)。我已经使用了需要的任何实现。
    • 这比拥有一个自动递增的额外表(也有 1 列和 1 行)更简单吗?这是提示这个问题的问题:stackoverflow.com/questions/6481505/…
    • 使用auto_increment,您必须执行insert 并且隐式设置last_insert_id()。现在你有超过 1 行,如果你想保持 1 行,必须做一个delete 来再次削减它。这对我来说似乎很奇怪和浪费。此外,如果服务器重新启动,InnoDB 表上的auto_increment 可以重置为max(id)。序列可能并不比auto_increment 表简单,但它们要灵活得多。
    • 啊,好的。被我耍到了。您可以将其添加到您的答案中吗?可能对其他人有帮助。
    猜你喜欢
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 2018-10-22
    • 2010-10-04
    • 2015-03-10
    • 1970-01-01
    • 2016-01-31
    • 1970-01-01
    相关资源
    最近更新 更多