我可以将事务与 ALTER TABLE 一起使用吗?

Posted

技术标签:

【中文标题】我可以将事务与 ALTER TABLE 一起使用吗?【英文标题】:Can I use transactions with ALTER TABLE? 【发布时间】:2014-05-13 10:18:59 【问题描述】:

我是 SQL 事务的初学者(实际上是新手),所以我可能遗漏了一些明显的东西。

我有这个 SQL 代码,我正在尝试通过 phpMyAdmin 运行:

START TRANSACTION;

INSERT INTO `users` VALUES(NULL, 'User A', 'user.a@example.com', '4', 'User A');
INSERT INTO `users` VALUES(NULL, 'User B', 'user.b@example.com', '3', 'User B');

ALTER TABLE `users` CHANGE `level` `level` TINYINT(3) UNSIGNED NOT NULL;
ALTER TABLE `users` CHANGE `number` `number` INT(10) UNSIGNED NOT NULL;
ALTER TABLE `users` ADD COLUMN `number` INT(10) UNSIGNED NOT NULL AFTER `id`;

COMMIT;

第二个ALTER 导致#1054 - Unknown column 'number' in 'users' 错误。

但是,当它发生在 phpMyAdmin 中时,我可以看到,前两个 INSERTs(或整个事务)没有回滚。 users 表确实包含两条新记录。

我错过了什么? phpMyAdmin 不支持事务?或者我不明白,事务实际上是如何工作的,这很正常,这两个INSERTs 在出错的情况下不会回滚?

【问题讨论】:

FWIW 这在 Postgres 中运行良好,这也是一些人更喜欢 Postgres 而不是 mysql 的原因之一。 【参考方案1】:

MySQL cause an implicit commit 中的一些语句(最显着的 DDL)它们被执行和 cannot be rolled back - 因此这也防止了先前的 DML 更改被回滚。 p>

本节中列出的语句(以及它们的任何同义词)隐式结束当前会话中的任何活动事务,就像您在执行语句之前完成了 COMMIT 一样。从 MySQL 5.5.3 开始,大多数这些语句在执行后也会导致隐式提交;有关其他详细信息,请参阅本节末尾。

由于ALTER TABLE是受影响的语句之一,SQL批处理被有效地处理为:

START TRANSACTION;
INSERT INTO `users` VALUES(NULL, 'User A', 'user.a@example.com', '4', 'User A');

COMMIT; -- prevents ROLLBACK of insert(s), even if DDL fails
ALTER TABLE `users` CHANGE `level` `level` TINYINT(3) UNSIGNED NOT NULL;

建议的解决方案是keep DDL and DML separated。 documentation 说:

您应该设计您的 [DML] 事务以不包含此类 [DDL] 语句。如果您在一个无法回滚的事务中提前发出一条语句,然后另一条语句随后失败,则在这种情况下,无法通过发出 ROLLBACK 语句来回滚该事务的全部效果。

【讨论】:

为什么MySQL在执行语句之前决定COMMIT?它不应该失败,说明需要明确关闭事务。 隐式提交——而不是引发错误——可能源于历史设计/决策,并在古代丢失。虽然可以以任何一种方式提出论点,但重新创建做出各种实现选择的上下文通常并不容易或富有成效。

以上是关于我可以将事务与 ALTER TABLE 一起使用吗?的主要内容,如果未能解决你的问题,请参考以下文章

Alter Table Engine = InnoDB 可以同时在多个表上运行吗?

是否可以回滚主要 SQL 数据库中的 CREATE TABLE 和 ALTER TABLE 语句?

ALTER TABLE CREATE CONSTRAINT IF NOT EXIST 可能吗?

是否可以将 Spark 中的 data.table 与 Spark Dataframes 一起使用?

将 PreparedStatement 批处理与 Statement 批处理混合,可以吗?

MySQL的表现ALTER TABLE在列之后添加列 - 在一张大桌子上