Tips for MySQL to PostgreSQL Switch
Posted on July 2nd, 2008 by Greg Allard in MySQL | Comments
If you’ve decided to move a few tables from MySQL to PostgreSQL, these few tips might help. I won’t get into any reasons why to move to PostgreSQL or not. There are already many discussions on the topic.
Create Syntax
The first five listed need to be done in order; the rest can be in any.
- Replace mediumint with int
- Replace tinyint with smallint
- Replace int\([0-9]+\) with int. (This is a regular expression that will find any instance of int() where there is at least one number between the parenthesis)
- Remove all instances of NOT NULL I don’t know what I was thinking here.
- Replace int\s+auto_increment with SERIAL (Another regular expression that will catch a case with multiple spaces between int and auto_increment).
- UPDATE: try int(\s+|\s+NOT NULL\s+)auto_increment instead to catch when not null is in between int and auto.
- UPDATE: try (smallint|int)(\s+|\s+NOT NULL\s+)auto_increment so it will catch the case when it is smallint as well.
- Remove ENGINE=MyISAM (Change if you use a different engine, i.e. innodb)
- Remove DEFAULT CHARSET=latin1 (Change if you use a different character set)
- Remove AUTO_INCREMENT=[0-9]+ (Another regular expression to find all cases of an auto increment number)
- Remove IF NOT EXISTS
- Remove all `
phpMyAdmin
phpMyAdmin has options when exporting that may make some of these steps unnecessary. If you keep the create sql in a separate file from the insert sql, you save yourself from changing any data that may look like some of the above when it isn’t actual sql syntax.
Auto Increment and Serial
After running the create table SQL, a sequence table will be created for the serial column. This sequence will need to be altered if the auto increment won’t be starting at one. A query like the one below will need to be done for each table.
ALTER SEQUENCE tab_name_col_name_seq RESTART WITH #;
Indexes
To define a column to be indexed, it is done in a separate query. Remove anything that looks like KEY username (username). The query to run after the create table query should look like
CREATE INDEX index_name ON table_name (column_name);
Insert Syntax
The syntax here is pretty close, just one thing needs to be changed.
- Replace “” with \”. (MySQL and PostgreSQL use different methods of escaping these quotes.)
Make sure the complete inserts option is enabled and extended inserts isn’t. PosgreSQL doesn’t support multiple inserts with one query the same way. (As far as I know at the moment)
Your Mileage May Vary
This was tested using phpMyAdmin export and phpPgAdmin SQL executing. The regular expressions and other replace commands were done in jEdit. With my few test cases I probably didn’t catch everything. Let me know how it works out for you.
I use webfaction to host a lot of my django projects. It has an easy setup that will get you developing quickly and a great community of talented programmers. There is also a quick setup for rails, wordpress, and a lot more.
Related posts:
- Careful with Numeric Data Types in MySQL TINYINT can be set to length of 4, but that doesn’t mean the max value is 9999. A tiny int...
- How To Use Triggers to Track Changes in MySQL Setting constraints and rules in the database is better than writing special code to handle the same task since it...
- How to Break a MySQL Left Join Care must be taken when placing conditions on the results of the right-hand table of a LEFT JOIN because it...
Greg Allard is the programmer behind
July 2nd, 2008 on 10:00 am
“Remove all instances of NOT NULL”
WTF!? This can seriously break many schemas. Don’t do this.
Postres supports multiple-row inserts only in latest versions (8.3+ IIRC).
another useful replacement may be CURRENT_TIMESTAMP with NOW() and timestamp with timestamp without time zone, etc.
July 2nd, 2008 on 10:17 am
@kL
Thanks for the comment. I updated the post to cross out the NOT NULL part, I think I meant that only for the auto_increment part, but messed it up.
I’ll check out the newer versions to see what’s been changed. I was on 7.4.7.
July 2nd, 2008 on 11:00 am
“Remove all instances of NOT NULL”
WTF!? This can seriously break many schemas. Don't do this.
Postres supports multiple-row inserts only in latest versions (8.3+ IIRC).
another useful replacement may be CURRENT_TIMESTAMP with NOW() and timestamp with timestamp without time zone, etc.
July 2nd, 2008 on 11:17 am
@kL
Thanks for the comment. I updated the post to cross out the NOT NULL part, I think I meant that only for the auto_increment part, but messed it up.
I'll check out the newer versions to see what's been changed. I was on 7.4.7.
July 2nd, 2008 on 11:34 am
- datetime in MySQL is equal to timestamp in PostgreSQL. There is no equivalent of MySQL’s timestamp data type in PostgreSQL
- An enum column in MySQL is equal to a varchar with a check constraint in PostgreSQL, for example:
column_name enum(’foo’, ‘bar’) is equal to column_name varchar(3) check (column_name IN (’foo’, ‘bar’))
- The blob datatype in MySQL is equal to a bytea datatype in PostgreSQL
Also, when truncating a table, I do not believe PostgreSQL resets the sequence for serial columns, so it will need to be done manually.
Obviously there are more differences when moving to PostgreSQL, but these are most of the big ones for CREATE TABLE statements.
Just a little FYI. I wrote these all out, but forgot my email. Your blog is very unfriendly to comments without an email, as in I lost my comments.
July 2nd, 2008 on 12:34 pm
- datetime in MySQL is equal to timestamp in PostgreSQL. There is no equivalent of MySQL's timestamp data type in PostgreSQL
- An enum column in MySQL is equal to a varchar with a check constraint in PostgreSQL, for example:
column_name enum('foo', 'bar') is equal to column_name varchar(3) check (column_name IN ('foo', 'bar'))
- The blob datatype in MySQL is equal to a bytea datatype in PostgreSQL
Also, when truncating a table, I do not believe PostgreSQL resets the sequence for serial columns, so it will need to be done manually.
Obviously there are more differences when moving to PostgreSQL, but these are most of the big ones for CREATE TABLE statements.
Just a little FYI. I wrote these all out, but forgot my email. Your blog is very unfriendly to comments without an email, as in I lost my comments.
July 2nd, 2008 on 1:18 pm
I removed the requirement for e-mail. I had no idea WordPress handled it so poorly since I was always logged in.
July 2nd, 2008 on 2:18 pm
I removed the requirement for e-mail. I had no idea WordPress handled it so poorly since I was always logged in.
July 2nd, 2008 on 8:56 pm
[...] te encuentras en la situación descrita en el título, encontré unos tips para hacerlo de una forma sencilla. El autor del HowTo, nos indica que los 5 primeros pasos se [...]
July 3rd, 2008 on 6:51 am
[...] 详细内容 [...]
July 11th, 2008 on 12:25 am
can you give me the sql equivalent of this to postgresql, i’ll be using phppgadmin thanks…
CREATE TABLE manila_area_tbl
(
area_no tinyint(4) NOT NULL auto_increment,
area_name varchar(65) NOT NULL,
disp_flag char(1) NOT NULL default ‘1′,
disp_no tinyint(4) NOT NULL,
del_flag char(1) NOT NULL default ‘0′,
PRIMARY KEY (`area_no`),
KEY disp_no` (`disp_no`),KEY `del_flag` (`del_flag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=25;
July 11th, 2008 on 1:25 am
can you give me the sql equivalent of this to postgresql, i'll be using phppgadmin thanks…
CREATE TABLE manila_area_tbl
(
area_no tinyint(4) NOT NULL auto_increment,
area_name varchar(65) NOT NULL,
disp_flag char(1) NOT NULL default '1',
disp_no tinyint(4) NOT NULL,
del_flag char(1) NOT NULL default '0',
PRIMARY KEY (`area_no`),
KEY disp_no` (`disp_no`),KEY `del_flag` (`del_flag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=25;
July 11th, 2008 on 8:56 am
I noticed another case I may have missed, so I edited the post to catch when the key may be a smallint.
Try running these 4 queries.
CREATE TABLE manila_area_tbl
(
area_no SERIAL,
area_name varchar(65) NOT NULL,
disp_flag char(1) NOT NULL default 1,
disp_no smallint NOT NULL,
del_flag char(1) NOT NULL default 0,
PRIMARY KEY (area_no)
) ;
CREATE INDEX disp_no ON manila_area_tbl (disp_no);
CREATE INDEX del_flag ON manila_area_tbl (del_flag);
ALTER SEQUENCE manila_area_tbl_area_no_seq RESTART WITH 25;
July 11th, 2008 on 9:56 am
I noticed another case I may have missed, so I edited the post to catch when the key may be a smallint.
Try running these 4 queries.
CREATE TABLE manila_area_tbl
(
area_no SERIAL,
area_name varchar(65) NOT NULL,
disp_flag char(1) NOT NULL default 1,
disp_no smallint NOT NULL,
del_flag char(1) NOT NULL default 0,
PRIMARY KEY (area_no)
) ;
CREATE INDEX disp_no ON manila_area_tbl (disp_no);
CREATE INDEX del_flag ON manila_area_tbl (del_flag);
ALTER SEQUENCE manila_area_tbl_area_no_seq RESTART WITH 25;
September 28th, 2008 on 5:26 am
Hello,
do you know how to do something like this ?
ALTER SEQUENCE univers_id_seq RESTART WITH (SELECT max(id) + 1 FROM univers);
Thanks
Emilien
September 30th, 2008 on 11:05 am
If that query isn't working, try moving the +1 out of the select statement like this:
ALTER SEQUENCE univers_id_seq RESTART WITH (SELECT max(id) FROM univers)+1;