Cap'n Steve has asked for the wisdom of the Perl Monks concerning the following question:

I recently took over a project from someone, released my own version and then later released an updated version. The problem is that the table structure it uses is different in each version and it's rapidly becoming a bother to take all the different possibilities into account during the install routine.

I figured someone else must have had this problem before and it should be relatively easy to make the two tables structurally identical. I have yet to find anything, even though at this point I don't care what language it's written in. I have the basic idea of how I'd write my own, but I'd definitely prefer to use an existing solution.
  • Comment on How to synchronize two database tables in MySQL?

Replies are listed 'Best First'.
Re: How to synchronize two database tables in MySQL?
by dragonchild (Archbishop) on Jan 08, 2006 at 02:29 UTC
    You don't want to synchronize them blindly. If all you ever do is add nullable columns, then you're find. However, if you remove a column or change a column to a different type or add restrictions (NOT NULL, foreigh key, etc), then you have a hard problem.

    The best solution is twofold:

    1. For each update, write an upgrade script for the DB to go from the most recent schema to this one.
    2. Require your users to upgrade to each version. They cannot skip versions.

    You can automate step 2 any number of ways. It will take them a little longer to upgrade, but with a better chance of a successful upgrade.


    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
      I suppose an incremental upgrade would be the safest solution, although that seems less elegant. As I said, in the brief time I've worked on this problem it seems that adding a default value would solve most of the issues. I did however, run into some problems with keys, especially the fact that all auto_increment columns must be primary keys so you can't simply alter each column, then drop and re-add the indexes as was the original plan.
Re: How to synchronize two database tables in MySQL?
by McDarren (Abbot) on Jan 08, 2006 at 00:51 UTC
    This seems to be a MySQL question rather than a Perl one.

    Having said that, I'll still offer a few comments (which may or may not be helpful)

    The information you've provided is a little sketchy, although I believe I have a high-level understanding of what your problem is.

    I've faced similar challenges in the past, and the approach I've taken is basically as follows:

    1. Take a dump (mysqldump) of any existing data
    2. Install the new tables
    3. Massage the old data so that it fits into the new table structure
    4. Pump the old data into the new tables.

    Of course, all this is done in a test environment before it goes anywhere near a production system. And if step 3 requires anything more than some basic manipulation then I'll usually turn to Perl.

    Hope this helps,
    Darren :)

      I'm not really concerned about data right now, just structure. I'll be setting default values for any new columns I might add, but I need everyone to have the same table definitions. Here's how this is currently done:
      1. Examine table structure to determine which version they have
      2. Run different queries depending on the version

      What I want to do is store the table format within the install script, which would then examine the database and run the appropriate query, whether it's a DROP or ALTER or CREATE query. In the course of thinking about this, my theoretical structure looks something like this:
      my %tables = ('table1' => ( 'column1' => 'column definition', 'column2' => 'column definition') );
      with a separate hash for keys and storage engine types.
        When you do #2, what are the various queries? i.e. are you making a set of queries to bring verion X up to current for all previous versions? If so, would be a lot easier (for this and future releases) if the set queries were to bring version X up to version Y (version X+1), and just apply step #2 iteratively until you're at the current state.

        You could obsolete #1 by starting to store the schema version number in the schema somewhere .. if you already have a config file/table could just put it in there .. or maybe a new table version_history with columns version_number, version_name, version_description, release_date, install_date ...
Re: How to synchronize two database tables in MySQL?
by bsb (Priest) on Jan 08, 2006 at 11:02 UTC
    Alzabo has schema diffing and altering capabilities. You may be able to automatically import you schemas and then generate the ALTERs required.

    It's been a long time since I used Alzabo, so I'm not sure of the current status or whether this plan is feasible. Perhaps someone else can provide more up to date information.

    Usually I just diff the schema dumps and migrate by hand. But as I have only one deployment it's easy.

    mysqldump dev -d --add-drop-table=false > dev.sql mysqldump live -d --add-drop-table=false > live.sql perl -pi -e 's/^--.*\n//;s{/\*.*\*/;\n?}{}' dev.sql live.sql diff -B -U 5 dev.sql live.sql > schema.diff

    Brad

Re: How to synchronize two database tables in MySQL?
by astroboy (Chaplain) on Jan 08, 2006 at 08:51 UTC

    I had the same problem, and decided that the simplest solution was to purchase a 3rd party tool. I bought SQLBalance. It was really buggy, my questions went unanswered, and in the end, I forked out yet more money for Sync Database. It was much more expensive, but at least it worked

    So why spend money? Well, I couldn't find any OS solutions, and I needed something at short notice. Generally you can compare the db definitions and generate delta SQL yourself, but there's also possibilities that there may be bugs in your code, and I needed something quick - and which worked. I lucked out with SQLBalance, but at least I discovered the problems in my test run. Sync Database has worked for me, but I personally I think that it's way overpriced.

Re: How to synchronize two database tables in MySQL?
by jhourcle (Prior) on Jan 08, 2006 at 12:33 UTC

    It's a mySQL problem, but there's a Perl solution: SQL::Translator, aka SQLFairy.

    So yes, other people have had the problem before you. I've never used it for what you're describing, as I only came across it last week, but it has a 'sqldiff', which will attempt to take two schemas, and give you the changes necessary to turn the first into the second. (see the documentation, as there are some special notes about the diff it generates)

Re: How to synchronize two database tables in MySQL?
by kulls (Hermit) on Jan 08, 2006 at 14:13 UTC
    i suggest you to go ahead with database replication and redirect to database forums. so that you can get the better solution of the problem and come back here, if you want to implement that solution through perl if needed.
    - kulls
Re: How to synchronize two database tables in MySQL?
by TedPride (Priest) on Jan 08, 2006 at 22:18 UTC
    What you need is a routine that takes the set of incremental changes from version x to version y (a la Cap'n Steve's suggestion) and merges them into one set of changes. This is probably much easier than trying to interpret changes based on table definitions alone.
Re: How to synchronize two database tables in MySQL?
by Anonymous Monk on Jan 08, 2006 at 14:34 UTC
    You may want to go to sourceforge and look for the mysql_diff project there.
Re: How to synchronize two database tables in MySQL?
by Cap'n Steve (Friar) on Jan 09, 2006 at 03:13 UTC
    Thanks for the suggestions guys, there're are some interesting tools out there. The non-Perl ones seem a bit on the expensive side, though. Also, I'm not sure what database replication has to do with anything, am I missing something?
Re: How to synchronize two database tables in MySQL?
by DungeonKeeper (Novice) on Jan 09, 2006 at 14:47 UTC
    Perhaps devise a mapping file of the entire range of possibilities in the form:

    this_version_tablename1.col1|version-1_table_col|version-2...

    this_version_tablename1.col2|...

    ...

    this_version_tablename2.col1|...

    ...

    ...

    Leaving blank the places where the column did not exist in the relevant version. i.e. the columns delimited by '|' represent database versions and each such column could be in the form version.table.column

    Then given any source version, all the data can be migrated automatically to any target version according to the table with the help of a simple script that:

    - reads the mapping file into a AoHoH (array of version of hash of table of hash of column)

    - uses that to iterate reading in from any old version database and to lookup where in the new database to put it.

    Everything but the troll