MariaDB - INNODB 在创建增量记录时跳过数字序列 - 为什么?
MariaDB - INNODB skipping the number sequence while creating incremental records - why?
我不知道这是否是 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
结果(预期行为):
现在如果我使用完全相同的语句,但是,通知引擎是 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
结果(奇怪的结果-跳过数字序列):
事实上,两个引擎都创建了预期的 4096 条记录,但我对 INNO
的行为感到担心,因为我正在将我的数据库从 MYISAM
迁移到 INNODB
并且我不知道这会对我的申请产生多大影响。
需要 auto_increment 机制来生成 唯一的 值,该值大于它之前生成的任何值。它不保证生成 个连续 个值。
这里有一些讨论:https://bugs.mysql.com/bug.php?id=57643
忠实地生成连续值并不重要,因为由于其他原因任何值都可能 "lost":
- 您的 INSERT 失败,例如因为违反了 UNIQUE KEY 或 FOREIGN KEY 等约束。
- 您回滚了 INSERT 的事务。
- 您成功并提交,但稍后该行被您或其他会话删除。
Auto-inc 值不会返回到任何类型的队列,因为其他并发会话可能同时生成了更多的 id 值。 InnoDB 不值得维护一个未分配的 id 值池,因为该池可能变得巨大且浪费。
此外,"lose" ID 值可能是合适的,否则有人会认为他们打算删除的行不知何故又回来了。
To summarize the reason for this statement, it is a scheduling system
that I have that uses this statement to create the calendar table.
不完全在您关于缺少 ID 的问题的范围内。
但是有更好的方法来生成数字和/或日历 table 然后重复 INSERT ... SELECT
多次。
所有方法都可以直接与其他table JOINed 或用于填充(索引)(临时)table
用于号码生成。
如果你的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
对于不支持window功能的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
对于不支持window功能的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
我不知道这是否是 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
结果(预期行为):
现在如果我使用完全相同的语句,但是,通知引擎是 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
结果(奇怪的结果-跳过数字序列):
事实上,两个引擎都创建了预期的 4096 条记录,但我对 INNO
的行为感到担心,因为我正在将我的数据库从 MYISAM
迁移到 INNODB
并且我不知道这会对我的申请产生多大影响。
需要 auto_increment 机制来生成 唯一的 值,该值大于它之前生成的任何值。它不保证生成 个连续 个值。
这里有一些讨论:https://bugs.mysql.com/bug.php?id=57643
忠实地生成连续值并不重要,因为由于其他原因任何值都可能 "lost":
- 您的 INSERT 失败,例如因为违反了 UNIQUE KEY 或 FOREIGN KEY 等约束。
- 您回滚了 INSERT 的事务。
- 您成功并提交,但稍后该行被您或其他会话删除。
Auto-inc 值不会返回到任何类型的队列,因为其他并发会话可能同时生成了更多的 id 值。 InnoDB 不值得维护一个未分配的 id 值池,因为该池可能变得巨大且浪费。
此外,"lose" ID 值可能是合适的,否则有人会认为他们打算删除的行不知何故又回来了。
To summarize the reason for this statement, it is a scheduling system that I have that uses this statement to create the calendar table.
不完全在您关于缺少 ID 的问题的范围内。
但是有更好的方法来生成数字和/或日历 table 然后重复 INSERT ... SELECT
多次。
所有方法都可以直接与其他table JOINed 或用于填充(索引)(临时)table
用于号码生成。
如果你的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
对于不支持window功能的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
对于不支持window功能的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