Controlling Outcomes of Migration from MySQL to PostgreSQL

The first thing one should do is to look at the destination database to see which objects need to be validated after migrating a database from MySQL to PostgreSQL.

Table Definitions

Table definitions are exposed in MySQL like this:

  • If you’re using the MySQL client, you should run this SQL statement: DESC table_name
  • If you’re using phpMyAdmin go to the left pane, highlight the table, and from there select the ‘Structure’ tab

In PostgreSQL, you can run this statement to expose the definition: \d table_name

Once every column has equal sizes, default values and types in the PostgreSQL table that is generated from the MySQL conversion process, you know it’s been properly converted. You can see the values for appropriate conversions in this table for the various data types of MySQL:

Remember, while there are equivalencies in the data types of PostgreSQL and MySQL, some are exactly the same while others are a bit different. Check this table for types mapping before you begin migrating from MySQL to PostgreSQL.

MySQL PostgreSQL
BIGINT BIGINT
BINARY(n) BYTEA
BIT BOOLEAN
CHAR(n), CHARACTER(n) CHAR(n), CHARACTER(n)
DATE DATE
DATETIME TIMESTAMP [WITHOUT TIME ZONE]
DECIMAL(p,s), DEC(p,s) DECIMAL(p,s), DEC(p,s)
DOUBLE DOUBLE PRECISION
FLOAT REAL
INT, INTEGER INT, INTEGER
MEDIUMINT INTEGER
NUMERIC(p,s) NUMERIC(p,s)
SMALLINT SMALLINT
TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB TEXT
TINYINT SMALLINT
TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT TEXT
TIME TIME [WITHOUT TIME ZONE]
TIMESTAMP TIMESTAMP [WITHOUT TIME ZONE]
VARBINARY(n), VARBINARY(max) BYTEA
VARCHAR(n) VARCHAR(n)
VARCHAR(max) TEXT

While PostgreSQL uses SERIAL type, along with its modifications, for integer-like columns, in MySQL the equivalent argument is known as ‘auto_increment’, and what it does is automatically increase the value of the field whenever a new row is put in.

MySQL PostgreSQL
BIGINT AUTO_INCREMENT BIGSERIAL
INTEGER AUTO_INCREMENT SERIAL
SMALLINT AUTO_INCREMENT SMALLSERIAL
TINYINT AUTO_INCREMENT SMALLSERIAL

In MySQL, every type of integer is able to have an UNSIGNED attribute. This applies to bigint, int, smallint and tinyint. This attribution means that you have to use only positive numbers, with a greater upper range within the span of acceptable values. This is not the case in PostgreSQL. You can see in the following table how you need to map unsigned MySQL types into PostgreSQL.

MySQL PostgreSQL
BIGINT UNSIGNED NUMERIC(20)
INT UNSIGNED BIGINT
MEDIUMINT UNSIGNED INTEGER
SMALLINT UNSIGNED INTEGER
TINYINT UNSIGNED INTEGER

Remember, in MySQL you can enter the following value into date columns: ‘000-00-00’. You can’t do this in PostgreSQL. Instead, you’ll need to put NULL rather than that value when you migrate data. There is the danger of breaking database logics by doing this though, and in such a case you’ll need to use a different mapping.

Data

You can visually compare fragments from PostgreSQL and MySQL tables to validate the data you’ve converted. You can do it like this:

  • If you’re using the MySQL client, to explore data fragments, run this SQL statement SELECT * FROM the_table LIMIT start_position, number_of_rows
  • If you’re using phpMyAdmin go to the left pane, where you’ll highlight the table. After that select the ‘Browse’ tab

In PostgreSQL the process is similar, with the SELECT-query, but there are a number of differences.

SELECT * FROM the_table LIMIT number_of_rows OFFSET start_position;

You’ll also need to make sure that both your MySQL tables and your PostgreSQL tables have the same row count. You can do that in both MySQL and PostSQL with this query:

SELECT COUNT(*) FROM the_table; 

Indexes

In MySQL, this is how you list indexes:

  • If you’re using the MySQL client, you can run the following SQL statement:

SHOW INDEXES FROM the_table;

  • If you’re using phpMyAdmin go to the left pane, where you’ll highlight the table. After this you can go to the ‘Structure’ tab. Once you select that, after the table structure all the indexes will be listed.

In PostgreSQL, you’ll be able go to the bottom of the table definition to see information about indexes. You can generate this table by using this command: \d table_name

Foreign Keys

In MySQL, if you want to expose foreign keys information, you’ll need to do the following:

  • If you’re using the MySQL console client, you should run the following SQL statement <<<SQLCODE4>>>
  • If you’re using phpMyAdmin, go to the left pane where you will highlight the table, after that go to the ‘Structure’ tab and below the table you will select the ‘Relations view’ link.

In PostgreSQL, you can get the same information by using the service table “information_schema”:

SELECT

    tabcon.constraint_name, tabcon.table_name, keycol.column_name,

    concol.table_name AS ftable_name,

    concol.column_name AS fcolumn_name

FROM

    information_schema.table_constraints AS tabcon

    JOIN information_schema.key_column_usage AS keycol

      ON tabcon.constraint_name = keycol.constraint_name

    JOIN information_schema.constraint_column_usage AS concol

      ON concol.constraint_name = tabcon.constraint_name

WHERE constraint_type = ‘FOREIGN KEY’ AND tabcon.table_name=’the_table_name’; 

Views

Comparing the SELECT-statement of each view in PostgreSQL and MySQL is really the only way you can make sure that all views have been converted correctly, keeping these two DBMS’s differences in SQL dialects in mind. Because this requires extensive knowledge of database programming, it is not included in this article. You can, however, easily access a list of all views in both the destination and source databases in both DBMS.

In MySQL, you can use the following query to expose the list of the views in the database:

SHOW FULL TABLES IN `database name` WHERE TABLE_TYPE LIKE ‘VIEW’;

You can perform the same function in PostgreSQL by using this query:

SELECT table_name FROM INFORMATION_SCHEMA.views;