Saltar al contenido

Convertir MySQL a PostgreSQL

Suelta un volcado de mysqldump, mariadb-dump, mysqlpump, phpMyAdmin o Adminer, en .sql o .sql.gz, y descarga un script de PostgreSQL con las mismas tablas, claves, índices y datos. Cada valor que PostgreSQL no puede guardar tal cual, y cada objeto que no admite, aparece en el informe.

Tu archivo
sqlshop_2024.sqlArchivo de ejemplo, 5,1 kB
Tu base de datos

18 filas convertidas, 8 correcciones, ninguna rechazada

Mostrar

Tablas en shop_2024.sqlVolcado de MySQL 8.0.46 · escrito por mysqldump · utf8mb4

  • customers8 filas3
    7 columnas
    idbigint not null
    namevarchar(100) not null
    emailvarchar(150) not null
    countryvarchar(2) not null
    is_activeboolean not null
    signed_update
    created_attimestamptz(0) not null
  • orders10 filas5
    9 columnas
    idbigint not null
    customer_idbigint not null
    statustext not null
    totalnumeric(10,2) not null
    itemsinteger not null
    ordered_attimestamp(0) not null
    shipped_attimestamp(0)
    notevarchar(255)
    updated_attimestamptz(0) not null
-- Written by IntoSQL from a MySQL dump. Load it with: psql -v ON_ERROR_STOP=1 -f <file>
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;
SET synchronous_commit = off;
SET maintenance_work_mem = '512MB';

CREATE TABLE customers (
    id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
    name varchar(100) NOT NULL,
    email varchar(150) NOT NULL,
    country varchar(2) DEFAULT 'US' NOT NULL,
    is_active boolean DEFAULT true NOT NULL,
    signed_up date,
    created_at timestamptz(0) DEFAULT CURRENT_TIMESTAMP NOT NULL,
    CONSTRAINT customers_id_check CHECK (id >= 0)
);

CREATE TABLE orders (
    id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
    customer_id bigint NOT NULL,
    status text DEFAULT 'pending' NOT NULL,
    total numeric(10,2) NOT NULL,
    items integer DEFAULT 1 NOT NULL,
    ordered_at timestamp(0) NOT NULL,
    shipped_at timestamp(0),
    note varchar(255),
    updated_at timestamptz(0) DEFAULT CURRENT_TIMESTAMP NOT NULL,
    CONSTRAINT orders_id_check CHECK (id >= 0),
    CONSTRAINT orders_customer_id_check CHECK (customer_id >= 0),
    CONSTRAINT orders_status_check CHECK (status IN ('pending', 'paid', 'shipped', 'cancelled', '')),
    CONSTRAINT orders_items_check CHECK (items >= 0)
);

COPY customers (id, name, email, country, is_active, signed_up, created_at) FROM stdin;
1	Ana Martínez	ana@example.com	MX	t	2023-01-15	2023-01-15 10:24:00+00:00
2	Zoë Müller	zoe@example.de	DE	t	2023-02-03	2023-02-03 08:11:42+00:00
3	José Peña	jose@example.es	ES	f	2023-03-21	2023-03-21 19:05:17+00:00
4	李雷	lilei@example.cn	CN	t	2023-05-09	2023-05-09 02:40:00+00:00
5	Noah O'Brien	noah@example.ie	IE	t	\N	2023-06-30 14:00:00+00:00
6	Amélie Laurent	amelie@example.fr	FR	t	2024-01-02	2024-01-02 09:30:00+00:00
7	Ravi Patel	ravi@example.in	IN	f	2024-02-29	2024-02-29 23:59:59+00:00
8	Sofia Rossi 🌻	sofia@example.it	IT	t	2024-04-12	2024-04-12 12:12:12+00:00
\.

COPY orders (id, customer_id, status, total, items, ordered_at, shipped_at, note, updated_at) FROM stdin;
1	1	shipped	129.90	3	2024-03-01 10:00:00	2024-03-02 16:30:00	\N	2024-03-02 16:30:00+00:00
2	2	paid	54.00	1	2024-03-02 11:15:00	\N	Gift wrap 🎁	2024-03-02 11:20:00+00:00
3	1	cancelled	12.50	1	2024-03-03 09:45:00	\N	Customer changed their mind	2024-03-03 10:00:00+00:00
4	4	shipped	980.00	12	2024-03-05 02:10:00	2024-03-07 08:00:00	请小心轻放	2024-03-07 08:00:00+00:00
5	5	pending	45.75	2	2024-03-08 17:20:00	\N	Leave at the door; ring twice	2024-03-08 17:20:00+00:00
6	6	shipped	310.40	5	2024-03-10 13:00:00	2024-03-11 09:00:00	Fragile: "glass"	2024-03-11 09:00:00+00:00
7	8	paid	76.20	2	2024-03-11 18:45:00	\N	\N	2024-03-11 18:46:00+00:00
8	2		19.99	1	2024-03-12 08:00:00	\N	Status lost in an old import	2024-03-12 08:00:00+00:00
9	3	shipped	64.00	2	2024-03-14 15:30:00	\N	Shipping date never recorded	2024-03-15 10:00:00+00:00
10	7	paid	1499.00	1	2024-03-15 21:05:00	\N	C:\\Users\\ravi\\invoice.pdf	2024-03-15 21:05:00+00:00
\.


-- Keys, indexes and constraints, after the rows so each builds once.
CREATE UNIQUE INDEX customers_pkey ON customers (id);
CREATE UNIQUE INDEX customers_uq_email ON customers (email);
CREATE UNIQUE INDEX orders_pkey ON orders (id);
CREATE INDEX orders_idx_customer ON orders (customer_id);
CREATE INDEX orders_idx_status_date ON orders (status, ordered_at);
ALTER TABLE customers ADD CONSTRAINT customers_pkey PRIMARY KEY USING INDEX customers_pkey;
ALTER TABLE customers ADD CONSTRAINT customers_uq_email UNIQUE USING INDEX customers_uq_email;
ALTER TABLE orders ADD CONSTRAINT orders_pkey PRIMARY KEY USING INDEX orders_pkey;
ALTER TABLE orders ADD CONSTRAINT fk_orders_customer FOREIGN KEY (customer_id) REFERENCES customers (id) NOT VALID;
DO $f$ BEGIN
    ALTER TABLE orders VALIDATE CONSTRAINT fk_orders_customer;
EXCEPTION WHEN foreign_key_violation THEN
    RAISE WARNING 'foreign key % has rows without a parent: it stays NOT VALID, and new rows are checked', 'fk_orders_customer';
END $f$;
SELECT setval(pg_get_serial_sequence('customers', 'id'), LEAST(GREATEST(COALESCE((SELECT max(id) FROM customers)::numeric, 0) + 1, 1001), 9223372036854775807)::bigint, false);
COMMENT ON TABLE customers IS 'People who bought at least once';
SELECT setval(pg_get_serial_sequence('orders', 'id'), LEAST(GREATEST(COALESCE((SELECT max(id) FROM orders)::numeric, 0) + 1, 5001), 9223372036854775807)::bigint, false);
CREATE FUNCTION orders_updated_at_on_update() RETURNS trigger LANGUAGE plpgsql AS $f$
BEGIN
    IF NEW IS DISTINCT FROM OLD AND NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at THEN
        NEW.updated_at := CURRENT_TIMESTAMP;
    END IF;
    RETURN NEW;
END
$f$;
CREATE TRIGGER orders_updated_at_on_update BEFORE UPDATE ON orders FOR EACH ROW EXECUTE FUNCTION orders_updated_at_on_update();

Correcciones

  1. "id" de customers no tiene signo, así que se volvió bigint con un CHECK de que no sea negativa.
  2. "signed_up" de customers ahora admite NULL: algunos de sus valores no se pudieron guardar y quedan en NULL.
  3. El valor predeterminado de "signed_up" en customers, 0000‑00‑00, no es una fecha que PostgreSQL acepte, así que la columna no tiene valor predeterminado.
  4. "customer_id" de orders no tiene signo, así que se volvió bigint con un CHECK de que no sea negativa.
  5. "id" de orders no tiene signo, así que se volvió bigint con un CHECK de que no sea negativa.
  6. "items" de orders no tiene signo, así que se volvió integer con un CHECK de que no sea negativa.
  7. 1 fila de "status" en orders contiene el valor vacío que MySQL guarda para entradas no válidas; el CHECK de sus valores lo admite.
  8. "updated_at" de orders cambia a la hora actual en cada actualización; en PostgreSQL lo hace un disparador.

Para revisar

  • 1 valor en "signed_up" de customers es una fecha cero o imposible, como 0000‑00‑00, que PostgreSQL no puede guardar, así que queda en NULL. Si esas fechas significan algo, reemplázalas en el origen por fechas reales antes del volcado.
  • 1 valor en "shipped_at" de orders es una fecha cero o imposible, como 0000‑00‑00 00:00:00, que PostgreSQL no puede guardar, así que queda en NULL. Si esas fechas significan algo, reemplázalas en el origen por fechas reales antes del volcado.

Tu archivo se convierte en esta computadora y nunca se sube.

Otros ejemplos:

Qué cambia de MySQL a PostgreSQL

Cada fila es una regla que aplica el convertidor, con la línea que escribe en el informe cuando la regla cambia algo en tus datos.

  • TINYINT(1), BOOL

    boolean cuando cada valor es 0 o 1, si no smallint

    El BOOL de MySQL es un TINYINT(1) que puede contener cualquier número de -128 a 127. Una columna que solo contiene 0 y 1 se vuelve boolean; una que contiene 2 o -1 sigue siendo un número para que ningún valor cambie de significado.

    Línea del informe: bool_kept_numeric

  • UNSIGNED integers

    el siguiente entero más amplio con un CHECK de que no sea negativo; BIGINT UNSIGNED es bigint cuando cada valor cabe, si no numeric(20,0)

    PostgreSQL no tiene enteros sin signo. Un tipo más amplio contiene todo el rango sin signo, y el CHECK deja fuera los valores negativos como hacía MySQL.

    Línea del informe: unsigned_widened

  • ZEROFILL and display widths

    se descarta

    El relleno con ceros es una forma de mostrar un número, no parte de él. Los números se conservan; dales formato al leerlos si el relleno importa.

    Línea del informe: zerofill_dropped

  • FLOAT(M,D)

    real o double precision, sin la escala

    Los números de punto flotante de PostgreSQL no tienen escala fija. Los valores guardados se copian tal cual.

    Línea del informe: float_scale_dropped

  • BIT(1), BIT(n)

    boolean, bit(n)

    Un solo bit es un sí o un no; los campos de bits más anchos conservan cada bit.

  • zero, partial or impossible dates (0000-00-00, 2024-02-30)

    NULL

    PostgreSQL solo guarda fechas reales del calendario. MySQL conserva fechas cero e imposibles cuando su modo SQL lo permite; se vuelven NULL y el informe las cuenta.

    Línea del informe: invalid_date_null

  • NOT NULL columns with values that become NULL

    sin NOT NULL

    Una columna cuyos valores tuvieron que volverse NULL no puede conservar NOT NULL, o la carga se detendría.

    Línea del informe: not_null_relaxed

  • DEFAULT '0000-00-00'

    sin valor predeterminado

    Un valor predeterminado que PostgreSQL no puede guardar se deja fuera en lugar de reemplazarse por una fecha inventada.

    Línea del informe: zero_date_default

  • TIME

    time cuando cada valor cabe en un día, si no interval

    El TIME de MySQL es una duración de -838:59:59 a 838:59:59. Una columna cuyos valores caben en un día sigue siendo una hora del día; una con valores más largos o negativos se vuelve interval.

    Línea del informe: time_as_interval

  • YEAR

    smallint con un CHECK de su rango

    PostgreSQL no tiene un tipo para años; el CHECK conserva los mismos valores que MySQL permite.

  • CHAR(n)

    varchar(n)

    MySQL quita los espacios finales de los valores CHAR al leerlos, y el char(n) de PostgreSQL los volvería a agregar, así que varchar(n) conserva los valores tal como MySQL los devuelve.

  • DATETIME

    timestamp

    Un DATETIME es una lectura de reloj sin zona horaria, como el timestamp de PostgreSQL.

  • TIMESTAMP

    timestamptz en la zona horaria que indica el volcado, UTC si no indica ninguna

    Un TIMESTAMP es un instante. Los volcados indican la zona en que lo escribieron (mysqldump escribe UTC); si no la indican, los valores se leen como UTC y el informe lo dice.

    Línea del informe: timestamp_zone_assumed

  • ENUM

    text con un CHECK de sus valores

    El texto con un CHECK conserva los mismos valores y es más fácil de cambiar que un enum type de PostgreSQL.

  • ENUM holding '' (MySQL's error value)

    el valor vacío, que el CHECK admite

    MySQL guarda una cadena vacía cuando un valor no válido entra en un ENUM en un modo SQL permisivo. Esas filas se conservan y el CHECK las admite.

    Línea del informe: enum_invalid_value

  • ENUM or SET holding values outside its list

    sin CHECK

    Los valores que la lista no nombra no cumplirían el CHECK, así que el CHECK no se crea.

    Línea del informe: enum_check_skipped

  • SET

    text[] con un CHECK de que cada elemento esté en la lista

    Un arreglo mantiene cada elemento por separado, así que las consultas pueden comprobar si un elemento está incluido.

  • JSON

    jsonb; json cuando un valor contiene \u0000

    jsonb es el tipo que PostgreSQL indexa y consulta bien, pero rechaza el escape \u0000, así que una columna que lo tiene sigue siendo json.

    Línea del informe: json_not_jsonb

  • NUL bytes in text

    se quita

    El texto de PostgreSQL no admite un byte NUL. El byte se quita y el informe cuenta los valores.

    Línea del informe: nul_removed

  • text that isn't valid in its character set

    los bytes incorrectos como carácter de reemplazo

    El texto de PostgreSQL debe ser UTF-8 válido.

    Línea del informe: invalid_utf8_replaced

  • text decoded twice (ñ)

    se copia tal cual

    El volcado contiene exactamente estos caracteres, así que se copian; el informe los señala.

    Línea del informe: mojibake_suspected

  • AUTO_INCREMENT

    GENERATED BY DEFAULT AS IDENTITY, con el contador después de los ids copiados

    Las columnas de identidad aceptan los ids copiados, y el contador sigue después del más alto o del AUTO_INCREMENT del volcado, el que sea mayor.

  • MyISAM AUTO_INCREMENT on the second column of a key

    un contador para la tabla

    MyISAM cuenta por grupo de la primera columna de la clave; PostgreSQL tiene un contador por columna.

    Línea del informe: myisam_group_autoincrement

  • ON UPDATE CURRENT_TIMESTAMP

    un disparador BEFORE UPDATE

    El disparador pone la hora actual en la columna cuando una fila cambia y la actualización no la asignó, como hace MySQL.

    Línea del informe: on_update_trigger

  • generated columns

    STORED

    PostgreSQL 13 a 17 solo guarda columnas generadas; la fórmula es la misma.

    Línea del informe: virtual_as_stored

  • generated columns with functions PostgreSQL lacks

    una columna simple

    Una fórmula que no se puede escribir para PostgreSQL deja una columna simple y una línea en el informe.

    Línea del informe: generated_not_translated

  • INVISIBLE columns

    columnas normales

    PostgreSQL no tiene columnas invisibles.

    Línea del informe: invisible_column

  • CHECK constraints

    traducido, en línea en CREATE TABLE

    Los CHECK en línea se comprueban al cargar cada fila, en lugar de recorrer la tabla por cada CHECK al final.

  • CHECK constraints with functions PostgreSQL lacks

    no se crea

    Un CHECK que no se puede escribir para PostgreSQL se deja fuera y aparece en el informe.

    Línea del informe: check_skipped

  • CHECK ... NOT ENFORCED

    no se crea

    MySQL conserva estos CHECK sin aplicarlos, así que las filas pueden no cumplirlos.

    Línea del informe: check_not_enforced

  • defaults with functions PostgreSQL lacks

    sin valor predeterminado

    Un valor predeterminado que no se puede escribir para PostgreSQL se deja fuera y aparece en el informe.

    Línea del informe: default_not_translated

  • keys and indexes on a prefix, c(20)

    un índice sobre left(c, 20)

    PostgreSQL indexa expresiones, así que se indexa el mismo prefijo.

    Línea del informe: prefix_unique

  • FULLTEXT indexes

    GIN sobre to_tsvector('simple', ...)

    La búsqueda de texto completo funciona con @@ en lugar de MATCH ... AGAINST.

    Línea del informe: fulltext_as_gin

  • indexes on values over 2,000 bytes

    hash

    Una entrada de btree no puede contener valores tan largos; un índice hash sigue encontrando valores iguales.

    Línea del informe: index_on_wide_values

  • SPATIAL indexes

    no se crea sin PostGIS

    Los índices espaciales necesitan PostGIS.

    Línea del informe: spatial_index_skipped

  • functional indexes PostgreSQL can't write

    no se crea

    Un índice sobre una expresión que no se puede escribir para PostgreSQL se deja fuera y aparece en el informe.

    Línea del informe: index_skipped

  • foreign keys

    después de los datos, NOT VALID, luego validada con una advertencia si hay huérfanas

    Los volcados se cargan con FOREIGN_KEY_CHECKS=0 y MyISAM nunca comprueba, así que las filas sin padre son comunes. La clave se crea primero y se valida al final; las filas huérfanas la dejan NOT VALID con una advertencia en lugar de detener la carga.

  • foreign keys to columns without a unique key

    no se crea

    PostgreSQL necesita una clave única en las columnas referenciadas; InnoDB no.

    Línea del informe: foreign_key_skipped

  • table and column names

    en minúsculas cuando siguen siendo únicos

    Los nombres en minúsculas se pueden escribir sin comillas en PostgreSQL.

    Línea del informe: identifier_renamed

  • names over 63 bytes

    acortado con un hash

    Los nombres de PostgreSQL admiten 63 bytes; el hash mantiene distintos los nombres acortados.

    Línea del informe: name_shortened

  • GEOMETRY, POINT, POLYGON...

    WKB en bytea

    Sin PostGIS, el formato binario estándar (well-known binary) conserva cada geometría con exactitud.

    Línea del informe: geometry_as_wkb

  • INET4, INET6, UUID (MariaDB)

    inet, uuid

    PostgreSQL tiene los mismos tipos.

  • BINARY, VARBINARY, BLOB

    bytea

    Los bytes siguen siendo bytes.

  • PARTITION BY

    una sola tabla

    Las filas son las mismas; vuelve a particionar la tabla en PostgreSQL si lo necesita.

    Línea del informe: partitions_dropped

  • WITH SYSTEM VERSIONING (MariaDB)

    una tabla con las filas actuales

    Un volcado solo contiene las filas actuales.

    Línea del informe: system_versioning_dropped

  • MyISAM, MEMORY and other engines

    una tabla normal

    PostgreSQL tiene un solo motor de tablas.

    Línea del informe: engine_dropped

  • views, triggers, procedures, functions and events

    listados en el informe

    Su código está escrito en el lenguaje de MySQL, así que se listan para que los vuelvas a crear.

    Línea del informe: not_converted_objects

  • dumps without CREATE TABLE

    tipos leídos de los valores

    Un volcado hecho con --no-create-info solo tiene filas, así que los tipos salen de los valores.

    Línea del informe: no_schema

  • geometries in a dump without CREATE TABLE

    WKB en bytea

    El formato de geometría de MySQL se reconoce por los valores y se guarda como WKB estándar.

    Línea del informe: geometry_inferred

  • _ci collations

    comparación exacta

    PostgreSQL compara el texto exactamente, salvo que se le indique otra cosa.

    Línea del informe: case_insensitive_collation

  • SET NAMES utf8 from utf8mb4 tables

    se convierte, con una advertencia

    El mysqlvolcado de MySQL 5.x escribe utf8 de 3 bytes por defecto y reemplaza cada emoji por un signo de interrogación antes de que cualquier convertidor vea el archivo.

    Línea del informe: dump_lost_four_byte_chars

  • dumps without SET NAMES whose text isn't UTF-8

    se lee como Latin-1 (Windows-1252)

    El latin1 de MySQL es Windows-1252, la codificación habitual de esos volcados.

    Línea del informe: charset_guessed

  • sjis, cp932, gbk, big5 dumps

    se lee como UTF-8, con una advertencia

    Estas codificaciones todavía no se decodifican.

    Línea del informe: unsupported_charset

  • UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows

    se evalúan

    Algunas herramientas de volcado escriben los valores como estas llamadas; se leen como los valores que producen, con el WKT geográfico convertido para que la longitud vaya primero.

  • other expressions in rows

    NULL

    Un convertidor que no ejecuta una base de datos no puede evaluarlas.

    Línea del informe: expression_value

  • DEFAULT in VALUES

    el valor predeterminado de la columna

    Un valor predeterminado literal se copia en la fila.

    Línea del informe: default_keyword_filled

  • DEFAULT in VALUES without a literal default

    NULL

    No hay ningún valor que copiar.

    Línea del informe: default_keyword_null

  • REPLACE and INSERT IGNORE

    filas simples

    Un volcado tiene una fila por clave, así que las filas son las mismas.

    Línea del informe: replace_as_insert

  • types IntoSQL doesn't know

    text

    El texto admite cualquier valor; cambia el tipo después de cargar.

    Línea del informe: unknown_type

  • values that don't fit their column

    NULL

    Un volcado dañado o escrito a mano puede contener valores que su columna no admite; se vuelven NULL para que se cargue el resto.

    Línea del informe: invalid_value_null

  • unreadable geometries

    NULL

    Los bytes que no son una geometría no se pueden guardar como una.

    Línea del informe: bad_geometry

  • CREATE SEQUENCE (MariaDB)

    CREATE SEQUENCE con su valor actual

    Las secuencias de PostgreSQL aceptan las mismas opciones.

En MySQLEn PostgreSQLLínea del informe
TINYINT(1), BOOLboolean cuando cada valor es 0 o 1, si no smallintEl BOOL de MySQL es un TINYINT(1) que puede contener cualquier número de -128 a 127. Una columna que solo contiene 0 y 1 se vuelve boolean; una que contiene 2 o -1 sigue siendo un número para que ningún valor cambie de significado.bool_kept_numeric
UNSIGNED integersel siguiente entero más amplio con un CHECK de que no sea negativo; BIGINT UNSIGNED es bigint cuando cada valor cabe, si no numeric(20,0)PostgreSQL no tiene enteros sin signo. Un tipo más amplio contiene todo el rango sin signo, y el CHECK deja fuera los valores negativos como hacía MySQL.unsigned_widened
ZEROFILL and display widthsse descartaEl relleno con ceros es una forma de mostrar un número, no parte de él. Los números se conservan; dales formato al leerlos si el relleno importa.zerofill_dropped
FLOAT(M,D)real o double precision, sin la escalaLos números de punto flotante de PostgreSQL no tienen escala fija. Los valores guardados se copian tal cual.float_scale_dropped
BIT(1), BIT(n)boolean, bit(n)Un solo bit es un sí o un no; los campos de bits más anchos conservan cada bit.
zero, partial or impossible dates (0000-00-00, 2024-02-30)NULLPostgreSQL solo guarda fechas reales del calendario. MySQL conserva fechas cero e imposibles cuando su modo SQL lo permite; se vuelven NULL y el informe las cuenta.invalid_date_null
NOT NULL columns with values that become NULLsin NOT NULLUna columna cuyos valores tuvieron que volverse NULL no puede conservar NOT NULL, o la carga se detendría.not_null_relaxed
DEFAULT '0000-00-00'sin valor predeterminadoUn valor predeterminado que PostgreSQL no puede guardar se deja fuera en lugar de reemplazarse por una fecha inventada.zero_date_default
TIMEtime cuando cada valor cabe en un día, si no intervalEl TIME de MySQL es una duración de -838:59:59 a 838:59:59. Una columna cuyos valores caben en un día sigue siendo una hora del día; una con valores más largos o negativos se vuelve interval.time_as_interval
YEARsmallint con un CHECK de su rangoPostgreSQL no tiene un tipo para años; el CHECK conserva los mismos valores que MySQL permite.
CHAR(n)varchar(n)MySQL quita los espacios finales de los valores CHAR al leerlos, y el char(n) de PostgreSQL los volvería a agregar, así que varchar(n) conserva los valores tal como MySQL los devuelve.
DATETIMEtimestampUn DATETIME es una lectura de reloj sin zona horaria, como el timestamp de PostgreSQL.
TIMESTAMPtimestamptz en la zona horaria que indica el volcado, UTC si no indica ningunaUn TIMESTAMP es un instante. Los volcados indican la zona en que lo escribieron (mysqldump escribe UTC); si no la indican, los valores se leen como UTC y el informe lo dice.timestamp_zone_assumed
ENUMtext con un CHECK de sus valoresEl texto con un CHECK conserva los mismos valores y es más fácil de cambiar que un enum type de PostgreSQL.
ENUM holding '' (MySQL's error value)el valor vacío, que el CHECK admiteMySQL guarda una cadena vacía cuando un valor no válido entra en un ENUM en un modo SQL permisivo. Esas filas se conservan y el CHECK las admite.enum_invalid_value
ENUM or SET holding values outside its listsin CHECKLos valores que la lista no nombra no cumplirían el CHECK, así que el CHECK no se crea.enum_check_skipped
SETtext[] con un CHECK de que cada elemento esté en la listaUn arreglo mantiene cada elemento por separado, así que las consultas pueden comprobar si un elemento está incluido.
JSONjsonb; json cuando un valor contiene \u0000jsonb es el tipo que PostgreSQL indexa y consulta bien, pero rechaza el escape \u0000, así que una columna que lo tiene sigue siendo json.json_not_jsonb
NUL bytes in textse quitaEl texto de PostgreSQL no admite un byte NUL. El byte se quita y el informe cuenta los valores.nul_removed
text that isn't valid in its character setlos bytes incorrectos como carácter de reemplazoEl texto de PostgreSQL debe ser UTF-8 válido.invalid_utf8_replaced
text decoded twice (ñ)se copia tal cualEl volcado contiene exactamente estos caracteres, así que se copian; el informe los señala.mojibake_suspected
AUTO_INCREMENTGENERATED BY DEFAULT AS IDENTITY, con el contador después de los ids copiadosLas columnas de identidad aceptan los ids copiados, y el contador sigue después del más alto o del AUTO_INCREMENT del volcado, el que sea mayor.
MyISAM AUTO_INCREMENT on the second column of a keyun contador para la tablaMyISAM cuenta por grupo de la primera columna de la clave; PostgreSQL tiene un contador por columna.myisam_group_autoincrement
ON UPDATE CURRENT_TIMESTAMPun disparador BEFORE UPDATEEl disparador pone la hora actual en la columna cuando una fila cambia y la actualización no la asignó, como hace MySQL.on_update_trigger
generated columnsSTOREDPostgreSQL 13 a 17 solo guarda columnas generadas; la fórmula es la misma.virtual_as_stored
generated columns with functions PostgreSQL lacksuna columna simpleUna fórmula que no se puede escribir para PostgreSQL deja una columna simple y una línea en el informe.generated_not_translated
INVISIBLE columnscolumnas normalesPostgreSQL no tiene columnas invisibles.invisible_column
CHECK constraintstraducido, en línea en CREATE TABLELos CHECK en línea se comprueban al cargar cada fila, en lugar de recorrer la tabla por cada CHECK al final.
CHECK constraints with functions PostgreSQL lacksno se creaUn CHECK que no se puede escribir para PostgreSQL se deja fuera y aparece en el informe.check_skipped
CHECK ... NOT ENFORCEDno se creaMySQL conserva estos CHECK sin aplicarlos, así que las filas pueden no cumplirlos.check_not_enforced
defaults with functions PostgreSQL lackssin valor predeterminadoUn valor predeterminado que no se puede escribir para PostgreSQL se deja fuera y aparece en el informe.default_not_translated
keys and indexes on a prefix, c(20)un índice sobre left(c, 20)PostgreSQL indexa expresiones, así que se indexa el mismo prefijo.prefix_unique
FULLTEXT indexesGIN sobre to_tsvector('simple', ...)La búsqueda de texto completo funciona con @@ en lugar de MATCH ... AGAINST.fulltext_as_gin
indexes on values over 2,000 byteshashUna entrada de btree no puede contener valores tan largos; un índice hash sigue encontrando valores iguales.index_on_wide_values
SPATIAL indexesno se crea sin PostGISLos índices espaciales necesitan PostGIS.spatial_index_skipped
functional indexes PostgreSQL can't writeno se creaUn índice sobre una expresión que no se puede escribir para PostgreSQL se deja fuera y aparece en el informe.index_skipped
foreign keysdespués de los datos, NOT VALID, luego validada con una advertencia si hay huérfanasLos volcados se cargan con FOREIGN_KEY_CHECKS=0 y MyISAM nunca comprueba, así que las filas sin padre son comunes. La clave se crea primero y se valida al final; las filas huérfanas la dejan NOT VALID con una advertencia en lugar de detener la carga.
foreign keys to columns without a unique keyno se creaPostgreSQL necesita una clave única en las columnas referenciadas; InnoDB no.foreign_key_skipped
table and column namesen minúsculas cuando siguen siendo únicosLos nombres en minúsculas se pueden escribir sin comillas en PostgreSQL.identifier_renamed
names over 63 bytesacortado con un hashLos nombres de PostgreSQL admiten 63 bytes; el hash mantiene distintos los nombres acortados.name_shortened
GEOMETRY, POINT, POLYGON...WKB en byteaSin PostGIS, el formato binario estándar (well-known binary) conserva cada geometría con exactitud.geometry_as_wkb
INET4, INET6, UUID (MariaDB)inet, uuidPostgreSQL tiene los mismos tipos.
BINARY, VARBINARY, BLOBbyteaLos bytes siguen siendo bytes.
PARTITION BYuna sola tablaLas filas son las mismas; vuelve a particionar la tabla en PostgreSQL si lo necesita.partitions_dropped
WITH SYSTEM VERSIONING (MariaDB)una tabla con las filas actualesUn volcado solo contiene las filas actuales.system_versioning_dropped
MyISAM, MEMORY and other enginesuna tabla normalPostgreSQL tiene un solo motor de tablas.engine_dropped
views, triggers, procedures, functions and eventslistados en el informeSu código está escrito en el lenguaje de MySQL, así que se listan para que los vuelvas a crear.not_converted_objects
dumps without CREATE TABLEtipos leídos de los valoresUn volcado hecho con --no-create-info solo tiene filas, así que los tipos salen de los valores.no_schema
geometries in a dump without CREATE TABLEWKB en byteaEl formato de geometría de MySQL se reconoce por los valores y se guarda como WKB estándar.geometry_inferred
_ci collationscomparación exactaPostgreSQL compara el texto exactamente, salvo que se le indique otra cosa.case_insensitive_collation
SET NAMES utf8 from utf8mb4 tablesse convierte, con una advertenciaEl mysqlvolcado de MySQL 5.x escribe utf8 de 3 bytes por defecto y reemplaza cada emoji por un signo de interrogación antes de que cualquier convertidor vea el archivo.dump_lost_four_byte_chars
dumps without SET NAMES whose text isn't UTF-8se lee como Latin-1 (Windows-1252)El latin1 de MySQL es Windows-1252, la codificación habitual de esos volcados.charset_guessed
sjis, cp932, gbk, big5 dumpsse lee como UTF-8, con una advertenciaEstas codificaciones todavía no se decodifican.unsupported_charset
UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rowsse evalúanAlgunas herramientas de volcado escriben los valores como estas llamadas; se leen como los valores que producen, con el WKT geográfico convertido para que la longitud vaya primero.
other expressions in rowsNULLUn convertidor que no ejecuta una base de datos no puede evaluarlas.expression_value
DEFAULT in VALUESel valor predeterminado de la columnaUn valor predeterminado literal se copia en la fila.default_keyword_filled
DEFAULT in VALUES without a literal defaultNULLNo hay ningún valor que copiar.default_keyword_null
REPLACE and INSERT IGNOREfilas simplesUn volcado tiene una fila por clave, así que las filas son las mismas.replace_as_insert
types IntoSQL doesn't knowtextEl texto admite cualquier valor; cambia el tipo después de cargar.unknown_type
values that don't fit their columnNULLUn volcado dañado o escrito a mano puede contener valores que su columna no admite; se vuelven NULL para que se cargue el resto.invalid_value_null
unreadable geometriesNULLLos bytes que no son una geometría no se pueden guardar como una.bad_geometry
CREATE SEQUENCE (MariaDB)CREATE SEQUENCE con su valor actualLas secuencias de PostgreSQL aceptan las mismas opciones.

Carga el script en PostgreSQL

El script crea las tablas, copia las filas y luego agrega claves, índices y claves foráneas, así que carga rápido y se detiene en el primer error.

psql -v ON_ERROR_STOP=1 -d mydb -f mysql.postgresql.sql

Las claves foráneas se agregan como NOT VALID y luego se validan. Si una fila apunta a una fila que no existe, el script te dice qué restricción es, en lugar de fallar a medias.

Para un volcado grande, crea primero la base de datos y ejecuta el script con psql desde la misma máquina que el servidor.

Haz el volcado de MySQL para el mejor resultado

El convertidor lee lo que contiene el volcado. Estas opciones conservan más de tus datos en él:

  • --default-character-set=utf8mb4 conserva los emoji y otros caracteres de 4 bytes, que un volcado utf8 pierde antes de que el convertidor lo vea.
  • --hex-blob escribe las columnas binarias en hexadecimal, que sobrevive a la copia entre sistemas.
  • --routines --triggers --events agregan esos objetos al volcado, para que el informe pueda listarlos.

Qué no se convierte

Las vistas, los disparadores, los procedimientos almacenados, las funciones y los eventos se escriben en el lenguaje propio de cada base de datos. Aparecen en el informe por nombre, nunca se descartan en silencio, para que puedas reescribirlos para PostgreSQL.

Comparado con pgloader

Los mismos volcados, convertidos y cargados por IntoSQL y por pgloader 3.6.7 en una sola máquina (un i9-10900K de 10 núcleos con un SATA SSD), en octubre de 2026.

  • Una columna TINYINT(1) con 2, -1 y -5: pgloader carga true en cada una, sin advertencia. IntoSQL conserva los números y el informe dice por qué.
  • Un volcado con un valor predeterminado 'it's', una tabla con una comilla invertida en el nombre, un ENUM con el valor vacío y un TIME de más de 24 horas: pgloader se detiene. IntoSQL lo convierte y lo carga.
  • Lo que carga pgloader pierde sus claves primarias, únicas y foráneas, y sus identidades. IntoSQL las conserva todas.
  • Un volcado de 5.3 GB con 30 millones de filas: pgloader necesita restaurarlo antes en un MySQL en ejecución, lo que tarda 18 minutos, y luego se queda sin memoria con su configuración predeterminada. IntoSQL convierte el volcado en 10 segundos, y psql carga el script en unos 3 minutos.
  • En 476 volcados de MySQL 5.5 a 9 y MariaDB 10.3 a 12, cargados en PostgreSQL 13 y 18: ningún error de carga, y ningún valor cambió sin una línea en el informe.

Preguntas sobre MySQL a PostgreSQL

¿No encuentras la respuesta? Escríbenos desde la página de contacto y te responde una persona. Página de contacto

¿Se sube mi volcado?

No. El convertidor se ejecuta en tu navegador y lee el volcado de tu disco por partes, así que nunca sale de tu computadora. La app de escritorio y la línea de comandos también convierten en tu computadora.

¿Qué tan grande puede ser el volcado?

El convertidor nunca tiene todo el volcado en memoria: lee el archivo dos veces, por rangos, y escribe el script sobre la marcha. Los volcados de varios gigabytes se convierten en el navegador, en la app de escritorio y con la línea de comandos.

¿Lee volcados de MariaDB?

Sí. Los volcados de mariadb-dump y de MariaDB 10.3 a 12 se convierten de la misma forma, incluidas las columnas INET6, INET4 y UUID de MariaDB.

¿Qué pasa con las fechas 0000-00-00?

PostgreSQL no tiene fecha cero, así que se vuelven NULL y la columna acepta NULL. El informe dice cuántas había en cada columna.

¿Se conservan los valores AUTO_INCREMENT?

Sí. Cada columna AUTO_INCREMENT se vuelve una columna de identidad, cada fila conserva su id, y el siguiente id sigue después del más alto, o después del AUTO_INCREMENT de la tabla si es mayor.

¿TINYINT(1) se vuelve boolean?

Solo cuando cada valor de la columna es 0 o 1. Si contiene otros números, como 2 o -1, sigue siendo un número y el informe dice por qué, así que ningún valor se vuelve true por accidente.

¿Qué pasa con los procedimientos almacenados y los disparadores?

Aparecen en el informe por nombre y no se convierten, porque cada base de datos los escribe en su propio lenguaje. ON UPDATE CURRENT_TIMESTAMP es la excepción: se vuelve un disparador de PostgreSQL.

¿En qué versiones de PostgreSQL carga el script?

PostgreSQL 13 a 18. El mismo script carga en cada una, y se detiene en el primer error si algo sale mal.