【问题标题】:Some sort of “different auto-increment indexes” per a primary key values每个主键值的某种“不同的自动增量索引”
【发布时间】:2011-11-24 02:15:30
【问题描述】:

我有一个表,它有一个 id(具有自动增量的主键)、uid(例如,引用用户 ID 的键)和其他一些对我来说问题无关紧要。

我想为每个 uid 条目在 id 上创建不同的自动增量键。

所以,我将添加一个 uid 10 的条目,并且该条目的 id 字段将具有 1,因为没有uid 中值为 10 的先前条目。我将添加一个 uid 4 的新条目,它的 id 将是 3 因为我已经输入了两个 uid 4。

...非常明显的解释,但我试图尽可能清晰地解释这个想法......清楚地展示这个想法。

  1. 什么 SQL 引擎可以原生提供这样的功能? (非基于 Microsoft/Oracle)
  2. 如果没有,我怎样才能最好地复制它?可能是触发器?
  3. 此功能是否有更合适的名称?
  4. 如果您知道提供这种功能的非 SQL 数据库引擎,请随便命名,我很好奇。

谢谢。

【问题讨论】:

  • 你真正想要达到什么目的?将这么多“逻辑”放入主键和外键听起来确实是个坏主意。
  • @a_horse_with_no_name 例如,我想存储用户所做的更改或任何操作,所有用户都带有一个自动增量字段,以按数字顺序排列他们所做的事情。为此,我需要每个用户使用不同的表,这显然是一个可怕的想法。
  • 听起来您应该将更改与时间戳一起存储,然后在检索信息时按该时间戳排序,以便按照完成的顺序获取它们。
  • 听起来可以管理,但是,使用小整数不是更有效吗?

标签: sql database primary-key auto-increment


【解决方案1】:

SQL Server 应该允许您这样做。如果你不能使用computed column 来实现它(可能不是——有一些限制),你当然可以在trigger 中实现它。

MySQL 也允许您通过触发器来实现这一点。

【讨论】:

    【解决方案2】:

    在评论中,您提出了有关效率的问题。除非您要处理大量数据,否则与使用 4 字节 INT 相比,存储 8 字节 DATETIME 的开销并不大。

    它还极大地简化了您的数据插入,并且能够处理被删除的记录,而不会在您的序列中创建“漏洞”。


    如果您确实需要,请注意字段名称。如果您在一个表中有uidid,我希望id 在该表中是唯一的,而uid 指的是别的东西。或许,改为使用字段名称 property_idamendment_id

    在实现方面,一般有两种选择。


    1)。一个触发器

    实现方式不同,但逻辑保持不变。由于您没有指定 RDBMS(非 MS/Oracle 除外),因此一般逻辑很简单...

    • 启动一个事务(通常这是在触发器中隐式启动的)
    • 为要插入的 property_id 找到 MAX(amendment_id)
    • MAX(amendment_id) + 1更新新插入的值
    • 提交事务

    需要注意的是...
    - 同时插入多条记录
    - 正在插入的记录,其中正已填充了 modify_id
    - 更新改变现有记录


    2)。一个存储过程

    如果您使用存储过程来控制对表的写入,您将获得更多的控制权。

    • 隐含地,您知道您只处理一条记录。
    • 您根本没有为 DEFAULT 字段提供参数。
    • 您知道哪些更新/删除可以发生,哪些不能发生。
    • 您可以实现所有您喜欢的业务逻辑,而无需隐藏触发器


    我个人推荐存储过程路线,但触发器确实有效。

    【讨论】:

      【解决方案3】:

      正确设置数据类型很重要。

      您所描述的是一个多部分密钥。所以使用多部分密钥。不要试图将所有内容都编码成一个神奇的整数,你会毒害你的其余代码。

      如果一条记录由(entity_id,version_number) 标识,则接受该描述并直接使用它,而不是破坏键的含义。您将不得不编写限制版本号的查询,但这没关系。数据库擅长这类事情。

      version_number 可以是时间戳,正如 a_horse_with_no_name 所暗示的那样。这是个好主意。使用时间戳而不是普通整数没有明显的性能劣势。你得到的是意义,这更重要。

      您可以维护一个“最新版本”表,其中对于每个entity_id,只包含最新version_number 的记录。这对你来说会更有用,所以只有当你真的需要性能时才这样做。

      【讨论】:

        【解决方案4】:

        MySQL 的 MyISAM 引擎可以做到这一点。请参阅他们的手册,在Using AUTO_INCREMENT 部分:

        对于 MyISAM 表,您可以在多列索引中的辅助列上指定 AUTO_INCREMENT。在这种情况下,AUTO_INCREMENT 列的生成值计算为 MAX(auto_increment_column) + 1 WHERE prefix=given-prefix。当您要将数据放入有序组时,这很有用。

        文档在该段落之后继续,显示了一个示例。

        MySQL 中的 InnoDB 引擎不支持此功能,这很遗憾,因为在几乎所有情况下都最好使用 InnoDB。

        如果不锁定 INSERT 表,则无法使用触发器(或任何仅限于事务范围的 SQL 语句)模拟此行为。考虑以下一系列操作:

        1. Mario 启动事务并为用户 4 插入新行。
        2. Bill 开始事务并为用户 4 插入新行。
        3. Mario 的会话触发一个触发器来计算用户 4 的 MAX(id)+1。你得到 3。
        4. Bill 的会话触发一个触发器来计算 MAX(id)。我得到 3 个。
        5. Bill 的会话完成了他的 INSERT 并提交。
        6. Mario 的会话试图完成他的 INSERT,但 (userid=4, id=3) 的行现在存在,因此 Mario 发生主键冲突。

        一般来说,如果没有某种同步,您将无法控制这些步骤的执行顺序。

        解决方案是:

        • 获取独占表锁。在尝试 INSERT 之前,锁定表。这对于防止并发 INSERT 像上面的示例一样创建 race condition 是必要的。有必要锁定整个表,因为您试图限制 INSERT 没有要锁定的特定行(如果您尝试使用 UPDATE 控制对给定行的访问,则可以只锁定特定行)。但是锁定表会导致对表的访问变为串行,这会限制您的吞吐量。

        • 在事务范围之外进行。以不会对两个并发事务隐藏的方式生成 ID 号。顺便说一句,这就是 AUTO_INCREMENT 所做的。两个并发会话将各自获得一个唯一的 id 值,无论它们的执行顺序或提交顺序如何。但是跟踪每个用户标识的最后生成的标识需要访问数据库或重复的数据存储。例如,每个用户 ID 有一个 memcached 键,可以是 incremented atomically

        确保插入获得唯一值相对容易。但是很难确保它们会获得连续序数值。还要考虑:

        • 如果您在事务中插入但随后回滚会发生什么情况?您在该事务中分配了 id 值 3,然后我分配了值 4,所以如果您回滚并提交,现在就会出现差距。
        • 如果由于表上的其他约束(例如,另一列不是 NULL)而导致 INSERT 失败,会发生什么情况?您也可以通过这种方式获得差距。
        • 如果您曾经删除一行,是否需要为相同的用户 ID 重新编号以下所有行?如果您使用该解决方案,这对您的 memcached 条目有什么影响?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-06-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多