Vai al contenuto

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.

Il tuo file
sqlshop_2024.sqlFile di esempio, 5,1 kB
Il tuo database

18 righe convertite, 8 correzioni, nessuna rifiutata

Mostra

Tabelle in shop_2024.sqlDump MySQL 8.0.46 · scritto da mysqldump · utf8mb4

  • customers8 righe3
    7 colonne
    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 righe5
    9 colonne
    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();

Correzioni

  1. "id" di customers è senza segno, quindi è diventata bigint con un vincolo CHECK che non sia negativa.
  2. "signed_up" di customers ora ammette NULL: alcuni dei suoi valori non si potevano salvare e sono NULL.
  3. 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.
  4. "customer_id" di orders è senza segno, quindi è diventata bigint con un vincolo CHECK che non sia negativa.
  5. "id" di orders è senza segno, quindi è diventata bigint con un vincolo CHECK che non sia negativa.
  6. "items" di orders è senza segno, quindi è diventata integer con un vincolo CHECK che non sia negativa.
  7. 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.
  8. "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 MySQLIn PostgreSQLRiga del report
TINYINT(1), BOOLboolean 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 integersl'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 widthsrimossoIl 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 NULLNOT 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
TIMEtime 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
YEARsmallint 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.
DATETIMEtimestampUn DATETIME è l'ora letta su un orologio, senza fuso orario, come il timestamp di PostgreSQL.
TIMESTAMPtimestamptz 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
ENUMtext 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 listnessun CHECKI valori che la lista non nomina violerebbero il vincolo CHECK, quindi il vincolo non viene creato.enum_check_skipped
SETtext[] con un CHECK che ogni elemento sia nella listaUn array tiene separato ogni elemento, così le query possono verificarne l'appartenenza.
JSONjsonb; 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 textrimossoIl 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 seti 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_INCREMENTGENERATED 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 keyun 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_TIMESTAMPun 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 columnsSTOREDPostgreSQL dalla 13 alla 17 salva solo colonne generate; la formula è la stessa.virtual_as_stored
generated columns with functions PostgreSQL lacksuna colonna sempliceUna formula che non si può scrivere per PostgreSQL lascia una colonna semplice e una riga nel report.generated_not_translated
INVISIBLE columnscolonne normaliPostgreSQL non ha colonne invisibili.invisible_column
CHECK constraintstradotti, 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 lacksnon creatoUn vincolo CHECK che non si può scrivere per PostgreSQL viene omesso e segnalato.check_skipped
CHECK ... NOT ENFORCEDnon creatiMySQL conserva questi vincoli senza applicarli, quindi le righe possono violarli.check_not_enforced
defaults with functions PostgreSQL lacksnessun 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 indexesGIN su to_tsvector('simple', ...)La ricerca full-text funziona con @@ invece di MATCH ... AGAINST.fulltext_as_gin
indexes on values over 2,000 byteshashUna voce di btree non può contenere valori così lunghi; un indice hash trova comunque i valori uguali.index_on_wide_values
SPATIAL indexesnon creati senza PostGISGli indici spaziali richiedono PostGIS.spatial_index_skipped
functional indexes PostgreSQL can't writenon creatoUn indice su un'espressione che non si può scrivere per PostgreSQL viene omesso e segnalato.index_skipped
foreign keysdopo 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 keynon creataPostgreSQL richiede una chiave univoca sulle colonne referenziate; InnoDB no.foreign_key_skipped
table and column namesminuscolo quando resta univocoIn PostgreSQL i nomi in minuscolo si possono scrivere senza virgolette.identifier_renamed
names over 63 bytesaccorciato 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, BLOBbyteaI byte restano byte.
PARTITION BYuna 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 enginesuna tabella normalePostgreSQL ha un solo motore di tabelle.engine_dropped
views, triggers, procedures, functions and eventselencati nel reportIl loro codice è scritto nel linguaggio di MySQL, quindi vengono elencati perché tu li ricrei.not_converted_objects
dumps without CREATE TABLEtipi 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 TABLEWKB dentro byteaIl formato delle geometrie di MySQL viene riconosciuto dai valori e salvato come WKB standard.geometry_inferred
_ci collationsconfronto esattoPostgreSQL confronta il testo in modo esatto, salvo indicazione diversa.case_insensitive_collation
SET NAMES utf8 from utf8mb4 tablesconvertito, 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-8letto come Latin-1 (Windows-1252)Il latin1 di MySQL è Windows-1252, la codifica abituale di questi dump.charset_guessed
sjis, cp932, gbk, big5 dumpsletto come UTF-8, con un avvisoQueste codifiche non vengono ancora decodificate.unsupported_charset
UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rowsvalutateAlcuni 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 rowsNULLUn convertitore che non esegue un database non può valutarle.expression_value
DEFAULT in VALUESil valore predefinito della colonnaUn valore predefinito letterale viene copiato nella riga.default_keyword_filled
DEFAULT in VALUES without a literal defaultNULLNon c'è nessun valore da copiare.default_keyword_null
REPLACE and INSERT IGNORErighe sempliciUn dump ha una riga per chiave, quindi le righe sono le stesse.replace_as_insert
types IntoSQL doesn't knowtextIl testo contiene qualsiasi valore; cambia il tipo dopo il caricamento.unknown_type
values that don't fit their columnNULLUn 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 geometriesNULLI 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.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

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.