---
title: "Converter MySQL para PostgreSQL"
description: "Converta um dump do MySQL ou MariaDB em um script do PostgreSQL que carrega na primeira tentativa, com um relatório de cada mudança. No navegador, sem enviar o dump."
canonical: https://intosql.com/pt-br/mysql-para-postgresql
last_updated: 2026-10-15
---

# 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.

## 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.

| No MySQL | No PostgreSQL | Linha do relatório |
| --- | --- | --- |
| TINYINT(1), BOOL | boolean quando todo valor é 0 ou 1, senão smallint | `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) | `unsigned_widened` |
| ZEROFILL and display widths | descartado | `zerofill_dropped` |
| FLOAT(M,D) | real ou double precision, sem a escala | `float_scale_dropped` |
| BIT(1), BIT(n) | boolean, bit(n) |  |
| zero, partial or impossible dates (0000-00-00, 2024-02-30) | NULL | `invalid_date_null` |
| NOT NULL columns with values that become NULL | NOT NULL removido | `not_null_relaxed` |
| DEFAULT '0000-00-00' | sem valor padrão | `zero_date_default` |
| TIME | time quando todo valor cabe em um dia, senão interval | `time_as_interval` |
| YEAR | smallint com um CHECK do intervalo |  |
| CHAR(n) | varchar(n) |  |
| DATETIME | timestamp |  |
| TIMESTAMP | timestamptz no fuso horário que o dump informa, UTC quando ele não informa nenhum | `timestamp_zone_assumed` |
| ENUM | text com um CHECK dos valores |  |
| ENUM holding '' (MySQL's error value) | o valor vazio, permitido pelo CHECK | `enum_invalid_value` |
| ENUM or SET holding values outside its list | sem CHECK | `enum_check_skipped` |
| SET | text[] com um CHECK de que cada elemento está na lista |  |
| JSON | jsonb; json quando um valor contém \u0000 | `json_not_jsonb` |
| NUL bytes in text | removido | `nul_removed` |
| text that isn't valid in its character set | os bytes inválidos como o caractere de substituição | `invalid_utf8_replaced` |
| text decoded twice (Ã±) | copiado como está | `mojibake_suspected` |
| AUTO_INCREMENT | GENERATED BY DEFAULT AS IDENTITY, com o contador ajustado depois dos ids copiados |  |
| MyISAM AUTO_INCREMENT on the second column of a key | um contador para a tabela | `myisam_group_autoincrement` |
| ON UPDATE CURRENT_TIMESTAMP | um gatilho BEFORE UPDATE | `on_update_trigger` |
| generated columns | STORED | `virtual_as_stored` |
| generated columns with functions PostgreSQL lacks | uma coluna simples | `generated_not_translated` |
| INVISIBLE columns | colunas comuns | `invisible_column` |
| CHECK constraints | traduzido, dentro do CREATE TABLE |  |
| CHECK constraints with functions PostgreSQL lacks | não criado | `check_skipped` |
| CHECK ... NOT ENFORCED | não criado | `check_not_enforced` |
| defaults with functions PostgreSQL lacks | sem valor padrão | `default_not_translated` |
| keys and indexes on a prefix, c(20) | um índice sobre left(c, 20) | `prefix_unique` |
| FULLTEXT indexes | GIN sobre to_tsvector('simple', ...) | `fulltext_as_gin` |
| indexes on values over 2,000 bytes | hash | `index_on_wide_values` |
| SPATIAL indexes | não criado sem PostGIS | `spatial_index_skipped` |
| functional indexes PostgreSQL can't write | não criado | `index_skipped` |
| foreign keys | depois dos dados, NOT VALID, e então validada com um aviso sobre órfãos |  |
| foreign keys to columns without a unique key | não criada | `foreign_key_skipped` |
| table and column names | em minúsculas quando continua único | `identifier_renamed` |
| names over 63 bytes | encurtado com um hash | `name_shortened` |
| GEOMETRY, POINT, POLYGON... | WKB em bytea | `geometry_as_wkb` |
| INET4, INET6, UUID (MariaDB) | inet, uuid |  |
| BINARY, VARBINARY, BLOB | bytea |  |
| PARTITION BY | uma tabela | `partitions_dropped` |
| WITH SYSTEM VERSIONING (MariaDB) | uma tabela com as linhas atuais | `system_versioning_dropped` |
| MyISAM, MEMORY and other engines | uma tabela comum | `engine_dropped` |
| views, triggers, procedures, functions and events | listados no relatório | `not_converted_objects` |
| dumps without CREATE TABLE | tipos deduzidos dos valores | `no_schema` |
| geometries in a dump without CREATE TABLE | WKB em bytea | `geometry_inferred` |
| _ci collations | comparação exata | `case_insensitive_collation` |
| SET NAMES utf8 from utf8mb4 tables | convertido, com um aviso | `dump_lost_four_byte_chars` |
| dumps without SET NAMES whose text isn't UTF-8 | lido como Latin-1 (Windows-1252) | `charset_guessed` |
| sjis, cp932, gbk, big5 dumps | lido como UTF-8, com um aviso | `unsupported_charset` |
| UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows | avaliadas |  |
| other expressions in rows | NULL | `expression_value` |
| DEFAULT in VALUES | o valor padrão da coluna | `default_keyword_filled` |
| DEFAULT in VALUES without a literal default | NULL | `default_keyword_null` |
| REPLACE and INSERT IGNORE | linhas simples | `replace_as_insert` |
| types IntoSQL doesn't know | text | `unknown_type` |
| values that don't fit their column | NULL | `invalid_value_null` |
| unreadable geometries | NULL | `bad_geometry` |
| CREATE SEQUENCE (MariaDB) | CREATE SEQUENCE com o valor atual |  |

## 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.

```shell
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

### 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.

## Outras conversões para PostgreSQL

- [Converter Excel para PostgreSQL](https://intosql.com/pt-br/excel-para-postgresql)
- [Converter CSV para PostgreSQL](https://intosql.com/pt-br/csv-para-postgresql)
- [Converter JSON para PostgreSQL](https://intosql.com/pt-br/json-para-postgresql)
