【问题标题】:AutoIncrement fields on databases without autoincrement field没有自动增量字段的数据库上的自动增量字段
【发布时间】:2010-10-06 07:15:54
【问题描述】:

在 MS Sql Server 中很容易创建自动增量字段。在我的系统中,我停止对主键使用自动增量字段,现在我使用 Guid。太棒了,这种变化给我带来了很多好处。但在另一个非主键字段中,我确实需要实现“软自动增量”。这是因为我的系统是独立于数据库的,所以我在 c# 中以编程方式创建了 autoinc 值。

我想了解没有自动增量的数据库上的自动增量字段的解决方案,您使用什么解决方案,为什么?有一些关于这个的 Sql Ansi 声明?并直接从我的 c# 生成,是更好的解决方案吗?

PS:我知道 select max(id)+1 from table 这不是真正的并发友好...

【问题讨论】:

  • 我建议将标题重写为更清晰的内容,例如“创建独立于数据库的自动增量字段”。我担心的是,当我搜索相同的主题时,我认为我不会找到您的标题。
  • 你真的需要一个不断递增的数字,还是只需要唯一性?
  • 是的,我需要“一个不断增加的数字”,对于另一种情况,我使用 guid、CRC 等......

标签: sql sql-server auto-increment ansi-sql


【解决方案1】:

大多数没有像 SQL Server 这样的自动增量字段的数据库(我特别想是 Oracle)都有序列,您可以在其中向序列询问下一个数字。无论有多少人同时请求号码,每个人都会得到一个唯一号码。

【讨论】:

    【解决方案2】:

    传统的解决方案是有一个看起来像这样的 id 表

    CREATE TABLE ids (
      tablename VARCHAR(32) NOT NULL PRIMARY KEY,
      nextid INTEGER
    )
    

    当您创建数据库时,每个表填充一行。

    然后您进行选择以获取要插入的表的下一个下一个 id,将其递增,然后使用新 id 更新表。显然,这里存在锁定问题,但对于插入率适中的数据库,它运行良好。而且它是完全便携的。

    【讨论】:

    • 这个解决方案受竞争条件的影响,每个主要的 RDBMS 都有一个事务安全的替代方案。
    【解决方案3】:

    生成唯一 id 值的机制不得受到事务隔离的约束。这是数据库为每个客户端生成不同值所必需的,这比 SELECT MAX(id)+1 FROM table 的技巧要好,如果两个客户端尝试同时分配新的 id 值,则会导致竞争条件。

    您无法使用标准 SQL 查询来模拟此操作(除非您使用表锁或 可序列化 事务)。它必须是一种内置于数据库引擎中的机制。

    在 SQL:2003 之前,ANSI SQL 没有描述为代理键生成唯一值的操作。在此之前,自增列没有标准,因此几乎每个品牌的 RDBMS 都提供了一些专有的解决方案。当然,它们变化很大,而且没有办法以简单、独立于数据库的方式使用它们。

    • MySQL 有AUTO_INCREMENT 列选项,或SERIAL 伪数据类型,相当于BIGINT UNSIGNED AUTO_INCREMENT
    • Microsoft SQL Server 具有IDENTITY 列选项和NEWSEQUENTIALID(),它介于自动增量和 GUID 之间;
    • Oracle 有一个SEQUENCE 对象;
    • PostgreSQL 有一个SEQUENCE 对象或SERIAL 伪数据类型,它根据命名约定隐式创建一个序列对象;
    • InterBase/Firebird 有一个GENERATOR 对象,这很像Oracle 中的SEQUENCE; Firebird 2.1 也支持SEQUENCE
    • SQLite 将声明为主键的任何整数都视为隐式自动递增;
    • DB2 UDB 几乎应有尽有:SEQUENCE 对象,或者您可以使用“GEN_ID”选项声明列。

    所有这些机制都在事务隔离之外运行,确保并发客户端获得唯一值。此外,在所有情况下,都有一种方法可以查询当前会话的最新生成值。必须有,因此您可以使用它在子表中插入行。

    【讨论】:

    • 在 Firebird 2.1 中:还有 SEQUENCE firebirdsql.org/rlsnotesh/…
    • @Hugues:感谢您的提示!我已将其添加到列表中。
    • SQL-Server 也有 NEWSEQUENTIALID(),它介于自动递增和 GUID 之间。
    • @ypercube,也谢谢你的提示,我已经添加了。
    【解决方案4】:

    我认为你的问题实际上是一个很好的问题。但是,尝试提出仅 SQL 的解决方案很容易迷失方向。实际上,您会希望通过使用自动增量类型的数据库实现来实现优化和事务安全。

    如果您需要抽象出自增运算符的实现,为什么不创建一个存储过程来返回您的自增值。大多数 SQL 方言以相对相同的方式访问存储过程,并且它应该更具可移植性。然后,您可以在创建存储过程时创建特定于数据库的自动增量逻辑 - 无需将许多语句更改为特定于供应商的语句。

    通过这种方式,您的插入可以像这样简单:

    INSERT INTO foo (id, name, rank, serial_number)
     VALUES (getNextFooId(), 'bar', 'fooRank', 123456);
    

    然后在初始化数据库时以数据库特定的方式定义getNextFooId()。

    【讨论】:

      【解决方案5】:

      如果您需要一个非主键自动增量字段,一个非常好的 MySQL 唯一解决方案是使用相对未知的 last_insert_id(expr) 函数来创建任意序列。

      如果 expr 作为参数给出 LAST_INSERT_ID() 的值 参数由函数返回 并被记住为下一个值 由 LAST_INSERT_ID() 返回。这 可以用来模拟序列...

      (来自http://dev.mysql.com/doc/refman/5.1/en/information-functions.html#function_last-insert-id

      这是一个示例,它演示了如何为每个帖子的 cmets 编号保留二级序列:

      CREATE TABLE  `post` (
        `id` INT(10) UNSIGNED NOT NULL,
        `title` VARCHAR(100) NOT NULL,
        `comment_sequence` INT(10) UNSIGNED NOT NULL DEFAULT '0',
        PRIMARY KEY (`id`)
      );
      
      CREATE TABLE  `comment` (
        `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
        `post_id`  INT(10) UNSIGNED NOT NULL,
        `sequence` INT(10) UNSIGNED NOT NULL,
        `content` TEXT NOT NULL,
        PRIMARY KEY (`id`)
      );
      
      INSERT INTO post(id, title) VALUES(1, 'first post');
      INSERT INTO post(id, title) VALUES(2, 'second post');
      
      UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=1;
      INSERT INTO `comment`(post_id, sequence, content) VALUES(1, Last_insert_id(), 'blah');
      
      UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=1;
      INSERT INTO `comment`(post_id, sequence, content) VALUES(1, Last_insert_id(), 'foo');
      
      UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=1;
      INSERT INTO `comment`(post_id, sequence, content) VALUES(1, Last_insert_id(), 'bar');
      
      UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=2;
      INSERT INTO `comment`(post_id, sequence, content) VALUES(2, Last_insert_id(), 'lorem');
      
      UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=2;
      INSERT INTO `comment`(post_id, sequence, content) VALUES(2, Last_insert_id(), 'ipsum');
      
      SELECT * FROM post;
      SELECT * FROM comment;
      

      【讨论】:

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