Migrar de MySQL a PostgresQL

Por:ivancp, enviado 18 sep 2011
Cualquiera sea la razón, migrar de MySQL a PostgreSQL no es muy complicado si se tienen algunos scripts que hagan el trabajo sucio por nosotros, pero aun así hay que hacer algunas tareas.

El problema radica en la diferencia que hay en la sintaxis del lenguaje SQL de ambos, se parecen pero no son iguales, yo creo que lo mas complicado es migrar los campos que están señalados como AUTO_INCREMENT en MySQL, en PostgreSQL no existe AUTO_INCREMENT como atributo de campo, para ello es necesario crear una secuencia y enlazarlo al campo.

Manos a la obra!

mysql_to_postgresql.png
Migrar MySQL A PostgreSQL

Paso 1: Generar un respaldo de la ESTRUCTURA de la base de datos MySQL

El primer paso para migrar nuestra base de datos MySQL a PostgreSQL es generar una copia de seguridad mediante mysqldump de la siguiente forma:

Code: Seleccionar todo
  1. $mysqldump -u [usuario] -p [base_de_datos]  --no-data > [archivo_salida].sql


Donde:
  • [usuario] el el nombre de usuario con el que accedemos a la base de datos (usualmente root)
  • [base_de_datos] es el nombre de la base de datos que vamos a migrar (en este caso la base de datos de ejemplo World).
  • [archivo_salida] es el nombre de nuestro archivo de salida que contendrá los comandos SQL generados por mysqldump
  • El parametro -p es para que musqldump nos pregunte por la contraseña del usuario, si no hay contraseña de base de datos entonces obviar este parametro.
  • El parametro --no-data omitirá los datos, por que sólo necesitamos la estructura de las tablas, una vez migrada la estructura seguimos con los datos mas adelante.

El comando se vería de la siguiente forma:

Code: Seleccionar todo
  1. $mysqldump -u root -p world  --no-data > world-mysql.sql


El comando producirá un archivo parecido al siguiente:

Code: Seleccionar todo
  1. -- MySQL dump 10.13  Distrib 5.1.54, for debian-linux-gnu (i686)

  2. --

  3. -- Host: localhost    Database: world

  4. -- ------------------------------------------------------

  5. -- Server version       5.1.54-1ubuntu4

  6.  

  7. --

  8. -- Table structure for table `City`

  9. --

  10.  


  11. /*!40101 SET @saved_cs_client     = @@character_set_client */;

  12. /*!40101 SET character_set_client = utf8 */;

  13. CREATE TABLE `City` (

  14.   `ID` int(11) NOT NULL AUTO_INCREMENT,

  15.   `Name` char(35) NOT NULL DEFAULT '',

  16.   `CountryCode` char(3) NOT NULL DEFAULT '',

  17.   `District` char(20) NOT NULL DEFAULT '',

  18.   `Population` int(11) NOT NULL DEFAULT '0',

  19.   PRIMARY KEY (`ID`)

  20. ) ENGINE=MyISAM AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1;

  21. /*!40101 SET character_set_client = @saved_cs_client */;

  22.  

  23. --

  24. -- Table structure for table `Country`

  25. --

  26.  

  27. DROP TABLE IF EXISTS `Country`;

  28. /*!40101 SET @saved_cs_client     = @@character_set_client */;

  29. /*!40101 SET character_set_client = utf8 */;

  30. CREATE TABLE `Country` (

  31.   `Code` char(3) NOT NULL DEFAULT '',

  32.   `Name` char(52) NOT NULL DEFAULT '',

  33.   `Continent` enum('Asia','Europe','North America','Africa','Oceania','Antarctica','South America') NOT NULL DEFAULT 'Asia',

  34.   `Region` char(26) NOT NULL DEFAULT '',

  35.   `SurfaceArea` float(10,2) NOT NULL DEFAULT '0.00',

  36.   `IndepYear` smallint(6) DEFAULT NULL,

  37.   `Population` int(11) NOT NULL DEFAULT '0',

  38.   `LifeExpectancy` float(3,1) DEFAULT NULL,

  39.   `GNP` float(10,2) DEFAULT NULL,

  40.   `GNPOld` float(10,2) DEFAULT NULL,

  41.   `LocalName` char(45) NOT NULL DEFAULT '',

  42.   `GovernmentForm` char(45) NOT NULL DEFAULT '',

  43.   `HeadOfState` char(60) DEFAULT NULL,

  44.   `Capital` int(11) DEFAULT NULL,

  45.   `Code2` char(2) NOT NULL DEFAULT '',

  46.   PRIMARY KEY (`Code`)

  47. ) ENGINE=MyISAM DEFAULT CHARSET=latin1;

  48. /*!40101 SET character_set_client = @saved_cs_client */;

  49.  

  50. --

  51. -- Table structure for table `CountryLanguage`

  52. --

  53.  

  54. DROP TABLE IF EXISTS `CountryLanguage`;

  55. /*!40101 SET @saved_cs_client     = @@character_set_client */;

  56. /*!40101 SET character_set_client = utf8 */;

  57. CREATE TABLE `CountryLanguage` (

  58.   `CountryCode` char(3) NOT NULL DEFAULT '',

  59.   `Language` char(30) NOT NULL DEFAULT '',

  60.   `IsOfficial` enum('T','F') NOT NULL DEFAULT 'F',

  61.   `Percentage` float(4,1) NOT NULL DEFAULT '0.0',

  62.   PRIMARY KEY (`CountryCode`,`Language`)

  63. ) ENGINE=MyISAM DEFAULT CHARSET=latin1;

  64. /*!40101 SET character_set_client = @saved_cs_client */;

  65. /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

  66.  

  67. -- Dump completed on 2011-09-17 11:36:57




Paso 2: Traducir SQL de MySQL a SQL de PostgresSQL utilizando mysql2pgsql.perl

mysql2pgsql.perl es un script escrito en Perl que "traduce" la sintaxis del código SQL de MySQL a un SQL que pueda entender PostgreSQL, es muy fácil de usar:

Code: Seleccionar todo
  1. $perl mysql2pgsql.perl [opciones] sql_formato_mysql.sql sql_formato_postgresql.sql


Para nuestro caso, el comando que ejecutamos es:

Code: Seleccionar todo
  1. $perl mysql2pgsql.perl --nodrop world-mysql.sql world-postgresql.sql


Donde:
    La opción --nodrop hace que el script no incluya los comandos DROP TABLE antes de crear las tablas.
El archivo world-postgresql.sql generado por el script se verá asi:

Code: Seleccionar todo
  1. --

  2. -- Generated from mysql2pgsql.perl

  3. -- http:// gborg.postgresql.org/project/mysql2psql/

  4. -- (c) 2001 - 2007 Jose M. Duarte, Joseph Speigle

  5. --

  6.  

  7. -- warnings are printed for drop tables if they do not exist

  8. -- please see http:// archives.postgresql.org/pgsql-novice/2004-10/msg00158.php

  9.  

  10. -- ##############################################################

  11.  

  12. -- MySQL dump 10.13  Distrib 5.1.54, for debian-linux-gnu (i686)

  13. --

  14. -- Host: localhost    Database: world

  15. -- ------------------------------------------------------

  16. -- Server version       5.1.54-1ubuntu4

  17.  

  18.  

  19. --

  20. -- Table structure for table City

  21. --

  22.  

  23. DROP SEQUENCE "city_id_seq" CASCADE ;

  24.  

  25. CREATE SEQUENCE "city_id_seq"  START WITH 4080 ;

  26.  

  27. CREATE TABLE  "city" (

  28.    "id" integer DEFAULT nextval('"city_id_seq"') NOT NULL,

  29.    "name"   char(35) NOT NULL DEFAULT '',

  30.    "countrycode"   char(3) NOT NULL DEFAULT '',

  31.    "district"   char(20) NOT NULL DEFAULT '',

  32.    "population"   int NOT NULL DEFAULT '0',

  33.    primary key ("id")

  34. )   ;

  35. /*!40101 SET character_set_client = @saved_cs_client */;

  36. /*!40101 SET @saved_cs_client     = @@character_set_client */;

  37. /*!40101 SET character_set_client = utf8 */;

  38.  

  39. --

  40. -- Table structure for table Country

  41. --

  42.  

  43. CREATE TABLE  "country" (

  44.    "code"   char(3) NOT NULL DEFAULT '',

  45.    "name"   char(52) NOT NULL DEFAULT '',

  46.  "continent" varchar CHECK ("continent" IN ( 'Asia','Europe','North America','Africa','Oceania','Antarctica','South America' )) NOT NULL DEFAULT 'Asia',

  47.    "region"   char(26) NOT NULL DEFAULT '',

  48.    "surfacearea"   double precision NOT NULL DEFAULT '0.00',

  49.    "indepyear"   smallint DEFAULT NULL,

  50.    "population"   int NOT NULL DEFAULT '0',

  51.    "lifeexpectancy"   double precision DEFAULT NULL,

  52.    "gnp"   double precision DEFAULT NULL,

  53.    "gnpold"   double precision DEFAULT NULL,

  54.    "localname"   char(45) NOT NULL DEFAULT '',

  55.    "governmentform"   char(45) NOT NULL DEFAULT '',

  56.    "headofstate"   char(60) DEFAULT NULL,

  57.    "capital"   int DEFAULT NULL,

  58.    "code2"   char(2) NOT NULL DEFAULT '',

  59.    primary key ("code")

  60. )  ;

  61. /*!40101 SET character_set_client = @saved_cs_client */;

  62. /*!40101 SET @saved_cs_client     = @@character_set_client */;

  63. /*!40101 SET character_set_client = utf8 */;

  64.  

  65. --

  66. -- Table structure for table CountryLanguage

  67. --

  68.  

  69. CREATE TABLE  "countrylanguage" (

  70.    "countrycode"   char(3) NOT NULL DEFAULT '',

  71.    "language"   char(30) NOT NULL DEFAULT '',

  72.  "isofficial" varchar CHECK ("isofficial" IN ( 'T

    '
    ,'F' )) NOT NULL DEFAULT 'F',

  73.    "percentage"   double precision NOT NULL DEFAULT '0.0',

  74.    primary key ("countrycode", "language")

  75. ) ;


Notar el cambio en la forma de declarar los campos y la creación de secuencias. Los comentarios en PostgreSQL son solo eso comentarios.

Paso 3: Volcar la estructura de las tablas a PostgreSQL

Este paso también es muy sencillo, lo podemos hacerlo mediante la linea de comando:

Code: Seleccionar todo
  1. $su postgres

  2. createdb world

  3. psql -f world_postgresql.sql -u usuario-db word



Paso 4: Volcar los datos de MySQL a PostgreSQL

Ya casi todo esta listo, ahora solo nos queda migrar los datos. Al igual que en el paso 1, tenemos que sacar un respaldo pero esta vez solamente de los datos sin la creación de la estructura de las tablas, para ellos utilizamos el siguiente comando:

Code: Seleccionar todo
  1. $mysqldump -u root -p world --no-create-info --complete-insert --skip-add-locks > world-mysql-data.sql


Donde:
  • --no-create-info Omite los scripts de creación de tabla (no las necesitamos)
  • --complete-insert Hace que los comandos INSERT contengan también los nombres de las columnas, PostgreSQL no permite comandos INSERT sin los nombres de las columnas.
  • --skip-add-locks omite el comando LOCK TABLE ... ese comando también es diferente en PostgreSQL.
Como resultado tendremos el archivo world-mysql-data.sql, va a ser algo parecido a esto:


Code: Seleccionar todo
  1. -- MySQL dump 10.13  Distrib 5.1.54, for debian-linux-gnu (i686)

  2. --

  3. -- Host: localhost    Database: world

  4. -- ------------------------------------------------------

  5. -- Server version       5.1.54-1ubuntu4

  6.  

  7. --

  8. -- Dumping data for table `City`

  9. --

  10.  

  11. INSERT INTO `City` (`ID`, `Name`, `CountryCode`, `District`, `Population`)

  12.    VALUES (1,'Kabul','AFG','Kabol',1780000),

  13.           (2,'Qandahar','AFG','Qandahar',237500),

  14.           (3,'Herat','AFG','Herat',186800)

  15.           ...

  16.  

  17. --

  18. -- Dumping data for table `Country`

  19. --

  20.  

  21.  

  22. INSERT INTO `Country` ( `Code`, `Name`, `Continent`, `Region`, `SurfaceArea`, `IndepYear`,

  23.                         `Population`, `LifeExpectancy`, `GNP`, `GNPOld`, `LocalName`, `GovernmentForm`,

  24.                         `HeadOfState`, `Capital`, `Code2`)

  25. VALUES ('ABW','Aruba','North America','Caribbean',193.00,NULL,103000,78.4,828.00,793.00,

  26.            'Aruba','Nonmetropolitan Territory of The Netherlands','Beatrix',129,'AW'),

  27.         ('AFG','Afghanistan','Asia','Southern and Central Asia',652090.00,1919,22720000,

  28.            45.9,5976.00,NULL,'Afganistan/Afqanestan','Islamic Emirate','Mohammad Omar',1,'AF'),

  29.         ('AGO','Angola','Africa','Central Africa',1246700.00,1975,12878000,38.3,6648.00,7984.00,

  30.           'Angola','Republic','José Eduardo dos Santos',56,'AO'),

  31.         ('AIA','Anguilla','North America','Caribbean',96.00,NULL,8000,76.1,63.20,NULL,

  32.           'Anguilla','Dependent Territory of the UK','Elisabeth II',62,'AI'),

  33.         ('ALB','Albania','Europe','Southern Europe',28748.00,1912,3401200,71.6,3205.00,2500.00,

  34.           'Shqipëria','Republic','Rexhep Mejdani',34,'AL'),

  35. ...

  36.  

  37. --

  38. -- Dumping data for table `CountryLanguage`

  39. --

  40.  

  41. INSERT INTO `CountryLanguage` (`CountryCode`, `Language`, `IsOfficial`, `Percentage`)

  42. VALUES ('ABW','Dutch','T',5.3),

  43.        ('ABW','English','F',9.5),

  44.        ('ABW','Papiamento','F',76.7),

  45.        ('ABW','Spanish','F',7.4)

  46.        ...

  47.  

  48. -- Dump completed on 2011-09-18 17:41:21



Es probable que tengas problemas con el caracter `, para ello vamos a reemplazar el caracter con la doble comilla " utilizando:

Code: Seleccionar todo
  1. $sed -i 's/`/'"'/g' world-mysql-data.sql



Pueden también utilizar su editor de texto favorito, el resultado es el siguiente:

Code: Seleccionar todo
  1. -- MySQL dump 10.13  Distrib 5.1.54, for debian-linux-gnu (i686)

  2. --

  3. -- Host: localhost    Database: world

  4. -- ------------------------------------------------------

  5. -- Server version       5.1.54-1ubuntu4

  6.  

  7. --

  8. -- Dumping data for table "City"

  9. --

  10.  

  11. INSERT INTO "City" ("ID", "Name", "CountryCode", "District", "Population")

  12.    VALUES (1,'Kabul','AFG','Kabol',1780000),

  13.           (2,'Qandahar','AFG','Qandahar',237500),

  14.           (3,'Herat','AFG','Herat',186800)

  15.           ...

  16.  

  17. --

  18. -- Dumping data for table "Country"

  19. --

  20.  

  21.  

  22. INSERT INTO "Country" ( "Code", "Name", "Continent", "Region", "SurfaceArea", "IndepYear",

  23.                         "Population", "LifeExpectancy", "GNP", "GNPOld", "LocalName", "GovernmentForm",

  24.                         "HeadOfState", "Capital", "Code2")

  25. VALUES ('ABW','Aruba','North America','Caribbean',193.00,NULL,103000,78.4,828.00,793.00,

  26.            'Aruba','Nonmetropolitan Territory of The Netherlands','Beatrix',129,'AW'),

  27.         ('AFG','Afghanistan','Asia','Southern and Central Asia',652090.00,1919,22720000,

  28.            45.9,5976.00,NULL,'Afganistan/Afqanestan','Islamic Emirate','Mohammad Omar',1,'AF'),

  29.         ('AGO','Angola','Africa','Central Africa',1246700.00,1975,12878000,38.3,6648.00,7984.00,

  30.           'Angola','Republic','José Eduardo dos Santos',56,'AO'),

  31.         ('AIA','Anguilla','North America','Caribbean',96.00,NULL,8000,76.1,63.20,NULL,

  32.           'Anguilla','Dependent Territory of the UK','Elisabeth II',62,'AI'),

  33.         ('ALB','Albania','Europe','Southern Europe',28748.00,1912,3401200,71.6,3205.00,2500.00,

  34.           'Shqipëria','Republic','Rexhep Mejdani',34,'AL'),

  35. ...

  36.  

  37. --

  38. -- Dumping data for table "CountryLanguage"

  39. --

  40.  

  41. INSERT INTO "CountryLanguage" ("CountryCode", "Language", "IsOfficial", "Percentage")

  42. VALUES ('ABW','Dutch','T',5.3),

  43.        ('ABW','English','F',9.5),

  44.        ('ABW','Papiamento','F',76.7),

  45.        ('ABW','Spanish','F',7.4)

  46.        ...

  47.  

  48. -- Dump completed on 2011-09-18 17:41:21



Una vez listo el archivo que contiene los datos, los volcamos a PostgreSQL, al igual que el paso 3:

Code: Seleccionar todo
  1. $su postgres

  2. psql -f world_mysql-data.sql -u usuario-db word



Enjoy, es todo!

Otros Artículos en esta sección

  • Instalar OCS Inventory Paso a paso
    Si necesitas mantener un inventario en tiempo real de los equipos conectados a tu red, te mostramos paso a paso como instalar y utilizar OCS Inventory.
    Por: ivancp, 2011-10-21
  • Como publicar artículos en Latindevelopers
    Si tienes algo interesante que decir puedes publicar artículos en nuestro sitio web. Las instrucciones son fáciles.
    Por: ivancp, 2011-08-30
  • Decimal a Binario
    Deseas saber como convertir un numero decimal a binario? esta es una implementacion en varios lenguajes de programación. PHP, C++, VB.NET, C#, JAVA, DELPHI, TCL, ASP.NET, etc
    Por: Blag, 2005-01-12
¿Alguna duda? Sientete libre de hacer tus pruntas en nuestro:
foro de Programación »