Pular para o conteúdo

Converter MySQL para PostgreSQL

Solte um dump do mysqldump, mariadb-dump, mysqlpump, phpMyAdmin ou Adminer, em .sql ou .sql.gz, e baixe um script do PostgreSQL com as mesmas tabelas, chaves, índices e dados. Cada valor que o PostgreSQL não aceita como está, e cada objeto que ele não traz, aparece no relatório.

Seu arquivo
sqlshop_2024.sqlArquivo de exemplo, 5,1 kB
Seu banco de dados

18 linhas convertidas, 8 correções, nenhuma rejeitada

Mostrar

Tabelas em shop_2024.sqlDump do MySQL 8.0.46 · gerado por mysqldump · utf8mb4

  • customers8 linhas3
    7 colunas
    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 linhas5
    9 colunas
    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();

Correções

  1. "id" de customers é sem sinal, então virou bigint com um CHECK de que não é negativa.
  2. "signed_up" de customers agora aceita NULL: alguns dos seus valores não puderam ser guardados e são NULL.
  3. O valor padrão de "signed_up" em customers, 0000‑00‑00, não é uma data que o PostgreSQL aceite, então a coluna fica sem valor padrão.
  4. "customer_id" de orders é sem sinal, então virou bigint com um CHECK de que não é negativa.
  5. "id" de orders é sem sinal, então virou bigint com um CHECK de que não é negativa.
  6. "items" de orders é sem sinal, então virou integer com um CHECK de que não é negativa.
  7. 1 linha de "status" em orders contém o valor vazio que o MySQL guarda para entradas inválidas; o CHECK dos valores o permite.
  8. "updated_at" de orders muda para a hora atual a cada atualização; no PostgreSQL, um gatilho faz isso.

Para revisar

  • 1 valor em "signed_up" de customers é uma data zero ou impossível, como 0000‑00‑00, que o PostgreSQL não consegue guardar, então ficou NULL. Se essas datas têm significado, troque-as na origem por datas reais antes do dump.
  • 1 valor em "shipped_at" de orders é uma data zero ou impossível, como 0000‑00‑00 00:00:00, que o PostgreSQL não consegue guardar, então ficou NULL. Se essas datas têm significado, troque-as na origem por datas reais antes do dump.

Seu arquivo é convertido neste computador e nunca é enviado.

Outros exemplos:

O que muda do MySQL para o PostgreSQL

Cada linha é uma regra que o conversor aplica, com a linha do relatório que ele escreve quando a regra muda algo nos seus dados.

  • TINYINT(1), BOOL

    boolean quando todo valor é 0 ou 1, senão smallint

    O BOOL do MySQL é um TINYINT(1) que aceita qualquer número de -128 a 127. Uma coluna que só contém 0 e 1 vira boolean; uma que contém 2 ou -1 continua sendo número, para que nenhum valor mude de significado.

    Linha do relatório: bool_kept_numeric

  • UNSIGNED integers

    o próximo inteiro mais largo com um CHECK de que não é negativo; BIGINT UNSIGNED vira bigint quando todo valor cabe, senão numeric(20,0)

    O PostgreSQL não tem inteiros sem sinal. Um tipo mais largo comporta todo o intervalo sem sinal, e o CHECK impede valores negativos, como o MySQL fazia.

    Linha do relatório: unsigned_widened

  • ZEROFILL and display widths

    descartado

    O preenchimento com zeros é uma forma de exibir um número, não parte dele. Os números são mantidos; formate-os na leitura se o preenchimento for importante.

    Linha do relatório: zerofill_dropped

  • FLOAT(M,D)

    real ou double precision, sem a escala

    Os floats do PostgreSQL não têm escala fixa. Os valores guardados são copiados como estão.

    Linha do relatório: float_scale_dropped

  • BIT(1), BIT(n)

    boolean, bit(n)

    Um único bit é um sim ou não; campos de bits mais largos mantêm todos os bits.

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

    NULL

    O PostgreSQL só guarda datas reais do calendário. O MySQL mantém datas zero e impossíveis quando o modo SQL permite; elas viram NULL e o relatório as conta.

    Linha do relatório: invalid_date_null

  • NOT NULL columns with values that become NULL

    NOT NULL removido

    Uma coluna cujos valores tiveram de virar NULL não pode manter NOT NULL, senão a carga pararia.

    Linha do relatório: not_null_relaxed

  • DEFAULT '0000-00-00'

    sem valor padrão

    Um valor padrão que o PostgreSQL não consegue guardar fica de fora em vez de ser trocado por uma data inventada.

    Linha do relatório: zero_date_default

  • TIME

    time quando todo valor cabe em um dia, senão interval

    O TIME do MySQL é uma duração de -838:59:59 a 838:59:59. Uma coluna cujos valores cabem em um dia continua sendo uma hora do dia; uma com valores maiores ou negativos vira interval.

    Linha do relatório: time_as_interval

  • YEAR

    smallint com um CHECK do intervalo

    O PostgreSQL não tem tipo de ano; o CHECK mantém os mesmos valores que o MySQL permite.

  • CHAR(n)

    varchar(n)

    O MySQL remove os espaços finais dos valores CHAR ao lê-los, e o char(n) do PostgreSQL os acrescentaria de volta, então varchar(n) mantém os valores como o MySQL os retorna.

  • DATETIME

    timestamp

    Um DATETIME é a leitura de um relógio de parede sem fuso horário, como o timestamp do PostgreSQL.

  • TIMESTAMP

    timestamptz no fuso horário que o dump informa, UTC quando ele não informa nenhum

    Um TIMESTAMP é um instante. Os dumps informam o fuso em que foram escritos (o mysqldump escreve em UTC); sem isso, os valores são lidos como UTC e o relatório avisa.

    Linha do relatório: timestamp_zone_assumed

  • ENUM

    text com um CHECK dos valores

    Texto com CHECK mantém os mesmos valores e é mais fácil de mudar do que um enum type do PostgreSQL.

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

    o valor vazio, permitido pelo CHECK

    O MySQL guarda uma string vazia quando um valor inválido entra em um ENUM em um modo SQL permissivo. Essas linhas são mantidas e o CHECK as permite.

    Linha do relatório: enum_invalid_value

  • ENUM or SET holding values outside its list

    sem CHECK

    Valores que a lista não nomeia falhariam no CHECK, então o CHECK não é criado.

    Linha do relatório: enum_check_skipped

  • SET

    text[] com um CHECK de que cada elemento está na lista

    Um array mantém cada elemento separado, então as consultas podem testar se um valor pertence a ele.

  • JSON

    jsonb; json quando um valor contém \u0000

    jsonb é o tipo que o PostgreSQL indexa e consulta bem, mas ele rejeita o escape \u0000, então uma coluna com ele continua json.

    Linha do relatório: json_not_jsonb

  • NUL bytes in text

    removido

    O texto do PostgreSQL não aceita o byte NUL. O byte é removido e o relatório conta os valores.

    Linha do relatório: nul_removed

  • text that isn't valid in its character set

    os bytes inválidos como o caractere de substituição

    O texto do PostgreSQL deve ser UTF-8 válido.

    Linha do relatório: invalid_utf8_replaced

  • text decoded twice (ñ)

    copiado como está

    O dump contém exatamente esses caracteres, então eles são copiados; o relatório os aponta.

    Linha do relatório: mojibake_suspected

  • AUTO_INCREMENT

    GENERATED BY DEFAULT AS IDENTITY, com o contador ajustado depois dos ids copiados

    Colunas identity aceitam os ids copiados, e o contador continua depois do maior deles ou do AUTO_INCREMENT do dump, o que for maior.

  • MyISAM AUTO_INCREMENT on the second column of a key

    um contador para a tabela

    O MyISAM conta por grupo da primeira coluna da chave; o PostgreSQL tem um contador por coluna.

    Linha do relatório: myisam_group_autoincrement

  • ON UPDATE CURRENT_TIMESTAMP

    um gatilho BEFORE UPDATE

    O gatilho define a coluna com a hora atual quando uma linha muda e a atualização não a definiu, como o MySQL faz.

    Linha do relatório: on_update_trigger

  • generated columns

    STORED

    O PostgreSQL 13 a 17 só armazena colunas geradas; a fórmula é a mesma.

    Linha do relatório: virtual_as_stored

  • generated columns with functions PostgreSQL lacks

    uma coluna simples

    Uma fórmula que não pode ser escrita para o PostgreSQL deixa uma coluna simples e uma linha no relatório.

    Linha do relatório: generated_not_translated

  • INVISIBLE columns

    colunas comuns

    O PostgreSQL não tem colunas invisíveis.

    Linha do relatório: invisible_column

  • CHECK constraints

    traduzido, dentro do CREATE TABLE

    Os CHECKs dentro do CREATE TABLE são testados à medida que cada linha carrega, em vez de uma varredura por CHECK depois.

  • CHECK constraints with functions PostgreSQL lacks

    não criado

    Um CHECK que não pode ser escrito para o PostgreSQL fica de fora e é informado no relatório.

    Linha do relatório: check_skipped

  • CHECK ... NOT ENFORCED

    não criado

    O MySQL mantém esses CHECKs sem aplicá-los, então as linhas podem violá-los.

    Linha do relatório: check_not_enforced

  • defaults with functions PostgreSQL lacks

    sem valor padrão

    Um valor padrão que não pode ser escrito para o PostgreSQL fica de fora e é informado no relatório.

    Linha do relatório: default_not_translated

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

    um índice sobre left(c, 20)

    O PostgreSQL indexa expressões, então o mesmo prefixo é indexado.

    Linha do relatório: prefix_unique

  • FULLTEXT indexes

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

    A busca de texto completo funciona com @@ em vez de MATCH ... AGAINST.

    Linha do relatório: fulltext_as_gin

  • indexes on values over 2,000 bytes

    hash

    Uma entrada de btree não comporta valores tão longos; um índice hash ainda encontra valores iguais.

    Linha do relatório: index_on_wide_values

  • SPATIAL indexes

    não criado sem PostGIS

    Índices espaciais precisam do PostGIS.

    Linha do relatório: spatial_index_skipped

  • functional indexes PostgreSQL can't write

    não criado

    Um índice sobre uma expressão que não pode ser escrita para o PostgreSQL fica de fora e é informado no relatório.

    Linha do relatório: index_skipped

  • foreign keys

    depois dos dados, NOT VALID, e então validada com um aviso sobre órfãos

    Os dumps são carregados com FOREIGN_KEY_CHECKS=0 e o MyISAM nunca verifica, então linhas sem pai são comuns. A chave é criada primeiro e validada por último; órfãos a deixam NOT VALID com um aviso, em vez de parar a carga.

  • foreign keys to columns without a unique key

    não criada

    O PostgreSQL exige uma chave única nas colunas referenciadas; o InnoDB não.

    Linha do relatório: foreign_key_skipped

  • table and column names

    em minúsculas quando continua único

    Nomes em minúsculas podem ser escritos sem aspas no PostgreSQL.

    Linha do relatório: identifier_renamed

  • names over 63 bytes

    encurtado com um hash

    Os nomes do PostgreSQL têm até 63 bytes; o hash mantém os nomes encurtados distintos.

    Linha do relatório: name_shortened

  • GEOMETRY, POINT, POLYGON...

    WKB em bytea

    Sem PostGIS, o formato binário padrão WKB (well-known binary) mantém cada geometria exata.

    Linha do relatório: geometry_as_wkb

  • INET4, INET6, UUID (MariaDB)

    inet, uuid

    O PostgreSQL tem os mesmos tipos.

  • BINARY, VARBINARY, BLOB

    bytea

    Bytes continuam sendo bytes.

  • PARTITION BY

    uma tabela

    As linhas são as mesmas; particione a tabela de novo no PostgreSQL se precisar.

    Linha do relatório: partitions_dropped

  • WITH SYSTEM VERSIONING (MariaDB)

    uma tabela com as linhas atuais

    Um dump contém só as linhas atuais.

    Linha do relatório: system_versioning_dropped

  • MyISAM, MEMORY and other engines

    uma tabela comum

    O PostgreSQL tem um único mecanismo de tabelas.

    Linha do relatório: engine_dropped

  • views, triggers, procedures, functions and events

    listados no relatório

    O código deles é escrito na linguagem do MySQL, então eles são listados para você recriá-los.

    Linha do relatório: not_converted_objects

  • dumps without CREATE TABLE

    tipos deduzidos dos valores

    Um dump feito com --no-create-info só tem linhas, então os tipos vêm dos valores.

    Linha do relatório: no_schema

  • geometries in a dump without CREATE TABLE

    WKB em bytea

    O formato de geometria do MySQL é reconhecido pelos valores e guardado como WKB padrão.

    Linha do relatório: geometry_inferred

  • _ci collations

    comparação exata

    O PostgreSQL compara texto de forma exata, a menos que se indique o contrário.

    Linha do relatório: case_insensitive_collation

  • SET NAMES utf8 from utf8mb4 tables

    convertido, com um aviso

    O mysqldump do MySQL 5.x escreve utf8 de 3 bytes por padrão e troca cada emoji por um ponto de interrogação antes que qualquer conversor veja o arquivo.

    Linha do relatório: dump_lost_four_byte_chars

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

    lido como Latin-1 (Windows-1252)

    O latin1 do MySQL é o Windows-1252, a codificação comum desses dumps.

    Linha do relatório: charset_guessed

  • sjis, cp932, gbk, big5 dumps

    lido como UTF-8, com um aviso

    Essas codificações ainda não são decodificadas.

    Linha do relatório: unsupported_charset

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

    avaliadas

    Algumas ferramentas de dump escrevem valores como essas chamadas; elas são lidas como os valores que produzem, com o WKT geográfico convertido para longitude primeiro.

  • other expressions in rows

    NULL

    Um conversor que não executa um banco de dados não consegue avaliá-las.

    Linha do relatório: expression_value

  • DEFAULT in VALUES

    o valor padrão da coluna

    Um valor padrão literal é copiado para a linha.

    Linha do relatório: default_keyword_filled

  • DEFAULT in VALUES without a literal default

    NULL

    Não há valor para copiar.

    Linha do relatório: default_keyword_null

  • REPLACE and INSERT IGNORE

    linhas simples

    Um dump tem uma linha por chave, então as linhas são as mesmas.

    Linha do relatório: replace_as_insert

  • types IntoSQL doesn't know

    text

    O tipo text aceita qualquer valor; mude o tipo depois da carga.

    Linha do relatório: unknown_type

  • values that don't fit their column

    NULL

    Um dump danificado ou escrito à mão pode conter valores que a coluna não aceita; eles viram NULL para que o resto carregue.

    Linha do relatório: invalid_value_null

  • unreadable geometries

    NULL

    Bytes que não são uma geometria não podem ser guardados como uma.

    Linha do relatório: bad_geometry

  • CREATE SEQUENCE (MariaDB)

    CREATE SEQUENCE com o valor atual

    As sequências do PostgreSQL aceitam as mesmas opções.

No MySQLNo PostgreSQLLinha do relatório
TINYINT(1), BOOLboolean quando todo valor é 0 ou 1, senão smallintO BOOL do MySQL é um TINYINT(1) que aceita qualquer número de -128 a 127. Uma coluna que só contém 0 e 1 vira boolean; uma que contém 2 ou -1 continua sendo número, para que nenhum valor mude de significado.bool_kept_numeric
UNSIGNED integerso próximo inteiro mais largo com um CHECK de que não é negativo; BIGINT UNSIGNED vira bigint quando todo valor cabe, senão numeric(20,0)O PostgreSQL não tem inteiros sem sinal. Um tipo mais largo comporta todo o intervalo sem sinal, e o CHECK impede valores negativos, como o MySQL fazia.unsigned_widened
ZEROFILL and display widthsdescartadoO preenchimento com zeros é uma forma de exibir um número, não parte dele. Os números são mantidos; formate-os na leitura se o preenchimento for importante.zerofill_dropped
FLOAT(M,D)real ou double precision, sem a escalaOs floats do PostgreSQL não têm escala fixa. Os valores guardados são copiados como estão.float_scale_dropped
BIT(1), BIT(n)boolean, bit(n)Um único bit é um sim ou não; campos de bits mais largos mantêm todos os bits.
zero, partial or impossible dates (0000-00-00, 2024-02-30)NULLO PostgreSQL só guarda datas reais do calendário. O MySQL mantém datas zero e impossíveis quando o modo SQL permite; elas viram NULL e o relatório as conta.invalid_date_null
NOT NULL columns with values that become NULLNOT NULL removidoUma coluna cujos valores tiveram de virar NULL não pode manter NOT NULL, senão a carga pararia.not_null_relaxed
DEFAULT '0000-00-00'sem valor padrãoUm valor padrão que o PostgreSQL não consegue guardar fica de fora em vez de ser trocado por uma data inventada.zero_date_default
TIMEtime quando todo valor cabe em um dia, senão intervalO TIME do MySQL é uma duração de -838:59:59 a 838:59:59. Uma coluna cujos valores cabem em um dia continua sendo uma hora do dia; uma com valores maiores ou negativos vira interval.time_as_interval
YEARsmallint com um CHECK do intervaloO PostgreSQL não tem tipo de ano; o CHECK mantém os mesmos valores que o MySQL permite.
CHAR(n)varchar(n)O MySQL remove os espaços finais dos valores CHAR ao lê-los, e o char(n) do PostgreSQL os acrescentaria de volta, então varchar(n) mantém os valores como o MySQL os retorna.
DATETIMEtimestampUm DATETIME é a leitura de um relógio de parede sem fuso horário, como o timestamp do PostgreSQL.
TIMESTAMPtimestamptz no fuso horário que o dump informa, UTC quando ele não informa nenhumUm TIMESTAMP é um instante. Os dumps informam o fuso em que foram escritos (o mysqldump escreve em UTC); sem isso, os valores são lidos como UTC e o relatório avisa.timestamp_zone_assumed
ENUMtext com um CHECK dos valoresTexto com CHECK mantém os mesmos valores e é mais fácil de mudar do que um enum type do PostgreSQL.
ENUM holding '' (MySQL's error value)o valor vazio, permitido pelo CHECKO MySQL guarda uma string vazia quando um valor inválido entra em um ENUM em um modo SQL permissivo. Essas linhas são mantidas e o CHECK as permite.enum_invalid_value
ENUM or SET holding values outside its listsem CHECKValores que a lista não nomeia falhariam no CHECK, então o CHECK não é criado.enum_check_skipped
SETtext[] com um CHECK de que cada elemento está na listaUm array mantém cada elemento separado, então as consultas podem testar se um valor pertence a ele.
JSONjsonb; json quando um valor contém \u0000jsonb é o tipo que o PostgreSQL indexa e consulta bem, mas ele rejeita o escape \u0000, então uma coluna com ele continua json.json_not_jsonb
NUL bytes in textremovidoO texto do PostgreSQL não aceita o byte NUL. O byte é removido e o relatório conta os valores.nul_removed
text that isn't valid in its character setos bytes inválidos como o caractere de substituiçãoO texto do PostgreSQL deve ser UTF-8 válido.invalid_utf8_replaced
text decoded twice (ñ)copiado como estáO dump contém exatamente esses caracteres, então eles são copiados; o relatório os aponta.mojibake_suspected
AUTO_INCREMENTGENERATED BY DEFAULT AS IDENTITY, com o contador ajustado depois dos ids copiadosColunas identity aceitam os ids copiados, e o contador continua depois do maior deles ou do AUTO_INCREMENT do dump, o que for maior.
MyISAM AUTO_INCREMENT on the second column of a keyum contador para a tabelaO MyISAM conta por grupo da primeira coluna da chave; o PostgreSQL tem um contador por coluna.myisam_group_autoincrement
ON UPDATE CURRENT_TIMESTAMPum gatilho BEFORE UPDATEO gatilho define a coluna com a hora atual quando uma linha muda e a atualização não a definiu, como o MySQL faz.on_update_trigger
generated columnsSTOREDO PostgreSQL 13 a 17 só armazena colunas geradas; a fórmula é a mesma.virtual_as_stored
generated columns with functions PostgreSQL lacksuma coluna simplesUma fórmula que não pode ser escrita para o PostgreSQL deixa uma coluna simples e uma linha no relatório.generated_not_translated
INVISIBLE columnscolunas comunsO PostgreSQL não tem colunas invisíveis.invisible_column
CHECK constraintstraduzido, dentro do CREATE TABLEOs CHECKs dentro do CREATE TABLE são testados à medida que cada linha carrega, em vez de uma varredura por CHECK depois.
CHECK constraints with functions PostgreSQL lacksnão criadoUm CHECK que não pode ser escrito para o PostgreSQL fica de fora e é informado no relatório.check_skipped
CHECK ... NOT ENFORCEDnão criadoO MySQL mantém esses CHECKs sem aplicá-los, então as linhas podem violá-los.check_not_enforced
defaults with functions PostgreSQL lackssem valor padrãoUm valor padrão que não pode ser escrito para o PostgreSQL fica de fora e é informado no relatório.default_not_translated
keys and indexes on a prefix, c(20)um índice sobre left(c, 20)O PostgreSQL indexa expressões, então o mesmo prefixo é indexado.prefix_unique
FULLTEXT indexesGIN sobre to_tsvector('simple', ...)A busca de texto completo funciona com @@ em vez de MATCH ... AGAINST.fulltext_as_gin
indexes on values over 2,000 byteshashUma entrada de btree não comporta valores tão longos; um índice hash ainda encontra valores iguais.index_on_wide_values
SPATIAL indexesnão criado sem PostGISÍndices espaciais precisam do PostGIS.spatial_index_skipped
functional indexes PostgreSQL can't writenão criadoUm índice sobre uma expressão que não pode ser escrita para o PostgreSQL fica de fora e é informado no relatório.index_skipped
foreign keysdepois dos dados, NOT VALID, e então validada com um aviso sobre órfãosOs dumps são carregados com FOREIGN_KEY_CHECKS=0 e o MyISAM nunca verifica, então linhas sem pai são comuns. A chave é criada primeiro e validada por último; órfãos a deixam NOT VALID com um aviso, em vez de parar a carga.
foreign keys to columns without a unique keynão criadaO PostgreSQL exige uma chave única nas colunas referenciadas; o InnoDB não.foreign_key_skipped
table and column namesem minúsculas quando continua únicoNomes em minúsculas podem ser escritos sem aspas no PostgreSQL.identifier_renamed
names over 63 bytesencurtado com um hashOs nomes do PostgreSQL têm até 63 bytes; o hash mantém os nomes encurtados distintos.name_shortened
GEOMETRY, POINT, POLYGON...WKB em byteaSem PostGIS, o formato binário padrão WKB (well-known binary) mantém cada geometria exata.geometry_as_wkb
INET4, INET6, UUID (MariaDB)inet, uuidO PostgreSQL tem os mesmos tipos.
BINARY, VARBINARY, BLOBbyteaBytes continuam sendo bytes.
PARTITION BYuma tabelaAs linhas são as mesmas; particione a tabela de novo no PostgreSQL se precisar.partitions_dropped
WITH SYSTEM VERSIONING (MariaDB)uma tabela com as linhas atuaisUm dump contém só as linhas atuais.system_versioning_dropped
MyISAM, MEMORY and other enginesuma tabela comumO PostgreSQL tem um único mecanismo de tabelas.engine_dropped
views, triggers, procedures, functions and eventslistados no relatórioO código deles é escrito na linguagem do MySQL, então eles são listados para você recriá-los.not_converted_objects
dumps without CREATE TABLEtipos deduzidos dos valoresUm dump feito com --no-create-info só tem linhas, então os tipos vêm dos valores.no_schema
geometries in a dump without CREATE TABLEWKB em byteaO formato de geometria do MySQL é reconhecido pelos valores e guardado como WKB padrão.geometry_inferred
_ci collationscomparação exataO PostgreSQL compara texto de forma exata, a menos que se indique o contrário.case_insensitive_collation
SET NAMES utf8 from utf8mb4 tablesconvertido, com um avisoO mysqldump do MySQL 5.x escreve utf8 de 3 bytes por padrão e troca cada emoji por um ponto de interrogação antes que qualquer conversor veja o arquivo.dump_lost_four_byte_chars
dumps without SET NAMES whose text isn't UTF-8lido como Latin-1 (Windows-1252)O latin1 do MySQL é o Windows-1252, a codificação comum desses dumps.charset_guessed
sjis, cp932, gbk, big5 dumpslido como UTF-8, com um avisoEssas codificações ainda não são decodificadas.unsupported_charset
UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rowsavaliadasAlgumas ferramentas de dump escrevem valores como essas chamadas; elas são lidas como os valores que produzem, com o WKT geográfico convertido para longitude primeiro.
other expressions in rowsNULLUm conversor que não executa um banco de dados não consegue avaliá-las.expression_value
DEFAULT in VALUESo valor padrão da colunaUm valor padrão literal é copiado para a linha.default_keyword_filled
DEFAULT in VALUES without a literal defaultNULLNão há valor para copiar.default_keyword_null
REPLACE and INSERT IGNORElinhas simplesUm dump tem uma linha por chave, então as linhas são as mesmas.replace_as_insert
types IntoSQL doesn't knowtextO tipo text aceita qualquer valor; mude o tipo depois da carga.unknown_type
values that don't fit their columnNULLUm dump danificado ou escrito à mão pode conter valores que a coluna não aceita; eles viram NULL para que o resto carregue.invalid_value_null
unreadable geometriesNULLBytes que não são uma geometria não podem ser guardados como uma.bad_geometry
CREATE SEQUENCE (MariaDB)CREATE SEQUENCE com o valor atualAs sequências do PostgreSQL aceitam as mesmas opções.

Carregue o script no PostgreSQL

O script cria as tabelas, copia as linhas e depois adiciona chaves, índices e chaves estrangeiras, então carrega rápido e para no primeiro erro.

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

As chaves estrangeiras são adicionadas como NOT VALID e depois validadas. Se uma linha aponta para uma linha que não existe, o script informa qual restrição, em vez de falhar no meio do caminho.

Para um dump grande, crie o banco de dados antes e execute o script com o psql na mesma máquina do servidor.

Gere o dump do MySQL para o melhor resultado

O conversor lê o que o dump contém. Estas opções mantêm mais dos seus dados nele:

  • --default-character-set=utf8mb4 mantém emoji e outros caracteres de 4 bytes, que um dump em utf8 perde antes de o conversor vê-lo.
  • --hex-blob escreve as colunas binárias em hexadecimal, o que sobrevive à cópia entre sistemas.
  • --routines --triggers --events adicionam esses objetos ao dump, para que o relatório possa listá-los.

O que não é convertido

Visões, gatilhos, procedimentos armazenados, funções e eventos são escritos na linguagem própria de cada banco de dados. Eles aparecem no relatório pelo nome, nunca são descartados em silêncio, para que você possa reescrevê-los para o PostgreSQL.

Comparado com o pgloader

Os mesmos dumps, convertidos e carregados pelo IntoSQL e pelo pgloader 3.6.7 em uma única máquina (um i9-10900K de 10 núcleos com SATA SSD), em outubro de 2026.

  • Uma coluna TINYINT(1) com 2, -1 e -5: o pgloader carrega true para cada um, sem aviso. O IntoSQL mantém os números e o relatório explica o motivo.
  • Um dump com um valor padrão 'it's', uma tabela com crase no nome, um ENUM com o valor vazio e um TIME acima de 24 horas: o pgloader para. O IntoSQL o converte e o carrega.
  • O que o pgloader carrega perdeu as chaves primárias, únicas e estrangeiras e as colunas identity. O IntoSQL mantém todas.
  • Um dump de 5.3 GB com 30 milhões de linhas: o pgloader exige restaurá-lo antes em um MySQL em execução, o que leva 18 minutos, e depois fica sem memória com as configurações padrão. O IntoSQL converte o dump em 10 segundos, e o psql carrega o script em cerca de 3 minutos.
  • Em 476 dumps do MySQL 5.5 ao 9 e do MariaDB 10.3 ao 12, carregados no PostgreSQL 13 e 18: nenhum erro de carga e nenhum valor alterado sem uma linha no relatório.

Perguntas sobre MySQL para PostgreSQL

Não encontrou a resposta? Escreva para nós pela página de contato e uma pessoa responde. Página de contato

Meu dump é enviado?

Não. O conversor roda no seu navegador e lê o dump do seu disco em partes, então ele nunca sai do seu computador. O app para desktop e a linha de comando também convertem no seu computador.

Qual o tamanho máximo do dump?

O conversor nunca guarda o dump inteiro na memória: ele lê o arquivo duas vezes, por intervalos, e escreve o script à medida que avança. Dumps de vários gigabytes são convertidos no navegador, no app para desktop e com a linha de comando.

Ele lê dumps do MariaDB?

Sim. Dumps do mariadb-dump e do MariaDB 10.3 ao 12 são convertidos da mesma forma, incluindo as colunas INET6, INET4 e UUID do MariaDB.

O que acontece com datas 0000-00-00?

O PostgreSQL não tem data zero, então elas viram NULL e a coluna passa a aceitar NULL. O relatório diz quantas havia em cada coluna.

Os valores de AUTO_INCREMENT são mantidos?

Sim. Cada coluna AUTO_INCREMENT vira uma coluna identity, cada linha mantém seu id, e o próximo id continua depois do maior, ou depois do AUTO_INCREMENT da tabela, se ele for maior.

TINYINT(1) vira boolean?

Só quando todo valor da coluna é 0 ou 1. Se ela contém outros números, como 2 ou -1, continua sendo número e o relatório explica o motivo, para que nenhum valor vire true por acidente.

E os procedimentos armazenados e gatilhos?

Eles aparecem no relatório pelo nome e não são convertidos, porque cada banco de dados os escreve na sua própria linguagem. ON UPDATE CURRENT_TIMESTAMP é a exceção: vira um gatilho do PostgreSQL.

Em quais versões do PostgreSQL o script carrega?

PostgreSQL 13 a 18. O mesmo script carrega em cada uma delas e para no primeiro erro se algo der errado.