Search code examples
mysqldatabasedatabase-schemainformation-schema

Finding out which tables are different in two versions of a database


I have 2 versions of a database (say db_dev and db_beta). I've made some changes in the db_dev database - added some tables, and changed a few columns in some existing tables. I need to find out the list of table names in which changes have been made.

I can easily find out the tables I've added by running the following query on the information_schema database:

SELECT table_name
FROM   tables
WHERE  table_schema = 'db_dev'
       AND table_name NOT IN (SELECT table_name
                              FROM   tables
                              WHERE  table_schema = 'db_beta');

How do I get the table_names whose column_names do not match in the two database versions?


Solution

  • Using information_schema, here is how it works.

    First, you know that the information_schema.COLUMNS table contains the columns definition. If one column has been changed, or a table does not exist, it will reflect in the information_schema.COLUMNS table.

    Difficult part is that you have to compare all columns of your COLUMNS table. So, you have to select TABLE_CATALOG,TABLE_NAME,COLUMN_NAME,ORDINAL_POSITION,COLUMN_DEFAULT, and so on (which is subject to evolution depending on your MySQL version). The column list is the result of the following query:

      SELECT GROUP_CONCAT(column_name)
      FROM information_schema.COLUMNS
      WHERE table_schema="information_schema"
           AND table_name="COLUMNS" AND column_name!='TABLE_SCHEMA';
    

    After that, we just have to SELECT TABLE_NAME, <column_list> and search for columns which appear once (column inexistent in other table), or where columns have two different definitions (columns altered). So we will have two different count in the resulting query to consider the two cases.

    We will so use a prepared statement to retrieve the list of column we want, and grouping the result.

    The resulting query does all the process for you:

     SELECT CONCAT(
          "SELECT DISTINCT TABLE_NAME
               FROM information_schema.COLUMNS
               WHERE TABLE_SCHEMA IN('db_dev', 'db_beta')
               GROUP BY table_name, COLUMN_NAME
               HAVING count(*)=1 OR 
               COUNT(DISTINCT CONCAT_WS(',', NULL, ",
                   GROUP_CONCAT(column_name)
               ,"))=2;")  
     FROM information_schema.COLUMNS
     WHERE table_schema="information_schema"
        AND table_name="COLUMNS" AND column_name!='TABLE_SCHEMA'
     INTO @sql;
    
     PREPARE stmt FROM @sql;
     EXECUTE @sql;