MariaDB-INNODB在创建增量记录时跳过数字顺序-为什么?

时间:2019-03-08 12:37:17

标签: mysql mariadb innodb myisam

我不知道INNODB是否会出现这种情况,但我真的认为这很奇怪。

如果我使用与MYISAM相同的SQL语句,则会发生预期的行为。

MYISAM

CREATE TABLE main_database.numero (
    id INT NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY(id)
) ENGINE = MYISAM DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
INSERT INTO main_database.numero VALUES(NULL); -- First, run once time ...
INSERT INTO main_database.numero SELECT NULL FROM main_database.numero; -- After, more 12 times = 4096 records

结果(预期行为):

enter image description here

现在,如果我使用完全相同的语句,则通知引擎为INNODB

INNODB

CREATE TABLE main_database.numero (
    id INT NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY(id)
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;

INSERT INTO main_database.numero VALUES(NULL); -- First, run once time ...
INSERT INTO main_database.numero SELECT NULL FROM main_database.numero; -- After, more 12 times = 4096 records

结果(奇怪的结果-跳过数字序列):

enter image description here

事实上,两个引擎都在创建预期的4096条记录,但是我担心INNO的行为,因为我正在将数据库从MYISAM迁移到INNODB,而我确实不知道会对我的应用程序产生多大的影响。

2 个答案:

答案 0 :(得分:2)

需要auto_increment机制来生成唯一值,该值大于其先前生成的任何值。它不保证生成连续值。

这里有一些讨论:https://bugs.mysql.com/bug.php?id=57643

如实生成连续值几乎没有什么意义,因为任何其他值可能会由于其他原因而“丢失”:

  • 您的INSERT失败,例如,因为违反了UNIQUE KEY或FOREIGN KEY之类的约束。
  • 您为INSERT回滚事务。
  • 您成功并提交,但是稍后该行被您或其他会话删除。

自动增量值不会返回到任何类型的队列,因为与此同时其他并发会话可能会生成更多的id值。 InnoDB维护未分配的id值的池是不值得的,因为该池可能变得巨大且浪费。

此外,“丢失”一个ID值可能是适当的,否则有人会认为他们原本打算删除的行又回来了。

答案 1 :(得分:2)

  

总结此声明的原因,它是一个调度系统   我使用该语句创建日历表。

这完全不在您的问题范围之内,该问题与缺少ID有关。
但是,还有更好的方法来生成数字和/或日历表,然后多次重复INSERT ... SELECT
所有方法都可以直接与其他表结合使用,也可以用于填充(索引)(临时)表

用于生成号码。

如果您的MariaDB / MySQL版本支持Windows功能

SET SESSION cte_max_recursion_depth = 5000;

WITH RECURSIVE number_generator(number) AS (
  SELECT 0
  UNION ALL
  SELECT number + 1 FROM number_generator
  WHERE number BETWEEN 0 AND 4096
)
SELECT * FROM number_generator

对于不支持窗口功能的MariaDB / MySQL。

SELECT 
  number_generator.number
FROM (

SELECT 
 @row := @row + 1 AS number
FROM (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row1
CROSS JOIN (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row2
CROSS JOIN (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row3
CROSS JOIN (
   SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row4
CROSS JOIN (
  SELECT @row := -1 
) init_user_params 
) AS number_generator
WHERE
 number_generator.number BETWEEN 0 AND 4096
ORDER BY 
 number_generator.number ASC

用于生成日历

如果您的MariaDB / MySQL版本支持Windows功能

SET SESSION cte_max_recursion_depth = 5000;

WITH RECURSIVE number_generator(number) AS (
  SELECT 0
  UNION ALL
  SELECT number + 1 FROM number_generator
  WHERE number BETWEEN 0 AND 4096
)
SELECT CURRENT_DATE + INTERVAL number_generator.number DAY FROM number_generator

对于不支持窗口功能的MariaDB / MySQL。

SELECT 
  CURRENT_DATE + INTERVAL number_generator.number DAY
FROM (

SELECT 
 @row := @row + 1 AS number
FROM (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row1
CROSS JOIN (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row2
CROSS JOIN (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row3
CROSS JOIN (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row4
CROSS JOIN (
  SELECT @row := -1 
) init_user_params 
) AS number_generator
WHERE
 number_generator.number BETWEEN 0 AND 4096
ORDER BY 
 number_generator.number ASC

CURRENT_DATE只是一个示例,您也可以使用过去或将来的固定日期作为示例,例如,您可以使用'2019-03-01'

此外,+ INTERVAL number_generator.number DAY还可以使用负数来生成该日期和其他值的过去列表,然后是DAY,如果您想使用月份,可以使用MONTH,想要年份使用YEAR