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.
18 righe convertite, 8 correzioni, nessuna rifiutata
Tabelle in shop_2024.sqlDump MySQL 8.0.46 · scritto da mysqldump · utf8mb4
customers8 righe3
7 colonne id bigint not null name varchar(100) not null email varchar(150) not null country varchar(2) not null is_active boolean not null signed_up date created_at timestamptz(0) not null orders10 righe5
9 colonne id bigint not null customer_id bigint not null status text not null total numeric(10,2) not null items integer not null ordered_at timestamp(0) not null shipped_at timestamp(0) note varchar(255) updated_at timestamptz(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();
Correzioni
- "id" di customers è senza segno, quindi è diventata bigint con un vincolo CHECK che non sia negativa.
- "signed_up" di customers ora ammette NULL: alcuni dei suoi valori non si potevano salvare e sono NULL.
- Il valore predefinito di "signed_up" in customers, 0000‑00‑00, non è una data che PostgreSQL accetta, quindi la colonna non ha un valore predefinito.
- "customer_id" di orders è senza segno, quindi è diventata bigint con un vincolo CHECK che non sia negativa.
- "id" di orders è senza segno, quindi è diventata bigint con un vincolo CHECK che non sia negativa.
- "items" di orders è senza segno, quindi è diventata integer con un vincolo CHECK che non sia negativa.
- 1 riga di "status" in orders contiene il valore vuoto che MySQL salva per le voci non valide; il vincolo CHECK sui suoi valori lo ammette.
- "updated_at" di orders passa all'ora corrente a ogni aggiornamento; in PostgreSQL lo fa un trigger.
Da controllare
- 1 valore in "signed_up" di customers è una data zero o impossibile come 0000‑00‑00, che PostgreSQL non può salvare, quindi è NULL. Se quelle date hanno un significato, sostituiscile nell'origine con date reali prima del dump.
- 1 valore in "shipped_at" di orders è una data zero o impossibile come 0000‑00‑00 00:00:00, che PostgreSQL non può salvare, quindi è NULL. Se quelle date hanno un significato, sostituiscile nell'origine con date reali prima del dump.
Il tuo file viene convertito su questo computer e non viene mai caricato.
Altri esempi:
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.
TINYINT(1), BOOL
boolean quando ogni valore è 0 o 1, altrimenti smallint
Il BOOL di MySQL è un TINYINT(1) che può contenere qualsiasi numero da -128 a 127. Una colonna che contiene solo 0 e 1 diventa boolean; una che contiene 2 o -1 resta un numero, così nessun valore cambia significato.
Riga del report: 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)
PostgreSQL non ha interi senza segno. Un tipo più ampio contiene tutto l'intervallo senza segno, e il vincolo CHECK tiene fuori i valori negativi come faceva MySQL.
Riga del report: unsigned_widened
ZEROFILL and display widths
rimosso
Il riempimento con zeri è un modo di mostrare un numero, non ne fa parte. I numeri restano; formattali quando li leggi se il riempimento conta.
Riga del report: zerofill_dropped
FLOAT(M,D)
real o double precision, senza la scala
I float di PostgreSQL non hanno una scala fissa. I valori salvati vengono copiati così come sono.
Riga del report: float_scale_dropped
BIT(1), BIT(n)
boolean, bit(n)
Un singolo bit è un sì o un no; i campi di bit più ampi mantengono ogni bit.
zero, partial or impossible dates (0000-00-00, 2024-02-30)
NULL
PostgreSQL salva solo date reali del calendario. MySQL conserva date zero e impossibili quando la sua modalità SQL lo consente; diventano NULL e il report le conta.
Riga del report: invalid_date_null
NOT NULL columns with values that become NULL
NOT NULL rimosso
Una colonna i cui valori sono dovuti diventare NULL non può mantenere NOT NULL, altrimenti il caricamento si fermerebbe.
Riga del report: not_null_relaxed
DEFAULT '0000-00-00'
nessun valore predefinito
Un valore predefinito che PostgreSQL non può salvare viene omesso invece di essere sostituito con una data inventata.
Riga del report: zero_date_default
TIME
time quando ogni valore sta in un giorno, altrimenti interval
Il TIME di MySQL è una durata da -838:59:59 a 838:59:59. Una colonna i cui valori stanno in un giorno resta un'ora del giorno; una con valori più lunghi o negativi diventa interval.
Riga del report: time_as_interval
YEAR
smallint con un CHECK del suo intervallo
PostgreSQL non ha un tipo anno; il vincolo CHECK mantiene gli stessi valori che MySQL consente.
CHAR(n)
varchar(n)
MySQL rimuove gli spazi finali dei valori CHAR quando li legge, e il char(n) di PostgreSQL li aggiungerebbe di nuovo, quindi varchar(n) mantiene i valori come li restituisce MySQL.
DATETIME
timestamp
Un DATETIME è l'ora letta su un orologio, senza fuso orario, come il timestamp di PostgreSQL.
TIMESTAMP
timestamptz nel fuso orario indicato dal dump, UTC se non ne indica uno
Un TIMESTAMP è un istante. I dump indicano il fuso in cui lo hanno scritto (mysqldump scrive UTC); senza questa indicazione, i valori vengono letti come UTC e il report lo dice.
Riga del report: timestamp_zone_assumed
ENUM
text con un CHECK dei suoi valori
Il testo con un vincolo CHECK mantiene gli stessi valori ed è più facile da modificare di un enum type di PostgreSQL.
ENUM holding '' (MySQL's error value)
il valore vuoto ammesso dal vincolo CHECK
MySQL salva una stringa vuota quando un valore non valido entra in un ENUM con una modalità SQL permissiva. Quelle righe restano e il vincolo CHECK le ammette.
Riga del report: enum_invalid_value
ENUM or SET holding values outside its list
nessun CHECK
I valori che la lista non nomina violerebbero il vincolo CHECK, quindi il vincolo non viene creato.
Riga del report: enum_check_skipped
SET
text[] con un CHECK che ogni elemento sia nella lista
Un array tiene separato ogni elemento, così le query possono verificarne l'appartenenza.
JSON
jsonb; json quando un valore contiene \u0000
jsonb è il tipo che PostgreSQL indicizza e interroga bene, ma rifiuta l'escape \u0000, quindi una colonna che lo contiene resta json.
Riga del report: json_not_jsonb
NUL bytes in text
rimosso
Il testo di PostgreSQL non può contenere un byte NUL. Il byte viene rimosso e il report conta i valori.
Riga del report: nul_removed
text that isn't valid in its character set
i byte errati come carattere di sostituzione
Il testo di PostgreSQL deve essere UTF-8 valido.
Riga del report: invalid_utf8_replaced
text decoded twice (ñ)
copiato così com'è
Il dump contiene esattamente questi caratteri, quindi vengono copiati; il report li segnala.
Riga del report: mojibake_suspected
AUTO_INCREMENT
GENERATED BY DEFAULT AS IDENTITY, con il contatore impostato oltre gli id copiati
Le colonne identity accettano gli id copiati, e il contatore riparte dopo il più alto o dopo l'AUTO_INCREMENT del dump, se è maggiore.
MyISAM AUTO_INCREMENT on the second column of a key
un solo contatore per la tabella
MyISAM conta per gruppo della prima colonna della chiave; PostgreSQL ha un contatore per colonna.
Riga del report: myisam_group_autoincrement
ON UPDATE CURRENT_TIMESTAMP
un trigger BEFORE UPDATE
Il trigger imposta la colonna all'ora corrente quando una riga cambia e l'aggiornamento non l'ha impostata, come fa MySQL.
Riga del report: on_update_trigger
generated columns
STORED
PostgreSQL dalla 13 alla 17 salva solo colonne generate; la formula è la stessa.
Riga del report: virtual_as_stored
generated columns with functions PostgreSQL lacks
una colonna semplice
Una formula che non si può scrivere per PostgreSQL lascia una colonna semplice e una riga nel report.
Riga del report: generated_not_translated
INVISIBLE columns
colonne normali
PostgreSQL non ha colonne invisibili.
Riga del report: invisible_column
CHECK constraints
tradotti, dentro CREATE TABLE
I vincoli CHECK dentro la tabella vengono verificati mentre ogni riga si carica, invece che con una scansione per vincolo alla fine.
CHECK constraints with functions PostgreSQL lacks
non creato
Un vincolo CHECK che non si può scrivere per PostgreSQL viene omesso e segnalato.
Riga del report: check_skipped
CHECK ... NOT ENFORCED
non creati
MySQL conserva questi vincoli senza applicarli, quindi le righe possono violarli.
Riga del report: check_not_enforced
defaults with functions PostgreSQL lacks
nessun valore predefinito
Un valore predefinito che non si può scrivere per PostgreSQL viene omesso e segnalato.
Riga del report: default_not_translated
keys and indexes on a prefix, c(20)
un indice su left(c, 20)
PostgreSQL indicizza le espressioni, quindi viene indicizzato lo stesso prefisso.
Riga del report: prefix_unique
FULLTEXT indexes
GIN su to_tsvector('simple', ...)
La ricerca full-text funziona con @@ invece di MATCH ... AGAINST.
Riga del report: fulltext_as_gin
indexes on values over 2,000 bytes
hash
Una voce di btree non può contenere valori così lunghi; un indice hash trova comunque i valori uguali.
Riga del report: index_on_wide_values
SPATIAL indexes
non creati senza PostGIS
Gli indici spaziali richiedono PostGIS.
Riga del report: spatial_index_skipped
functional indexes PostgreSQL can't write
non creato
Un indice su un'espressione che non si può scrivere per PostgreSQL viene omesso e segnalato.
Riga del report: index_skipped
foreign keys
dopo i dati, NOT VALID, poi validata con un avviso sulle righe orfane
I dump si caricano con FOREIGN_KEY_CHECKS=0 e MyISAM non controlla mai, quindi le righe senza un genitore sono comuni. La chiave viene creata per prima e validata per ultima; le righe orfane la lasciano NOT VALID con un avviso invece di fermare il caricamento.
foreign keys to columns without a unique key
non creata
PostgreSQL richiede una chiave univoca sulle colonne referenziate; InnoDB no.
Riga del report: foreign_key_skipped
table and column names
minuscolo quando resta univoco
In PostgreSQL i nomi in minuscolo si possono scrivere senza virgolette.
Riga del report: identifier_renamed
names over 63 bytes
accorciato con un hash
In PostgreSQL i nomi arrivano a 63 byte; l'hash tiene distinti i nomi accorciati.
Riga del report: name_shortened
GEOMETRY, POINT, POLYGON...
WKB dentro bytea
Senza PostGIS, il formato binario standard WKB conserva ogni geometria in modo esatto.
Riga del report: geometry_as_wkb
INET4, INET6, UUID (MariaDB)
inet, uuid
PostgreSQL ha gli stessi tipi.
BINARY, VARBINARY, BLOB
bytea
I byte restano byte.
PARTITION BY
una sola tabella
Le righe sono le stesse; partiziona di nuovo la tabella in PostgreSQL se serve.
Riga del report: partitions_dropped
WITH SYSTEM VERSIONING (MariaDB)
una tabella con le righe attuali
Un dump contiene solo le righe attuali.
Riga del report: system_versioning_dropped
MyISAM, MEMORY and other engines
una tabella normale
PostgreSQL ha un solo motore di tabelle.
Riga del report: engine_dropped
views, triggers, procedures, functions and events
elencati nel report
Il loro codice è scritto nel linguaggio di MySQL, quindi vengono elencati perché tu li ricrei.
Riga del report: not_converted_objects
dumps without CREATE TABLE
tipi ricavati dai valori
Un dump fatto con --no-create-info contiene solo righe, quindi i tipi vengono dai valori.
Riga del report: no_schema
geometries in a dump without CREATE TABLE
WKB dentro bytea
Il formato delle geometrie di MySQL viene riconosciuto dai valori e salvato come WKB standard.
Riga del report: geometry_inferred
_ci collations
confronto esatto
PostgreSQL confronta il testo in modo esatto, salvo indicazione diversa.
Riga del report: case_insensitive_collation
SET NAMES utf8 from utf8mb4 tables
convertito, con un avviso
mysqldump di MySQL 5.x scrive utf8 a 3 byte per impostazione predefinita e sostituisce ogni emoji con un punto interrogativo prima che un convertitore veda il file.
Riga del report: dump_lost_four_byte_chars
dumps without SET NAMES whose text isn't UTF-8
letto come Latin-1 (Windows-1252)
Il latin1 di MySQL è Windows-1252, la codifica abituale di questi dump.
Riga del report: charset_guessed
sjis, cp932, gbk, big5 dumps
letto come UTF-8, con un avviso
Queste codifiche non vengono ancora decodificate.
Riga del report: unsupported_charset
UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows
valutate
Alcuni strumenti di dump scrivono i valori come queste chiamate; vengono letti come i valori che producono, e il WKT geografico viene convertito mettendo prima la longitudine.
other expressions in rows
NULL
Un convertitore che non esegue un database non può valutarle.
Riga del report: expression_value
DEFAULT in VALUES
il valore predefinito della colonna
Un valore predefinito letterale viene copiato nella riga.
Riga del report: default_keyword_filled
DEFAULT in VALUES without a literal default
NULL
Non c'è nessun valore da copiare.
Riga del report: default_keyword_null
REPLACE and INSERT IGNORE
righe semplici
Un dump ha una riga per chiave, quindi le righe sono le stesse.
Riga del report: replace_as_insert
types IntoSQL doesn't know
text
Il testo contiene qualsiasi valore; cambia il tipo dopo il caricamento.
Riga del report: unknown_type
values that don't fit their column
NULL
Un dump danneggiato o scritto a mano può contenere valori che la colonna non ammette; diventano NULL così il resto si carica.
Riga del report: invalid_value_null
unreadable geometries
NULL
I byte che non sono una geometria non si possono salvare come tale.
Riga del report: bad_geometry
CREATE SEQUENCE (MariaDB)
CREATE SEQUENCE con il suo valore corrente
Le sequenze di PostgreSQL accettano le stesse opzioni.
| In MySQL | In PostgreSQL | Riga del report |
|---|---|---|
| TINYINT(1), BOOL | boolean quando ogni valore è 0 o 1, altrimenti smallintIl BOOL di MySQL è un TINYINT(1) che può contenere qualsiasi numero da -128 a 127. Una colonna che contiene solo 0 e 1 diventa boolean; una che contiene 2 o -1 resta un numero, così nessun valore cambia significato. | 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)PostgreSQL non ha interi senza segno. Un tipo più ampio contiene tutto l'intervallo senza segno, e il vincolo CHECK tiene fuori i valori negativi come faceva MySQL. | unsigned_widened |
| ZEROFILL and display widths | rimossoIl riempimento con zeri è un modo di mostrare un numero, non ne fa parte. I numeri restano; formattali quando li leggi se il riempimento conta. | zerofill_dropped |
| FLOAT(M,D) | real o double precision, senza la scalaI float di PostgreSQL non hanno una scala fissa. I valori salvati vengono copiati così come sono. | float_scale_dropped |
| BIT(1), BIT(n) | boolean, bit(n)Un singolo bit è un sì o un no; i campi di bit più ampi mantengono ogni bit. | |
| zero, partial or impossible dates (0000-00-00, 2024-02-30) | NULLPostgreSQL salva solo date reali del calendario. MySQL conserva date zero e impossibili quando la sua modalità SQL lo consente; diventano NULL e il report le conta. | invalid_date_null |
| NOT NULL columns with values that become NULL | NOT NULL rimossoUna colonna i cui valori sono dovuti diventare NULL non può mantenere NOT NULL, altrimenti il caricamento si fermerebbe. | not_null_relaxed |
| DEFAULT '0000-00-00' | nessun valore predefinitoUn valore predefinito che PostgreSQL non può salvare viene omesso invece di essere sostituito con una data inventata. | zero_date_default |
| TIME | time quando ogni valore sta in un giorno, altrimenti intervalIl TIME di MySQL è una durata da -838:59:59 a 838:59:59. Una colonna i cui valori stanno in un giorno resta un'ora del giorno; una con valori più lunghi o negativi diventa interval. | time_as_interval |
| YEAR | smallint con un CHECK del suo intervalloPostgreSQL non ha un tipo anno; il vincolo CHECK mantiene gli stessi valori che MySQL consente. | |
| CHAR(n) | varchar(n)MySQL rimuove gli spazi finali dei valori CHAR quando li legge, e il char(n) di PostgreSQL li aggiungerebbe di nuovo, quindi varchar(n) mantiene i valori come li restituisce MySQL. | |
| DATETIME | timestampUn DATETIME è l'ora letta su un orologio, senza fuso orario, come il timestamp di PostgreSQL. | |
| TIMESTAMP | timestamptz nel fuso orario indicato dal dump, UTC se non ne indica unoUn TIMESTAMP è un istante. I dump indicano il fuso in cui lo hanno scritto (mysqldump scrive UTC); senza questa indicazione, i valori vengono letti come UTC e il report lo dice. | timestamp_zone_assumed |
| ENUM | text con un CHECK dei suoi valoriIl testo con un vincolo CHECK mantiene gli stessi valori ed è più facile da modificare di un enum type di PostgreSQL. | |
| ENUM holding '' (MySQL's error value) | il valore vuoto ammesso dal vincolo CHECKMySQL salva una stringa vuota quando un valore non valido entra in un ENUM con una modalità SQL permissiva. Quelle righe restano e il vincolo CHECK le ammette. | enum_invalid_value |
| ENUM or SET holding values outside its list | nessun CHECKI valori che la lista non nomina violerebbero il vincolo CHECK, quindi il vincolo non viene creato. | enum_check_skipped |
| SET | text[] con un CHECK che ogni elemento sia nella listaUn array tiene separato ogni elemento, così le query possono verificarne l'appartenenza. | |
| JSON | jsonb; json quando un valore contiene \u0000jsonb è il tipo che PostgreSQL indicizza e interroga bene, ma rifiuta l'escape \u0000, quindi una colonna che lo contiene resta json. | json_not_jsonb |
| NUL bytes in text | rimossoIl testo di PostgreSQL non può contenere un byte NUL. Il byte viene rimosso e il report conta i valori. | nul_removed |
| text that isn't valid in its character set | i byte errati come carattere di sostituzioneIl testo di PostgreSQL deve essere UTF-8 valido. | invalid_utf8_replaced |
| text decoded twice (ñ) | copiato così com'èIl dump contiene esattamente questi caratteri, quindi vengono copiati; il report li segnala. | mojibake_suspected |
| AUTO_INCREMENT | GENERATED BY DEFAULT AS IDENTITY, con il contatore impostato oltre gli id copiatiLe colonne identity accettano gli id copiati, e il contatore riparte dopo il più alto o dopo l'AUTO_INCREMENT del dump, se è maggiore. | |
| MyISAM AUTO_INCREMENT on the second column of a key | un solo contatore per la tabellaMyISAM conta per gruppo della prima colonna della chiave; PostgreSQL ha un contatore per colonna. | myisam_group_autoincrement |
| ON UPDATE CURRENT_TIMESTAMP | un trigger BEFORE UPDATEIl trigger imposta la colonna all'ora corrente quando una riga cambia e l'aggiornamento non l'ha impostata, come fa MySQL. | on_update_trigger |
| generated columns | STOREDPostgreSQL dalla 13 alla 17 salva solo colonne generate; la formula è la stessa. | virtual_as_stored |
| generated columns with functions PostgreSQL lacks | una colonna sempliceUna formula che non si può scrivere per PostgreSQL lascia una colonna semplice e una riga nel report. | generated_not_translated |
| INVISIBLE columns | colonne normaliPostgreSQL non ha colonne invisibili. | invisible_column |
| CHECK constraints | tradotti, dentro CREATE TABLEI vincoli CHECK dentro la tabella vengono verificati mentre ogni riga si carica, invece che con una scansione per vincolo alla fine. | |
| CHECK constraints with functions PostgreSQL lacks | non creatoUn vincolo CHECK che non si può scrivere per PostgreSQL viene omesso e segnalato. | check_skipped |
| CHECK ... NOT ENFORCED | non creatiMySQL conserva questi vincoli senza applicarli, quindi le righe possono violarli. | check_not_enforced |
| defaults with functions PostgreSQL lacks | nessun valore predefinitoUn valore predefinito che non si può scrivere per PostgreSQL viene omesso e segnalato. | default_not_translated |
| keys and indexes on a prefix, c(20) | un indice su left(c, 20)PostgreSQL indicizza le espressioni, quindi viene indicizzato lo stesso prefisso. | prefix_unique |
| FULLTEXT indexes | GIN su to_tsvector('simple', ...)La ricerca full-text funziona con @@ invece di MATCH ... AGAINST. | fulltext_as_gin |
| indexes on values over 2,000 bytes | hashUna voce di btree non può contenere valori così lunghi; un indice hash trova comunque i valori uguali. | index_on_wide_values |
| SPATIAL indexes | non creati senza PostGISGli indici spaziali richiedono PostGIS. | spatial_index_skipped |
| functional indexes PostgreSQL can't write | non creatoUn indice su un'espressione che non si può scrivere per PostgreSQL viene omesso e segnalato. | index_skipped |
| foreign keys | dopo i dati, NOT VALID, poi validata con un avviso sulle righe orfaneI dump si caricano con FOREIGN_KEY_CHECKS=0 e MyISAM non controlla mai, quindi le righe senza un genitore sono comuni. La chiave viene creata per prima e validata per ultima; le righe orfane la lasciano NOT VALID con un avviso invece di fermare il caricamento. | |
| foreign keys to columns without a unique key | non creataPostgreSQL richiede una chiave univoca sulle colonne referenziate; InnoDB no. | foreign_key_skipped |
| table and column names | minuscolo quando resta univocoIn PostgreSQL i nomi in minuscolo si possono scrivere senza virgolette. | identifier_renamed |
| names over 63 bytes | accorciato con un hashIn PostgreSQL i nomi arrivano a 63 byte; l'hash tiene distinti i nomi accorciati. | name_shortened |
| GEOMETRY, POINT, POLYGON... | WKB dentro byteaSenza PostGIS, il formato binario standard WKB conserva ogni geometria in modo esatto. | geometry_as_wkb |
| INET4, INET6, UUID (MariaDB) | inet, uuidPostgreSQL ha gli stessi tipi. | |
| BINARY, VARBINARY, BLOB | byteaI byte restano byte. | |
| PARTITION BY | una sola tabellaLe righe sono le stesse; partiziona di nuovo la tabella in PostgreSQL se serve. | partitions_dropped |
| WITH SYSTEM VERSIONING (MariaDB) | una tabella con le righe attualiUn dump contiene solo le righe attuali. | system_versioning_dropped |
| MyISAM, MEMORY and other engines | una tabella normalePostgreSQL ha un solo motore di tabelle. | engine_dropped |
| views, triggers, procedures, functions and events | elencati nel reportIl loro codice è scritto nel linguaggio di MySQL, quindi vengono elencati perché tu li ricrei. | not_converted_objects |
| dumps without CREATE TABLE | tipi ricavati dai valoriUn dump fatto con --no-create-info contiene solo righe, quindi i tipi vengono dai valori. | no_schema |
| geometries in a dump without CREATE TABLE | WKB dentro byteaIl formato delle geometrie di MySQL viene riconosciuto dai valori e salvato come WKB standard. | geometry_inferred |
| _ci collations | confronto esattoPostgreSQL confronta il testo in modo esatto, salvo indicazione diversa. | case_insensitive_collation |
| SET NAMES utf8 from utf8mb4 tables | convertito, con un avvisomysqldump di MySQL 5.x scrive utf8 a 3 byte per impostazione predefinita e sostituisce ogni emoji con un punto interrogativo prima che un convertitore veda il file. | dump_lost_four_byte_chars |
| dumps without SET NAMES whose text isn't UTF-8 | letto come Latin-1 (Windows-1252)Il latin1 di MySQL è Windows-1252, la codifica abituale di questi dump. | charset_guessed |
| sjis, cp932, gbk, big5 dumps | letto come UTF-8, con un avvisoQueste codifiche non vengono ancora decodificate. | unsupported_charset |
| UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows | valutateAlcuni strumenti di dump scrivono i valori come queste chiamate; vengono letti come i valori che producono, e il WKT geografico viene convertito mettendo prima la longitudine. | |
| other expressions in rows | NULLUn convertitore che non esegue un database non può valutarle. | expression_value |
| DEFAULT in VALUES | il valore predefinito della colonnaUn valore predefinito letterale viene copiato nella riga. | default_keyword_filled |
| DEFAULT in VALUES without a literal default | NULLNon c'è nessun valore da copiare. | default_keyword_null |
| REPLACE and INSERT IGNORE | righe sempliciUn dump ha una riga per chiave, quindi le righe sono le stesse. | replace_as_insert |
| types IntoSQL doesn't know | textIl testo contiene qualsiasi valore; cambia il tipo dopo il caricamento. | unknown_type |
| values that don't fit their column | NULLUn dump danneggiato o scritto a mano può contenere valori che la colonna non ammette; diventano NULL così il resto si carica. | invalid_value_null |
| unreadable geometries | NULLI byte che non sono una geometria non si possono salvare come tale. | bad_geometry |
| CREATE SEQUENCE (MariaDB) | CREATE SEQUENCE con il suo valore correnteLe sequenze di PostgreSQL accettano le stesse opzioni. |
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.
psql -v ON_ERROR_STOP=1 -d mydb -f mysql.postgresql.sqlLe 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
Non trovi la risposta? Scrivici dalla pagina dei contatti e ti risponde una persona. Pagina dei contatti
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.