【问题标题】:MySQL DDL Trigger, Diff table schema for column renameMySQL DDL 触发器,列重命名的 Diff 表模式
【发布时间】:2012-01-21 02:52:48
【问题描述】:

我正在创建一个 PHP 脚本来比较两个数据库的架构。

我已经设法检查有关删除/添加的表、列、索引、引用的架构更改,但是当涉及到重命名的列时,我有点卡住了。

在以下示例中,源数据库包含最新的架构,而目标数据库包含类似的架构但可能已过时。

先决条件:

  • 我不知道自上次差异以来发生的变化。
  • 数据库中的数据将不匹配,但架构应该在 diff 之后。

以目标数据库中的以下架构为例。

Field   Type    Null    Key Default Extra
field1  int(11) NO      NULL     
field2  int(11) NO      NULL     
field3  int(11) NO      NULL     

然后在源数据库中假设以下架构。

Field   Type    Null    Key Default Extra
field1  int(11) NO      NULL     
field4  int(11) NO      NULL     
field3  int(11) NO      NULL     

在不明确知道发生了什么的情况下,我无法确定field2 是否通过DROP, ADD AFTERCHANGE COLUMN 更改为field4。以下两个查询在表结构方面实现了相同的结果,但是使用前者会丢失数据。

(1)    ALTER TABLE `demo` DROP `field2`
       ALTER TABLE `demo` ADD `field4` INT( 11 ) NOT NULL AFTER `field1` 

(2)    ALTER TABLE `demo` CHANGE `field2` `field4` INT( 11 ) NOT NULL 

我显然可以删除旧列名并创建一个新列名,但这样会丢失原始列中的所有数据。我需要使用ALTER TABLE table CHANGE COLUMN field new_name structure; 查询而不是DROP column FROM table 后跟ALTER TABLE table ADD column definition;

我希望我可以使用 DDL 触发器来跟踪架构中的更改并将此类更改的记录插入到源数据库中的表中。稍后我可以查询此表以确定某个列是如何形成的。但是,据我所知,不可能在 MySQL 中的 DDL 查询上运行触发器,从而排除记录这些更改的可能性。我在 MySQL Forge(现在位于 MySQL 开发者专区)上阅读了这个工作日志 (WL#2418: DDL Triggers),但不幸的是,它似乎仍在等待实施。

有没有一种方法可以更新表以匹配关于重命名列的架构而不会丢失数据?

我看过MySQLDiff 之类的东西,但它需要内置到现有的代码中,所以我必须自己构建它。

我考虑过的想法

  • 为每一列添加一个注释,它是一个唯一的数字或字符串(为了参数,将其称为哈希)。查询 information_schema 表以检索此值并在每一列上进行比较。如果它是唯一的,那么它就是一个新列,或者如果它匹配一个哈希而不是一个名称或结构,那么它就被重命名/重新配置了。

  • 比较架构,如果有新列 - 检查它相对于相邻列的位置。如果新列的名称与缺少的列的名称相同,则比较该列的结构。如果匹配,请考虑将其重命名。如果没有,请考虑将其删除然后添加。

【问题讨论】:

  • 当您使用 PHP 脚本比较方案时,我不明白为什么您在谈论触发器,因此您现在在检测到差异时可以使用更改或添加列执行以下解决方案,复制数据等
  • 我不知道该列是否被重命名或删除,并在其位置添加了另一个。如果前者是真的,那么我可以使用 Bill Karwin 的第一个解决方案,如果后者是真的,那么我可以使用他的第二个解决方案。问题是我不知道架构差异的原因。这意味着我不能在删除旧列之前随意将数据复制到新列中,因为它们可能不相关。我更新了我的问题,试图更清楚地解释我的问题。
  • 我猜你控制了这两个数据库?让两个数据库具有相同数据的目的是什么。是测试环境还是复制?
  • 用于将测试环境复制到多个实时数据库。我控制着这两个数据库,但主数据库上有多个开发人员,并且目前大约有 10-15 个不同的目标数据库,它们都具有不同的架构。
  • 好的,我曾经使用过一个设置,其中我们有一个来自实时服务器的主要测试环境,它使用 mysql 复制,然后是一个 PHP 设置,以轻松删除开发人员的数据库并从主要测试环境中复制它。但我不知道您的数据库有多大,是否有可能

标签: php mysql triggers diff alter-table


【解决方案1】:

没有办法检测到这一点,但如果你的本地机器需要它,那么有办法做到这一点。

  1. 启用Mysql general loggingtable
  2. 现在所有查询都将保存在mysql.general_log 表中。
  3. 您现在可以运行SELECT * FROM mysql.general_log WHERE argument LIKE 'ALTER%' 之类的查询,为您提供与数据库修改相关的所有 SQL 语句,包括列重命名。

希望对您有所帮助..

【讨论】:

    【解决方案2】:

    MySQL 不支持 DDL 触发器。我可以在他们的工作日志中看到他们已经为服务器 7.1 计划了它。

    任务 ID 是 WL#2418。

    【讨论】:

    • 感谢 Henrique,不幸的是,我已经发现了这一点并在我的问题中指出了这一点。我正在寻找替代解决方案:(
    • 是的,我现在看到了,对此感到抱歉。在我工作的地方,我们所做的是将 DDL 脚本包含在应用程序内的 SQL 文件中对某个应用程序版本所做的更改。例如,MyApplication-DDL-2.1.1.sql。然后在这个Application的一个配置文件中,我们设置这个版本的Application需要的Database版本是2.1.1。然后,当我们将应用程序部署到生产环境时,由我们的一位开发人员构建的 Java 程序确保通过运行所需的 SQL 文件来更新数据库版本。
    【解决方案3】:

    我认为您不应该以这种方式同步架构。每次更改任何内容时,您都应该检查迁移脚本到源代码,然后在数据库中创建一个表来记录应用了哪个迁移。这样,您可以确保所有迁移都应用于所有数据库。例如`

    CREATE TABLE `ddl_version` (
    version` varchar(32)  NOT NULL,
    `applied_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY `version` (`version`)
    
    )`  
    

    【讨论】:

      【解决方案4】:

      这里最大的问题是,如果为同一个表更改了超过 1 个列名(相同类型),则无法判断哪些列更改为哪个列。 (仍然可以选择实际比较数据并希望两个表中的某些数据相同,这样就可以推断出哪一列发生了什么变化,但这完全不可靠)

      除了更改 1 个(或更多)字段并添加 1 个(或更多)字段的问题之外,您将再次遇到与上述相同的问题。

      上面提到的 MySQLDiff 能够告诉你哪些字段是新的,哪些是旧的(更像是新表等),但它不能告诉你哪个列名被更改为什么而它没有t 甚至尝试。 (没有软件可以可靠地做到这一点,特别是如果您的数据在数据库之间不一样) 您可以在以下位置看到它的作用:mysql-1.5.0/library/database.lib.php 和 mysql-1.5.0/library/generator.lib.php

      我强烈推荐的一件事是在您的开发团队中引入一个非常简单的流程。 - 每次对数据库结构进行更改(或系统工作所需的任何记录)都应记录到例如。受版本控制的 db_changes.sql 文件。对于每个新修订,如果需要对 db 进行此类更改,则擦除 db_changes.sql 文件,记录更改并保存为新修订。这样,对数据库结构的更改就可以正确修改,并且应用它们只是区分 2 个修改点的问题。 (MysqlWorkbench 是客户端之一,如果您更改表,它会向您显示它将在内部运行的查询,如果您的开发人员需要使用客户端)

      【讨论】:

        【解决方案5】:
        ALTER TABLE table
         CHANGE COLUMN old_col_name
         new_col_name column_definition;
        

        这会更改列名而不会丢失列中的数据(除非您也更改了数据类型并且新数据类型小于旧数据类型,例如将 BIGINT 更改为 SMALLINT)。

        不幸的是,此命令要求您重复列的当前定义,例如数据类型、NOT NULL 和其他选项。 MySQL 中没有简单地重命名列的命令。

        CHANGE COLUMN 子句是标准 SQL 的 MySQL 扩展,它不提供就地重命名列的语法。

        如果您想坚持标准 SQL,另一种解决方案是添加新列,将数据从旧列复制到新列,然后删除旧列:

        ALTER TABLE table ADD COLUMN new_col_name column_definition;
        
        UPDATE table SET new_col_name = old_col_name;
        
        ALTER TABLE table DROP COLUMN old_col_name;
        

        【讨论】:

        • 嗨,比尔,抱歉,也许我的问题不清楚。我不打算问重命名表的语法是什么,我的重命名示例比任何东西都更像伪代码。我已经修改了我的问题,希望能更清楚地解释我的情况。
        猜你喜欢
        • 2015-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-12
        • 1970-01-01
        • 2021-02-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多