【问题标题】:MySQL equivalent of Oracle's SEQUENCE.NEXTVALMySQL 相当于 Oracle 的 SEQUENCE.NEXTVAL
【发布时间】:2012-02-21 05:59:20
【问题描述】:

我需要能够生成运行查询,该查询将返回下表中的下一个 ID 值:

CREATE TABLE animals (
     id MEDIUMINT NOT NULL AUTO_INCREMENT,
     name CHAR(30) NOT NULL,
     PRIMARY KEY (id)
)

在 Oracle 中,您可以在一个序列上调用 NEXTVAL,它会为您提供下一个序列(注意:无需在表上执行插入操作)。

谷歌搜索后,我发现您可以使用以下查询找到 auto_increment 的当前值:

SELECT Auto_increment 
FROM information_schema.tables 
WHERE table_name='animals';

问题是我希望每次查询值时都增加值。在 Oracle 中,当您调用 nextval 时,即使您不向表中插入行,序列的值也会增加。

有什么方法可以修改上述查询,使返回的值始终与上次调用查询时不同?即 Auto_increment 每次检查时都会递增,并且在查询中使用时会使用新值。

我使用的是 Spring JDBCTemplate,所以如果它可以在一个查询中完成就更好了。

【问题讨论】:

    标签: mysql sql database oracle spring


    【解决方案1】:
    1.) create table query
    2.) Insert query
    3.) Update query
    4.) Select query for that table e.g. SELECT next_val FROM hib_sequence;
    5.) Before each select update first, like this you can achieve similar
    
    -- CREATE TABLE hib_sequence (next_val INT NOT NULL);
    -- UPDATE hib_sequence SET next_val=LAST_INSERT_ID(next_val+1);
    -- select last_insert_id();
    -- INSERT INTO hib_sequence VALUES (0);
    
    -- select next_val from hib_sequence;
    

    【讨论】:

      【解决方案2】:

      MySQL 使用 AUTO_INCREMENT 来达到目的。但有以下区别:

      MySQL AUTO_INCREMENT 到 Oracle SEQUENCE 的区别

      • AUTO_INCREMENT 仅限于每个表一列
      • AUTO_INCREMENT 必须分配给特定的 table.column(不允许多表使用)
      • AUTO_INCREMENT 被插入为未指定的列,或值为 NULL

      如果您想查看 MySQL 的 SEQUENCE 实现,可以使用 SP。

      参考下面的链接解释了你想要的一切。

      http://ronaldbradford.com/blog/sequences-in-mysql-2006-01-26/

      【讨论】:

        【解决方案3】:

        看看 MariaDBs SEQUENCE 引擎。有了它,您可以像生成序列一样简单

        SELECT seq FROM seq_1_to_5;
        +-----+
        | seq |
        +-----+
        |   1 |
        |   2 |
        |   3 |
        |   4 |
        |   5 |
        +-----+
        

        详情here: https://mariadb.com/kb/en/mariadb/sequence/

        有了这个,你应该可以做类似的事情

        SELECT min(seq) as nextVal FROM seq_1_to_500 
        WHERE seq NOT IN (SELECT ID FROM customers)
        

        【讨论】:

        • 有趣的事情,但很可能不是问题的答案。
        【解决方案4】:

        这个 InnoDB 示例演示了一种使用互锁查询实现您自己的计数器的方法:

        http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

        您需要为什么创建差距?保留身份证? 我宁愿不惜一切代价“修复”设计并更新其他模块,而不是触及序列。

        我不只是显式地增加序列,而是通过为每个 id 插入一个默认行(标记为无效)来分配和返回 id 来暗示它。这种方法是一致且可移植的。 稍后,您可以通过匹配的序列值更新这些默认行,而不是使用显式序列值强制插入。 这需要更多内存但不需要锁。过期行的垃圾收集可以在这里提供帮助。 “插入或更新”语句可以重新创建垃圾收集的行,但我不会这样做。

        【讨论】:

        • 不惜一切代价?真的吗?
        【解决方案5】:

        http://docs.oracle.com/cd/E17952_01/refman-5.5-en/example-auto-increment.html

        3.6.9。使用 AUTO_INCREMENT

        AUTO_INCREMENT 属性可用于为新行生成唯一标识:

        CREATE TABLE animals (
             id MEDIUMINT NOT NULL AUTO_INCREMENT,
             name CHAR(30) NOT NULL,
             PRIMARY KEY (id)
        );
        
        INSERT INTO animals (name) VALUES
            ('dog'),('cat'),('penguin'),
            ('lax'),('whale'),('ostrich');
        
        SELECT * FROM animals;
        Which returns:
        
        +----+---------+
        | id | name    |
        +----+---------+
        |  1 | dog     |
        |  2 | cat     |
        |  3 | penguin |
        |  4 | lax     |
        |  5 | whale   |
        |  6 | ostrich |
        +----+---------+
        

        没有为 AUTO_INCREMENT 列指定值,因此 MySQL 自动分配了序列号。您还可以将 NULL 或 0 显式分配给列以生成序列号。

        您可以使用 LAST_INSERT_ID() SQL 函数或 mysql_insert_id() C API 函数检索最新的 AUTO_INCREMENT 值。这些函数是特定于连接的,因此它们的返回值不受另一个也在执行插入的连接的影响。

        对 AUTO_INCREMENT 列使用最小的整数数据类型,该数据类型大到足以容纳您需要的最大序列值。当列达到数据类型的上限时,下一次生成序列号的尝试失败。如果可能,请使用 UNSIGNED 属性以允许更大的范围。例如,如果您使用 TINYINT,则允许的最大序列号为 127。对于 TINYINT UNSIGNED,最大值为 255。请参阅第 11.2.1 节,“整数类型(精确值) - INTEGER、INT、SMALLINT、TINYINT、MEDIUMINT、BIGINT ” 表示所有整数类型的范围。

        注意 对于多行插入,LAST_INSERT_ID() 和 mysql_insert_id() 实际上从插入的第一行返回 AUTO_INCREMENT 键。这使得多行插入能够在复制设置中的其他服务器上正确复制。

        如果 AUTO_INCREMENT 列是多个索引的一部分,MySQL 使用以 AUTO_INCREMENT 列开头的索引生成序列值(如果有的话)。例如,如果动物表包含索引 PRIMARY KEY (grp, id) 和 INDEX (id),MySQL 将忽略 PRIMARY KEY 以生成序列值。因此,该表将包含单个序列,而不是每个 grp 值的序列。

        要从 1 以外的 AUTO_INCREMENT 值开始,请使用 CREATE TABLE 或 ALTER TABLE 设置该值,如下所示:

        mysql> ALTER TABLE tbl AUTO_INCREMENT = 100; InnoDB 笔记

        对于 InnoDB 表,如果在 INSERT 语句序列的中间修改包含自动增量值的列,请小心。例如,如果您使用 UPDATE 语句将新的、更大的值放入自动增量列中,则后续 INSERT 可能会遇到“重复条目”错误。如果您执行 DELETE 后跟更多 INSERT 语句,或者当您提交事务但不是在 UPDATE 语句之后,则测试是否已经存在自动增量值。

        MyISAM 笔记

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

        CREATE TABLE animals (
            grp ENUM('fish','mammal','bird') NOT NULL,
            id MEDIUMINT NOT NULL AUTO_INCREMENT,
            name CHAR(30) NOT NULL,
            PRIMARY KEY (grp,id)
        ) ENGINE=MyISAM;
        
        INSERT INTO animals (grp,name) VALUES
            ('mammal','dog'),('mammal','cat'),
            ('bird','penguin'),('fish','lax'),('mammal','whale'),
            ('bird','ostrich');
        
        SELECT * FROM animals ORDER BY grp,id;
        Which returns:
        
        +--------+----+---------+
        | grp    | id | name    |
        +--------+----+---------+
        | fish   |  1 | lax     |
        | mammal |  1 | dog     |
        | mammal |  2 | cat     |
        | mammal |  3 | whale   |
        | bird   |  1 | penguin |
        | bird   |  2 | ostrich |
        +--------+----+---------+
        

        在这种情况下(当 AUTO_INCREMENT 列是多列索引的一部分时),如果您删除任何组中具有最大 AUTO_INCREMENT 值的行,则会重用 AUTO_INCREMENT 值。即使是 MyISAM 表也会发生这种情况,因为 AUTO_INCREMENT 值通常不会被重用。

        进一步阅读

        有关 AUTO_INCREMENT 的更多信息可在此处获得:

        如何将 AUTO_INCREMENT 属性分配给列:第 13.1.17 节,“CREATE TABLE 语法”和第 13.1.7 节,“ALTER TABLE 语法”。

        AUTO_INCREMENT 的行为取决于 NO_AUTO_VALUE_ON_ZERO SQL 模式:第 5.1.7 节,“服务器 SQL 模式”。

        如何使用 LAST_INSERT_ID() 函数查找包含最新 AUTO_INCREMENT 值的行:第 12.14 节,“信息函数”。

        设置要使用的 AUTO_INCREMENT 值:第 5.1.4 节,“服务器系统变量”。

        AUTO_INCREMENT 和复制:第 16.4.1.1 节,“复制和 AUTO_INCREMENT”。

        与可用于复制的 AUTO_INCREMENT(auto_increment_increment 和 auto_increment_offset)相关的服务器系统变量:第 5.1.4 节,“服务器系统变量”。

        http://search.oracle.com/search/search?q=auto_increment&group=Documentation&x=0&y=0

        【讨论】:

          【解决方案6】:

          您想要该表上的下一个值,以便您可以创建尚未插入的行,而不会干扰使用自动增量的其他进程?

          一些选项:

          继续插入行以“用完序列”,将它们标记为待处理,然后再更新它们。

          插入事务并中止事务 - 自动编号序列应该用完并在表中正常产生“间隙” - 现在您可以免费使用该编号。

          使用Oracle,序列完全独立于表,因此进程可以使用或不使用序列(并且它们也可以对不同的表使用相同的序列)。在这种情况下,您可以实现一个仅序列表,您可以通过某种函数访问该表,对于需要依赖自动增量的其他进程,删除自动增量并使用从相同分配的触发器如果没有提供 id,则序列函数。

          【讨论】:

            【解决方案7】:

            您可以添加自己的 MySQL 函数 - 如 http://www.microshell.com/database/mysql/emulating-nextval-function-to-get-sequence-in-mysql/ 所示 - 允许您执行以下操作:

            SELECT nextval('sq_my_sequence') as next_sequence;
            

            【讨论】:

            • 我在谷歌搜索时看到了这个例子。不幸的是,它需要在 ID 列上删除 AUTO_INCREMENT 的使用,目前这是不可能的,因为我需要更改使用此表的其他模块 - 谢谢。
            • 在该页面的 cmets 中,有人提出可能存在竞争条件的担忧。使用@Ivan 提供的 auto_increment 方法似乎更容易,其中工作在单个语句中完成,因此是原子的。您可以将 Ivan 的方法包装在一个函数中,这样会很好,因为它可以让您充分利用这两种方法。
            • 您能否更新您对实际内容而不是链接的答案?您单独提供的查询将不起作用。
            猜你喜欢
            • 1970-01-01
            • 2012-03-09
            • 2012-01-14
            • 1970-01-01
            • 2011-11-01
            • 2017-04-08
            • 2012-08-02
            • 2012-01-07
            相关资源
            最近更新 更多