20

I am creating a PHP script to compare schema of two databases.

I have managed to check for schema changes with regard to dropped/added tables, columns, indexes, references but when it comes to renamed columns I'm a bit stuck.

In the following example, the source database contains the most up to date schema and the destination database contains similar schema but is likely out of date.

Prerequisites:

  • I am not aware of changes which have occurred since the last diff.
  • The data in the databases will not match, but the schema should after the diff.

Take for example the following schema in the destination database.

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

And then assume the following schema in the source database.

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

Without knowing explicitly what occurred, I am unable to determine whether or not field2 changed to field4 by way of DROP, ADD AFTER or CHANGE COLUMN. The following two queries achieve the same result in terms of table structure, but the data is lost using the former.

(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 

I can obviously drop the old column name and create a new one but that then loses any data in the original column. I need to use an ALTER TABLE table CHANGE COLUMN field new_name structure; query rather than DROP column FROM table followed by ALTER TABLE table ADD column definition;

I was hoping that I could use a DDL trigger to track changes in schema and insert a record of such changes into a table in the source database. I could later query this table to determine how a certain column came to be. However, as far as I can tell it's not possible to run triggers on DDL queries in MySQL which rules out logging these changes. I had a read of this worklog (WL#2418: DDL Triggers) on MySQL Forge (now residing in MySQL Developer Zone) but it appears to be pending implementation still unfortunately.

Is there a way in which I can update tables to match a schema with regard to renamed columns without data loss?

I have looked at things like MySQLDiff but it needs to be built into an existing bit of code so I'm having to build it myself.

Ideas I've Considered

  • Add a comment to each column which is a unique number or string (call it a hash for the sake of argument). Query the information_schema table to retrieve this value and compare it on each column. If it's unique then it's a new column, or if it matches a hash but not a name or structure then it's been renamed/reconfigured.

  • Compare the schema, if there's a new column- check it's position with regard to adjacent columns. If the name of the new column is in the same position as one which is missing compare the structure of that column. If it matches, consider it renamed. If not, consider it deleted then added.

w5m
  • 2,286
  • 3
  • 34
  • 46
Ben Swinburne
  • 25,669
  • 10
  • 69
  • 108
  • i do not see why you are talking about triggers when you use a PHP script to compare the schemes so you now when a difference is detected and can execute the solution below with an alter or add column, copy the data etc – Iggy Van Der Wielen Jan 04 '12 at 16:01
  • I don't know if the column was renamed or deleted and another added in it's place. If the former is true then I can use Bill Karwin's first solution, and if the latter is true then I can use his second solution. The problem is such that I don't know the reason for the difference in schema. This means that I can't just copy data arbitrarily into new columns before deleting old ones as they may be unrelated. I have updated my question in an attempt to more clearly explain my problem. – Ben Swinburne Jan 04 '12 at 16:57
  • I guess you are in control of both databases? what is the purpose of having both databases with equal data. Is it a test environment or replication? – Iggy Van Der Wielen Jan 05 '12 at 08:49
  • It's for replicating test environments to multiple live databases. I am in control of both databases but there are multiple developers on the primary and there are about 10-15 different destination databases all currently with different schemas. – Ben Swinburne Jan 05 '12 at 09:06
  • ok, I used a setup once where we had one main test environment from the live server which uses mysql replication and then a PHP setup to easy drop a developer's database and copy it from the main test environment. But I do not know how large your databases are and if this is a possibility – Iggy Van Der Wielen Jan 05 '12 at 09:41
  • You normally should not solve these problems inside your database, because the database is the subject to change here. Solving this inside the DB will add complexity, while solving this outside of the DB will keep things separated. Plus, existing solutions for database schema revisions already exists, you're not the first one with those needs ;) - But I don't write this to stop you from playing around with some feature the DB might have. – hakre Jan 08 '12 at 13:52

5 Answers5

1

There is no way to detect this but if you need this for your local machine then there is a way to do this.

  1. Enable Mysql general logging to table
  2. Now all queries will be saved in mysql.general_log table.
  3. You can now run a query like SELECT * FROM mysql.general_log WHERE argument LIKE 'ALTER%' to give you all SQL statements that relate to database modification including column renames.

Hope that helps..

Community
  • 1
  • 1
supersan
  • 5,671
  • 3
  • 45
  • 64
0

MySQL doesn't supports DDL Triggers. I can see in their Work Log that they've planned it for server 7.1.

The Task ID is WL#2418.

Henrique Ordine
  • 3,337
  • 4
  • 44
  • 70
  • Thanks Henrique, I'd already spotted that and noted it in my question unfortunately. I was looking for alternate solutions :( – Ben Swinburne Aug 10 '12 at 14:11
  • Yes, I see that now, sorry about that. What we do where I work, is to include the DDL script, of the changes made for a certain version of an application in an SQL file inside the application. So, for instance, MyApplication-DDL-2.1.1.sql. Then in the a configuration file of this Application, we set that the version of the Database needed by this version of the Application is 2.1.1. Then, when we deploy the application to production, a Java program, built by one of our Developers, makes sure that the Database version is updated by running the required SQL Files. – Henrique Ordine Aug 11 '12 at 09:45
0
ALTER TABLE table
 CHANGE COLUMN old_col_name
 new_col_name column_definition;

This changes the column name without losing the data in the column (unless you change the data type as well and the new data type is smaller than the old data type, e.g. changing BIGINT to SMALLINT).

This command unfortunately requires that you repeat the current definition of the column, e.g. data type, NOT NULL, and other options. There is no command in MySQL to simply rename a column.

The CHANGE COLUMN clause is a MySQL extension to standard SQL, which provides no syntax to rename a column in place.

Another solution if you want to stick to standard SQL is to add a new column, copy the data from the old column to the new column, and then drop the old column:

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;
Bill Karwin
  • 538,548
  • 86
  • 673
  • 828
  • Hi Bill, apologies, perhaps my question was unclear. I wasn't intending to ask what the syntax is for renaming a table, my rename example was more psuedocode than anything. I have amended my question which will hopefully explain my situation more clearly. – Ben Swinburne Jan 04 '12 at 16:55
0

The biggest issue here is that if more than 1 column name (of the same type) was changed for the same table there is NO way to tell which of the columns was changed to which. (There could still be the option of actually comparing the data and hope that some data is the same in both tables, that way be able to deduce which column what changed to what, but it would be completely un-reliable)

Than there's also the issue of 1 (or more) field changed and 1 (or more) added, you would be running on the same exact issue as above again.

MySQLDiff mentioned above would be able to tell you which fields are new and which are old (and more like new tables etc), but it won't be able to tell you which column name got changed to what and it doesn't even try to. (No software can reliably do that, especially if your data is not the same between db's) You can see what it does in: mysql-1.5.0/library/database.lib.php and mysql-1.5.0/library/generator.lib.php

One thing I would highly recommend though, is introducing a very simple process in your development team. - Every time a change to db structure is made (or any records that are needed for the system to work) should be recorded into a eg. db_changes.sql file which is under version control. For every new revision, if such changes to the db are needed then the db_changes.sql file is wiped, changes get recorded and saved as a new revision. That way changes to the database structure are revised properly and applying them is just a matter of diff-ing 2 revision points. (MysqlWorkbench is one of the clients that if you change tables it will show you the query it will internally run, if your devs need to use a client at all)

haknick
  • 1,892
  • 1
  • 20
  • 28
0

I don't think you should be synchronizing the schema this way. You should be checking migrations scripts to source code every time you change anything, then have a table in your database that logs which migration was applied. This way you can make sure that all the migrations were applied on all databases. for example `

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`)

)`  
Assaf Karmon
  • 915
  • 1
  • 10
  • 23