从前,我有一张这样的桌子:
CREATE TABLE `Events` ( `EvtId` INT UNSIGNED NOT NULL AUTO_INCREMENT, `AlarmId` INT UNSIGNED, -- Other fields omitted for brevity PRIMARY KEY (`EvtId`) ); AlarmId被允许成为NULL。
现在,由于我希望将每个事件的零警报或零警报扩展为每个事件的零或零警报,因此在软件更新中,我将数据库实例更改为具有以下内容:
CREATE TABLE `Events` ( `EvtId` INT UNSIGNED NOT NULL AUTO_INCREMENT, -- Other fields omitted for brevity PRIMARY KEY (`EvtId`) ); CREATE TABLE `EventAlarms` ( `EvtId` INT UNSIGNED NOT NULL, `AlarmId` INT UNSIGNED NOT NULL, PRIMARY KEY (`EvtId`, `AlarmId`), CONSTRAINT `fk_evt` FOREIGN KEY (`EvtId`) REFERENCES `Events` (`EvtId`) ON DELETE CASCADE ON UPDATE CASCADE );
到目前为止,一切都很好。
数据也很容易迁移:
INSERT INTO `EventAlarms` SELECT `EvtId`, `AlarmId` FROM `Events` WHERE `AlarmId` IS NOT NULL; ALTER TABLE `Events` DROP COLUMN `AlarmId`;
事实是,我的系统要求降级也是可能的。我接受降级有时会对数据造成损失,这没关系。但是,它们确实需要在可能的情况下工作,并且会导致数据库结构较旧,同时尽最大努力保留合理数量的原始数据。
在这种情况下,这意味着从每个事件零个或多个警报变为每个事件零个或一个警报。我可以这样做:
ALTER TABLE `Events` ADD COLUMN `AlarmId` INT UNSIGNED; UPDATE `Events` LEFT JOIN `EventAlarms` USING(`EvtId`) SET `Events`.`AlarmId` = `EventAlarms`.`AlarmId`; DROP TABLE `EventAlarms`;
…很好,因为我并不在乎哪个人被保留(尽力而为,请记住)。但是,请注意,这不利于复制,因为结果可能无法预测:
> SHOW WARNINGS; Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statements writing to a table with an auto- increment column after selecting from another table are unsafe because the order in which rows are retrieved determines what (if any) rows will be written. This order cannot be predicted and may differ on master and the slave.
是否有办法以某种方式“命令”或“限制”更新中的联接,还是我应该跳过整个企业并停止尝试变得聪明?如果是后者,我怎么能离开降级AlarmId为NULL 当且仅当有新表之间,我们无法区分安全多行?我确实要迁移(AlarmId如果只有一个)。
由于降级是“一次性”维护操作,因此不必完全实时,但是速度会很好。两个表都可能具有数千行。
(在CentOS 7上,MariaDB 5.5.56必须在CentOS 6上运行。)