没有简单的方法可以绕过 MySQL 中的 AUTO_INCREMENT 属性默认行为,即使您找到了方法,我也不建议您这样做,因为这是在 MySQL 中遇到问题的最佳方法短期的。 AUTO_INCREMENT 值不应在生产环境中调整或重置。
解决您的问题的一种可能方法是稍微非规范化您的模型。这个想法是将AUTO_INCREMENT 字段移动到您不必复制或删除行的边表中。然后,您所要做的就是在创建新项目时从该边表中获取新的 id 值,并在将行从一个表复制到另一个表时保留现有的 id 值。
为了实现这一点,我们将使用一个触发器,该触发器将为我们创建一个新的 id 并将其分配给我们的项目记录。 item 表的 id 字段必须可以为空才能工作,因此我们必须用唯一索引替换主键。
此模型更改将对您的应用程序完全透明,因此您将无需对应用程序代码进行任何更改。
这里有一些示例脚本。假设我们的数据库中有两个项目表,其中有一些常见的行,还有一些需要从第一个表移动到第二个表的行:
创建表`item1`(
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`item_res_id` int(11) NOT NULL DEFAULT '0',
主键(`id`)
) 引擎=InnoDB 默认字符集=utf8;
创建表`item2`(
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`item_res_id` int(11) NOT NULL DEFAULT '0',
主键(`id`)
) 引擎=InnoDB 默认字符集=utf8;
插入项目 1 (item_res_id) 值 (1);
插入项目 1 (item_res_id) 值 (2);
插入项目 2 (item_res_id) 值 (1);
如果我们尝试将一些数据从一个表移动到另一个表,然后重新启动您的服务器,我们将遇到AUTO_INCREMENT 值重置的问题。所以我们将模型稍作修改如下:
我们将分几个步骤来迁移我们的数据模型。以下迁移脚本中的 DDL 语句是使用neXtep Designer IDE 生成的。
- 首先我们创建一个新的 item_keys 表来保存
AUTO_INCREMENT 字段:
-- 创建表 'item_keys'
创建表 item_keys (
id BIGINT(20) 无符号非空
,key_ctime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) 引擎=InnoDB 默认字符集=utf8;
-- 在表 'item_keys' 上创建主键约束 'PRIMARY'
ALTER TABLE item_keys 添加约束主键(id);
- 但在激活
AUTO_INCREMENT 属性之前,我们必须将现有ID 插入到我们的新表中:
-- 使用现有 id 初始化 item_keys
插入到 item_keys (id)
选择 i1.id
来自项目 1 i1
左连接 item_keys ik ON ik.id = i1.id
哪里 ik.id 为空
;
插入到 item_keys (id)
选择 i2.id
FROM item2 i2
左连接 item_keys ik ON ik.id = i2.id
哪里 ik.id 为空
;
- 我们现在可以激活
AUTO_INCREMENT 属性,并为以后的插入初始化它的值:
-- 激活 auto_increment 约束...
ALTER TABLE item_keys MODIFY id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT;
-- 初始化 auto_increment 值
SELECT @inc_value := MAX(id) FROM item_keys;
SET @alter_query = CONCAT('ALTER TABLE item_keys AUTO_INCREMENT=',@inc_value);
从@alter_query 准备alter_query;
执行更改查询;
DEALLOCATE PREPARE alter_query;
- 然后我们可以修改
item1和item2表,用唯一索引替换主键,并引用item_keys表的主键:
-- 取消激活 auto_increment 约束...
ALTER TABLE item1 MODIFY id BIGINT(20) UNSIGNED NOT NULL;
-- 删除约束 'PRIMARY'...
ALTER TABLE item1 DROP PRIMARY KEY;
ALTER TABLE item1 MODIFY id BIGINT(20) UNSIGNED NULL;
-- 创建索引 'item1_uk'...
在 item1 (id) 上创建唯一索引 item1_uk;
-- 在表 'item1' 上创建外键约束 'item1_keys_fk'
ALTER TABLE item1 添加
约束 item1_keys_fk 外键 item1_keys_fk
(id) 参考 item_keys
(ID)
;
-- 取消激活 auto_increment 约束...
ALTER TABLE item2 MODIFY id BIGINT(20) UNSIGNED NOT NULL;
-- 删除约束 'PRIMARY'...
ALTER TABLE item2 DROP PRIMARY KEY;
ALTER TABLE item2 MODIFY id BIGINT(20) UNSIGNED NULL;
-- 创建索引 'item2_uk'...
在 item2 (id) 上创建唯一索引 item2_uk;
-- 在表 'item2' 上创建外键约束 'item2_keys_fk'
ALTER TABLE item2 添加
约束 item2_keys_fk 外键 item2_keys_fk
(id) 参考 item_keys
(ID)
;
- 最后,我们只需创建将为我们管理 ID 创建的触发器:
-- 在表 'item1' 上创建触发器 'tr_item1_bi'...
分隔符 |;
在插入 item1 之前创建触发器 tr_item1_bi
每一行
开始
如果(NEW.id 为空)那么
-- 如果 INSERT 语句中没有指定项目 id,则
-- 表示我们要创建一个新项目。我们插入一条新记录
-- 进入 item_keys 表以获取项目 ID。
插入到 item_keys (
key_ctime
)
值(现在());
SET NEW.id = LAST_INSERT_ID();
万一;
结尾;
|;
-- 在表 'item2' 上创建触发器 'tr_item2_bi'...
分隔符 |;
在插入 item2 之前创建触发器 tr_item2_bi
每一行
开始
如果(NEW.id 为空)那么
-- 如果 INSERT 语句中没有指定项目 id,则
-- 表示我们要创建一个新项目。我们插入一条新记录
-- 进入 item_keys 表以获取项目 ID。
插入到 item_keys (
key_ctime
)
值(现在());
SET NEW.id = LAST_INSERT_ID();
万一;
结尾;
|;
现在我们可以将数据从一个表移动到另一个表,保持 id 不变,如果我们重新启动服务器,item_keys 中的 AUTO_INCREMENT 值将保持不变。
--------------
插入第 2 项
选择 i1.*
来自项目 1 i1
左连接 item2 i2
开 i2.id = i1.id
i2.id 为空
--------------
查询正常,1 行受影响(0.04 秒)
记录:1 重复:0 警告:0
--------------
从项目 1 中删除
--------------
查询正常,2 行受影响(0.00 秒)
--------------
插入项目 1 (item_res_id) 值 (3)
--------------
查询正常,1 行受影响(0.00 秒)
--------------
从项目 1 中选择 *
--------------
+--------+-------------+
|编号 | item_res_id |
+--------+-------------+
| 3 | 3 |
+--------+-------------+
一组中的 1 行(0.00 秒)
--------------
从项目 2 中选择 *
--------------
+--------+-------------+
|编号 | item_res_id |
+--------+-------------+
| 1 | 1 |
| 2 | 2 |
+--------+-------------+
2 行(0.00 秒)
--------------
选择 * 从 item_keys
--------------
+----+----------+
|编号 | key_ctime |
+----+----------+
| 1 | 2010-11-14 10:31:21 |
| 2 | 2010-11-14 10:31:21 |
| 3 | 2010-11-14 10:31:46 |
+----+----------+
3 行一组(0.00 秒)