我是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 中时,我可以看到,前两个INSERT
(或整个事务)没有回滚。 users
表确实包含两个新记录。
我错过了什么? phpMyAdmin不支持交易?或者我不明白,交易实际上是如何工作的,这是很正常的,这两个INSERT
在出错时不会回滚?
答案 0 :(得分:9)
执行之前的MySQL DDL 中的一些语句(最值得注意的是cause an implicit commit)和cannot be rolled back - 因此这可以防止先前的DML更改被滚动回来了。
本节中列出的语句(以及它们的任何同义词)隐式结束当前会话中活动的任何事务,就像在执行语句之前已完成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语句无法回滚事务的完整效果。