---
title: "Convertire MySQL in PostgreSQL"
description: "Converti un dump MySQL o MariaDB in uno script PostgreSQL che si carica al primo tentativo, con un report di ogni modifica. Nel browser: il dump resta sul tuo computer."
canonical: https://intosql.com/it/mysql-in-postgresql
last_updated: 2026-10-15
---

# Convertire MySQL in PostgreSQL

Trascina un dump di mysqldump, mariadb-dump, mysqlpump, phpMyAdmin o Adminer, in .sql o .sql.gz, e scarica uno script PostgreSQL con le stesse tabelle, chiavi, indici e dati. Ogni valore che PostgreSQL non può contenere così com'è, e ogni oggetto che non supporta, è elencato nel report.

## Cosa cambia da MySQL a PostgreSQL

Ogni riga è una regola che il convertitore applica, con la riga del report che scrive quando la regola cambia qualcosa nei tuoi dati.

| In MySQL | In PostgreSQL | Riga del report |
| --- | --- | --- |
| TINYINT(1), BOOL | boolean quando ogni valore è 0 o 1, altrimenti smallint | `bool_kept_numeric` |
| UNSIGNED integers | l'intero più ampio successivo con un CHECK che non sia negativo; BIGINT UNSIGNED è bigint quando ogni valore ci sta, altrimenti numeric(20,0) | `unsigned_widened` |
| ZEROFILL and display widths | rimosso | `zerofill_dropped` |
| FLOAT(M,D) | real o double precision, senza la scala | `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 rimosso | `not_null_relaxed` |
| DEFAULT '0000-00-00' | nessun valore predefinito | `zero_date_default` |
| TIME | time quando ogni valore sta in un giorno, altrimenti interval | `time_as_interval` |
| YEAR | smallint con un CHECK del suo intervallo |  |
| CHAR(n) | varchar(n) |  |
| DATETIME | timestamp |  |
| TIMESTAMP | timestamptz nel fuso orario indicato dal dump, UTC se non ne indica uno | `timestamp_zone_assumed` |
| ENUM | text con un CHECK dei suoi valori |  |
| ENUM holding '' (MySQL's error value) | il valore vuoto ammesso dal vincolo CHECK | `enum_invalid_value` |
| ENUM or SET holding values outside its list | nessun CHECK | `enum_check_skipped` |
| SET | text[] con un CHECK che ogni elemento sia nella lista |  |
| JSON | jsonb; json quando un valore contiene \u0000 | `json_not_jsonb` |
| NUL bytes in text | rimosso | `nul_removed` |
| text that isn't valid in its character set | i byte errati come carattere di sostituzione | `invalid_utf8_replaced` |
| text decoded twice (Ã±) | copiato così com'è | `mojibake_suspected` |
| AUTO_INCREMENT | GENERATED BY DEFAULT AS IDENTITY, con il contatore impostato oltre gli id copiati |  |
| MyISAM AUTO_INCREMENT on the second column of a key | un solo contatore per la tabella | `myisam_group_autoincrement` |
| ON UPDATE CURRENT_TIMESTAMP | un trigger BEFORE UPDATE | `on_update_trigger` |
| generated columns | STORED | `virtual_as_stored` |
| generated columns with functions PostgreSQL lacks | una colonna semplice | `generated_not_translated` |
| INVISIBLE columns | colonne normali | `invisible_column` |
| CHECK constraints | tradotti, dentro CREATE TABLE |  |
| CHECK constraints with functions PostgreSQL lacks | non creato | `check_skipped` |
| CHECK ... NOT ENFORCED | non creati | `check_not_enforced` |
| defaults with functions PostgreSQL lacks | nessun valore predefinito | `default_not_translated` |
| keys and indexes on a prefix, c(20) | un indice su left(c, 20) | `prefix_unique` |
| FULLTEXT indexes | GIN su to_tsvector('simple', ...) | `fulltext_as_gin` |
| indexes on values over 2,000 bytes | hash | `index_on_wide_values` |
| SPATIAL indexes | non creati senza PostGIS | `spatial_index_skipped` |
| functional indexes PostgreSQL can't write | non creato | `index_skipped` |
| foreign keys | dopo i dati, NOT VALID, poi validata con un avviso sulle righe orfane |  |
| foreign keys to columns without a unique key | non creata | `foreign_key_skipped` |
| table and column names | minuscolo quando resta univoco | `identifier_renamed` |
| names over 63 bytes | accorciato con un hash | `name_shortened` |
| GEOMETRY, POINT, POLYGON... | WKB dentro bytea | `geometry_as_wkb` |
| INET4, INET6, UUID (MariaDB) | inet, uuid |  |
| BINARY, VARBINARY, BLOB | bytea |  |
| PARTITION BY | una sola tabella | `partitions_dropped` |
| WITH SYSTEM VERSIONING (MariaDB) | una tabella con le righe attuali | `system_versioning_dropped` |
| MyISAM, MEMORY and other engines | una tabella normale | `engine_dropped` |
| views, triggers, procedures, functions and events | elencati nel report | `not_converted_objects` |
| dumps without CREATE TABLE | tipi ricavati dai valori | `no_schema` |
| geometries in a dump without CREATE TABLE | WKB dentro bytea | `geometry_inferred` |
| _ci collations | confronto esatto | `case_insensitive_collation` |
| SET NAMES utf8 from utf8mb4 tables | convertito, con un avviso | `dump_lost_four_byte_chars` |
| dumps without SET NAMES whose text isn't UTF-8 | letto come Latin-1 (Windows-1252) | `charset_guessed` |
| sjis, cp932, gbk, big5 dumps | letto come UTF-8, con un avviso | `unsupported_charset` |
| UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows | valutate |  |
| other expressions in rows | NULL | `expression_value` |
| DEFAULT in VALUES | il valore predefinito della colonna | `default_keyword_filled` |
| DEFAULT in VALUES without a literal default | NULL | `default_keyword_null` |
| REPLACE and INSERT IGNORE | righe semplici | `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 con il suo valore corrente |  |

## Carica lo script in PostgreSQL

Lo script crea le tabelle, copia le righe, poi aggiunge chiavi, indici e chiavi esterne, così si carica in fretta e si ferma al primo errore.

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

Le chiavi esterne vengono aggiunte come NOT VALID e poi validate. Se una riga punta a una riga che non esiste, lo script ti dice quale vincolo, invece di fallire a metà.

Per un dump grande, crea prima il database ed esegui lo script con psql dalla stessa macchina del server.

## Fai il dump di MySQL per il miglior risultato

Il convertitore legge ciò che il dump contiene. Con queste opzioni il dump conserva più dei tuoi dati:

- --default-character-set=utf8mb4 conserva emoji e altri caratteri a 4 byte, che un dump utf8 perde prima che il convertitore lo veda.
- --hex-blob scrive le colonne binarie in esadecimale, che resiste alla copia tra sistemi diversi.
- --routines --triggers --events aggiungono quegli oggetti al dump, così il report può elencarli.

## Cosa non viene convertito

Viste, trigger, stored procedure, funzioni ed eventi sono scritti nel linguaggio proprio di ogni database. Sono elencati per nome nel report, mai rimossi in silenzio, così puoi riscriverli per PostgreSQL.

## Confronto misurato con pgloader

Gli stessi dump, convertiti e caricati da IntoSQL e da pgloader 3.6.7 sulla stessa macchina (un i9-10900K a 10 core con un SATA SSD), a ottobre 2026.

- Una colonna TINYINT(1) che contiene 2, -1 e -5: pgloader carica true per ciascuno, senza un avviso. IntoSQL mantiene i numeri e il report spiega perché.
- Un dump con un valore predefinito 'it's', una tabella con un backtick nel nome, un ENUM che contiene il valore vuoto e un TIME oltre le 24 ore: pgloader si ferma. IntoSQL lo converte e lo carica.
- Ciò che pgloader carica ha perso le chiavi primarie, univoche ed esterne e le identity. IntoSQL le mantiene tutte.
- Un dump di 5.3 GB con 30 milioni di righe: pgloader richiede prima di ripristinarlo in un MySQL in esecuzione, il che richiede 18 minuti, poi esaurisce la memoria con le impostazioni predefinite. IntoSQL converte il dump in 10 secondi, e psql carica lo script in circa 3 minuti.
- Su 476 dump da MySQL 5.5 a 9 e da MariaDB 10.3 a 12, caricati in PostgreSQL 13 e 18: nessun errore di caricamento, e nessun valore modificato senza una riga nel report.

## Domande sulla conversione da MySQL a PostgreSQL

### Il mio dump viene caricato?

No. Il convertitore funziona nel tuo browser e legge il dump dal tuo disco a pezzi, quindi non lascia mai il tuo computer. Anche l'app desktop e la riga di comando convertono sul tuo computer.

### Quanto può essere grande il dump?

Il convertitore non tiene mai l'intero dump in memoria: legge il file due volte, per intervalli, e scrive lo script man mano. I dump di più gigabyte si convertono nel browser, nell'app desktop e con la riga di comando.

### Legge i dump di MariaDB?

Sì. I dump di mariadb-dump e di MariaDB dalla 10.3 alla 12 si convertono allo stesso modo, comprese le colonne INET6, INET4 e UUID di MariaDB.

### Cosa succede alle date 0000-00-00?

PostgreSQL non ha una data zero, quindi diventano NULL e la colonna accetta NULL. Il report dice quante erano in ogni colonna.

### I valori AUTO_INCREMENT vengono mantenuti?

Sì. Ogni colonna AUTO_INCREMENT diventa una colonna identity, ogni riga mantiene il suo id e l'id successivo riparte dopo il più alto, o dopo l'AUTO_INCREMENT della tabella se è maggiore.

### TINYINT(1) diventa boolean?

Solo quando ogni valore della colonna è 0 o 1. Se contiene altri numeri, come 2 o -1, resta un numero e il report spiega perché, così nessun valore diventa true per sbaglio.

### E le stored procedure e i trigger?

Sono elencati per nome nel report e non vengono convertiti, perché ogni database li scrive nel proprio linguaggio. ON UPDATE CURRENT_TIMESTAMP è l'eccezione: diventa un trigger di PostgreSQL.

### Su quali versioni di PostgreSQL si carica lo script?

PostgreSQL dalla 13 alla 18. Lo stesso script si carica su ognuna, fermandosi al primo errore se qualcosa va storto.

## Altre conversioni in PostgreSQL

- [Convertire Excel in PostgreSQL](https://intosql.com/it/excel-in-postgresql)
- [Convertire CSV in PostgreSQL](https://intosql.com/it/csv-in-postgresql)
- [Convertire JSON in PostgreSQL](https://intosql.com/it/json-in-postgresql)
