Naar de inhoud

MySQL omzetten naar PostgreSQL

Zet een dump van mysqldump, mariadb-dump, mysqlpump, phpMyAdmin of Adminer neer, als .sql of .sql.gz, en download een PostgreSQL-script met dezelfde tabellen, sleutels, indexen en data. Elke waarde die PostgreSQL niet ongewijzigd kan bevatten, en elk object dat het niet overneemt, staat in het rapport.

Je bestand
sqlshop_2024.sqlVoorbeeldbestand, 5,1 kB
Je database

18 rijen geconverteerd, 8 correcties, geen afgewezen

Tonen

Tabellen in shop_2024.sqlMySQL 8.0.46-dump · geschreven door mysqldump · utf8mb4

  • customers8 rijen3
    7 kolommen
    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 rijen5
    9 kolommen
    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();

Correcties

  1. "id" van customers is unsigned, dus het is bigint geworden met een check dat het niet negatief is.
  2. "signed_up" van customers staat nu NULL toe: sommige waarden konden niet worden opgeslagen en zijn NULL.
  3. De standaardwaarde van "signed_up" in customers, 0000‑00‑00, is geen datum die PostgreSQL accepteert, dus de kolom heeft geen standaardwaarde.
  4. "customer_id" van orders is unsigned, dus het is bigint geworden met een check dat het niet negatief is.
  5. "id" van orders is unsigned, dus het is bigint geworden met een check dat het niet negatief is.
  6. "items" van orders is unsigned, dus het is integer geworden met een check dat het niet negatief is.
  7. 1 rij van "status" in orders bevat de lege waarde die MySQL opslaat voor ongeldige invoer; de check op de waarden staat die toe.
  8. "updated_at" van orders verandert bij elke update in de huidige tijd; in PostgreSQL doet een trigger dat.

Na te kijken

  • 1 waarde in "signed_up" van customers is een nul- of onmogelijke datum zoals 0000‑00‑00, die PostgreSQL niet kan opslaan, dus die is NULL. Als die datums iets betekenen, vervang ze dan in de bron door echte datums voordat je de dump maakt.
  • 1 waarde in "shipped_at" van orders is een nul- of onmogelijke datum zoals 0000‑00‑00 00:00:00, die PostgreSQL niet kan opslaan, dus die is NULL. Als die datums iets betekenen, vervang ze dan in de bron door echte datums voordat je de dump maakt.

Je bestand wordt op deze computer omgezet en nooit geüpload.

Andere voorbeelden:

Wat er verandert van MySQL naar PostgreSQL

Elke rij is een regel die de converter toepast, met de rapportregel die hij schrijft als de regel iets in je data verandert.

  • TINYINT(1), BOOL

    boolean als elke waarde 0 of 1 is, anders smallint

    BOOL in MySQL is een TINYINT(1) die elk getal van -128 tot 127 kan bevatten. Een kolom met alleen 0 en 1 wordt boolean; een kolom met 2 of -1 blijft een getal, zodat geen waarde van betekenis verandert.

    Rapportregel: bool_kept_numeric

  • UNSIGNED integers

    het eerstvolgende bredere gehele getal met een CHECK dat het niet negatief is; BIGINT UNSIGNED wordt bigint als elke waarde past, anders numeric(20,0)

    PostgreSQL heeft geen unsigned gehele getallen. Een breder type bevat het hele unsigned bereik, en de check houdt negatieve waarden buiten, zoals MySQL deed.

    Rapportregel: unsigned_widened

  • ZEROFILL and display widths

    weggelaten

    Opvulling met nullen is een manier om een getal te tonen, geen deel ervan. De getallen blijven; formatteer ze bij het lezen als de opvulling ertoe doet.

    Rapportregel: zerofill_dropped

  • FLOAT(M,D)

    real of double precision, zonder de schaal

    Drijvendekommagetallen in PostgreSQL hebben geen vaste schaal. De opgeslagen waarden worden ongewijzigd gekopieerd.

    Rapportregel: float_scale_dropped

  • BIT(1), BIT(n)

    boolean, bit(n)

    Eén bit is een ja of nee; bredere bitvelden behouden elke bit.

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

    NULL

    PostgreSQL slaat alleen echte kalenderdatums op. MySQL bewaart nul- en onmogelijke datums als de SQL-modus dat toestaat; ze worden NULL en het rapport telt ze.

    Rapportregel: invalid_date_null

  • NOT NULL columns with values that become NULL

    NOT NULL weggelaten

    Een kolom waarvan waarden NULL moesten worden, kan NOT NULL niet houden, anders zou het laden stoppen.

    Rapportregel: not_null_relaxed

  • DEFAULT '0000-00-00'

    geen standaardwaarde

    Een standaardwaarde die PostgreSQL niet kan opslaan, wordt weggelaten in plaats van vervangen door een verzonnen datum.

    Rapportregel: zero_date_default

  • TIME

    time als elke waarde binnen een dag valt, anders interval

    TIME in MySQL is een duur van -838:59:59 tot 838:59:59. Een kolom waarvan de waarden binnen een dag passen, blijft een tijdstip; een kolom met langere of negatieve waarden wordt interval.

    Rapportregel: time_as_interval

  • YEAR

    smallint met een CHECK op het bereik

    PostgreSQL heeft geen jaartype; de check houdt dezelfde waarden die MySQL toestaat.

  • CHAR(n)

    varchar(n)

    MySQL verwijdert de spaties aan het einde van CHAR-waarden bij het lezen, en char(n) in PostgreSQL zou ze weer toevoegen, dus varchar(n) houdt de waarden zoals MySQL ze teruggeeft.

  • DATETIME

    timestamp

    Een DATETIME is een kloktijd zonder tijdzone, net als timestamp in PostgreSQL.

  • TIMESTAMP

    timestamptz in de tijdzone die de dump vermeldt, UTC als die er geen vermeldt

    Een TIMESTAMP is een moment. Dumps vermelden de zone waarin ze het schreven (mysqldump schrijft UTC); zonder die vermelding worden de waarden gelezen als UTC en meldt het rapport dat.

    Rapportregel: timestamp_zone_assumed

  • ENUM

    text met een CHECK op de waarden

    Text met een check houdt dezelfde waarden en is makkelijker te wijzigen dan een enum type in PostgreSQL.

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

    de lege waarde, toegestaan door de check

    MySQL slaat een lege string op als er in een tolerante SQL-modus een ongeldige waarde in een ENUM komt. Die rijen blijven behouden en de check staat ze toe.

    Rapportregel: enum_invalid_value

  • ENUM or SET holding values outside its list

    geen CHECK

    Waarden die niet in de lijst staan, zouden de check laten mislukken, dus de check wordt niet aangemaakt.

    Rapportregel: enum_check_skipped

  • SET

    text[] met een CHECK dat elk element in de lijst staat

    Een array houdt elk element apart, zodat query's op lidmaatschap kunnen testen.

  • JSON

    jsonb; json als een waarde \u0000 bevat

    jsonb is het type dat PostgreSQL goed indexeert en bevraagt, maar het weigert de escape \u0000, dus een kolom daarmee blijft json.

    Rapportregel: json_not_jsonb

  • NUL bytes in text

    verwijderd

    Tekst in PostgreSQL kan geen NUL-byte bevatten. De byte wordt verwijderd en het rapport telt de waarden.

    Rapportregel: nul_removed

  • text that isn't valid in its character set

    de foute bytes als het vervangingsteken

    Tekst in PostgreSQL moet geldige UTF-8 zijn.

    Rapportregel: invalid_utf8_replaced

  • text decoded twice (ñ)

    ongewijzigd gekopieerd

    De dump bevat precies deze tekens, dus ze worden gekopieerd; het rapport wijst ze aan.

    Rapportregel: mojibake_suspected

  • AUTO_INCREMENT

    GENERATED BY DEFAULT AS IDENTITY, met de teller voorbij de gekopieerde id's

    Identity-kolommen accepteren de gekopieerde id's, en de teller gaat verder na de hoogste daarvan of na de AUTO_INCREMENT van de dump, welke hoger is.

  • MyISAM AUTO_INCREMENT on the second column of a key

    één teller voor de tabel

    MyISAM telt per groep van de eerste kolom van de sleutel; PostgreSQL heeft één teller per kolom.

    Rapportregel: myisam_group_autoincrement

  • ON UPDATE CURRENT_TIMESTAMP

    een BEFORE UPDATE-trigger

    De trigger zet de kolom op de huidige tijd als een rij verandert en de update hem niet instelde, zoals MySQL doet.

    Rapportregel: on_update_trigger

  • generated columns

    STORED

    PostgreSQL 13 tot en met 17 slaat gegenereerde kolommen alleen op; de formule is dezelfde.

    Rapportregel: virtual_as_stored

  • generated columns with functions PostgreSQL lacks

    een gewone kolom

    Een formule die niet voor PostgreSQL kan worden geschreven, levert een gewone kolom en een regel in het rapport op.

    Rapportregel: generated_not_translated

  • INVISIBLE columns

    gewone kolommen

    PostgreSQL heeft geen onzichtbare kolommen.

    Rapportregel: invisible_column

  • CHECK constraints

    vertaald, inline in CREATE TABLE

    Inline checks worden getest terwijl elke rij laadt, in plaats van achteraf in een scan per check.

  • CHECK constraints with functions PostgreSQL lacks

    niet aangemaakt

    Een check die niet voor PostgreSQL kan worden geschreven, wordt weggelaten en gemeld.

    Rapportregel: check_skipped

  • CHECK ... NOT ENFORCED

    niet aangemaakt

    MySQL bewaart deze checks zonder ze toe te passen, dus de rijen kunnen ze schenden.

    Rapportregel: check_not_enforced

  • defaults with functions PostgreSQL lacks

    geen standaardwaarde

    Een standaardwaarde die niet voor PostgreSQL kan worden geschreven, wordt weggelaten en gemeld.

    Rapportregel: default_not_translated

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

    een index op left(c, 20)

    PostgreSQL indexeert expressies, dus hetzelfde prefix wordt geïndexeerd.

    Rapportregel: prefix_unique

  • FULLTEXT indexes

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

    Zoeken in volledige tekst werkt met @@ in plaats van MATCH ... AGAINST.

    Rapportregel: fulltext_as_gin

  • indexes on values over 2,000 bytes

    hash

    Een btree-entry kan zulke lange waarden niet bevatten; een hash-index vindt gelijke waarden nog steeds.

    Rapportregel: index_on_wide_values

  • SPATIAL indexes

    niet aangemaakt zonder PostGIS

    Ruimtelijke indexen hebben PostGIS nodig.

    Rapportregel: spatial_index_skipped

  • functional indexes PostgreSQL can't write

    niet aangemaakt

    Een index op een expressie die niet voor PostgreSQL kan worden geschreven, wordt weggelaten en gemeld.

    Rapportregel: index_skipped

  • foreign keys

    na de data, NOT VALID, daarna gevalideerd met een waarschuwing bij wezen

    Dumps laden met FOREIGN_KEY_CHECKS=0 en MyISAM controleert nooit, dus rijen zonder ouder komen vaak voor. De sleutel wordt eerst aangemaakt en als laatste gevalideerd; wezen laten hem NOT VALID met een waarschuwing, in plaats van het laden te stoppen.

  • foreign keys to columns without a unique key

    niet aangemaakt

    PostgreSQL heeft een unieke sleutel nodig op de kolommen waarnaar wordt verwezen; InnoDB niet.

    Rapportregel: foreign_key_skipped

  • table and column names

    kleine letters als dat uniek blijft

    Namen in kleine letters kunnen in PostgreSQL zonder aanhalingstekens worden geschreven.

    Rapportregel: identifier_renamed

  • names over 63 bytes

    ingekort met een hash

    Namen in PostgreSQL bevatten maximaal 63 bytes; de hash houdt ingekorte namen uit elkaar.

    Rapportregel: name_shortened

  • GEOMETRY, POINT, POLYGON...

    WKB in bytea

    Zonder PostGIS behoudt het standaardformaat well-known binary elke geometrie exact.

    Rapportregel: geometry_as_wkb

  • INET4, INET6, UUID (MariaDB)

    inet, uuid

    PostgreSQL heeft dezelfde typen.

  • BINARY, VARBINARY, BLOB

    bytea

    Bytes blijven bytes.

  • PARTITION BY

    één tabel

    De rijen zijn dezelfde; partitioneer de tabel opnieuw in PostgreSQL als dat nodig is.

    Rapportregel: partitions_dropped

  • WITH SYSTEM VERSIONING (MariaDB)

    een tabel met de huidige rijen

    Een dump bevat alleen de huidige rijen.

    Rapportregel: system_versioning_dropped

  • MyISAM, MEMORY and other engines

    een gewone tabel

    PostgreSQL heeft één tabelengine.

    Rapportregel: engine_dropped

  • views, triggers, procedures, functions and events

    vermeld in het rapport

    Hun code is geschreven in de taal van MySQL, dus ze worden vermeld zodat je ze opnieuw kunt maken.

    Rapportregel: not_converted_objects

  • dumps without CREATE TABLE

    typen afgeleid uit de waarden

    Een dump gemaakt met --no-create-info bevat alleen rijen, dus de typen komen uit de waarden.

    Rapportregel: no_schema

  • geometries in a dump without CREATE TABLE

    WKB in bytea

    Het geometrieformaat van MySQL wordt herkend aan de waarden en opgeslagen als standaard-WKB.

    Rapportregel: geometry_inferred

  • _ci collations

    exacte vergelijking

    PostgreSQL vergelijkt tekst exact, tenzij anders aangegeven.

    Rapportregel: case_insensitive_collation

  • SET NAMES utf8 from utf8mb4 tables

    geconverteerd, met een waarschuwing

    mysqldump van MySQL 5.x schrijft standaard 3-byte utf8 en vervangt elke emoji door een vraagteken voordat een converter het bestand ziet.

    Rapportregel: dump_lost_four_byte_chars

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

    gelezen als Latin-1 (Windows-1252)

    latin1 in MySQL is Windows-1252, de gebruikelijke codering van zulke dumps.

    Rapportregel: charset_guessed

  • sjis, cp932, gbk, big5 dumps

    gelezen als UTF-8, met een waarschuwing

    Deze coderingen worden nog niet gedecodeerd.

    Rapportregel: unsupported_charset

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

    geëvalueerd

    Sommige dumptools schrijven waarden als deze aanroepen; ze worden gelezen als de waarden die ze opleveren, met geografische WKT omgezet naar lengtegraad eerst.

  • other expressions in rows

    NULL

    Een converter die geen database draait, kan ze niet evalueren.

    Rapportregel: expression_value

  • DEFAULT in VALUES

    de standaardwaarde van de kolom

    Een letterlijke standaardwaarde wordt in de rij gekopieerd.

    Rapportregel: default_keyword_filled

  • DEFAULT in VALUES without a literal default

    NULL

    Er is geen waarde om te kopiëren.

    Rapportregel: default_keyword_null

  • REPLACE and INSERT IGNORE

    gewone rijen

    Een dump heeft één rij per sleutel, dus de rijen zijn dezelfde.

    Rapportregel: replace_as_insert

  • types IntoSQL doesn't know

    text

    Text kan elke waarde bevatten; wijzig het type na het laden.

    Rapportregel: unknown_type

  • values that don't fit their column

    NULL

    Een beschadigde of met de hand geschreven dump kan waarden bevatten die de kolom niet aankan; ze worden NULL zodat de rest laadt.

    Rapportregel: invalid_value_null

  • unreadable geometries

    NULL

    Bytes die geen geometrie zijn, kunnen niet als geometrie worden opgeslagen.

    Rapportregel: bad_geometry

  • CREATE SEQUENCE (MariaDB)

    CREATE SEQUENCE met de huidige waarde

    Sequences in PostgreSQL nemen dezelfde opties.

In MySQLIn PostgreSQLRapportregel
TINYINT(1), BOOLboolean als elke waarde 0 of 1 is, anders smallintBOOL in MySQL is een TINYINT(1) die elk getal van -128 tot 127 kan bevatten. Een kolom met alleen 0 en 1 wordt boolean; een kolom met 2 of -1 blijft een getal, zodat geen waarde van betekenis verandert.bool_kept_numeric
UNSIGNED integershet eerstvolgende bredere gehele getal met een CHECK dat het niet negatief is; BIGINT UNSIGNED wordt bigint als elke waarde past, anders numeric(20,0)PostgreSQL heeft geen unsigned gehele getallen. Een breder type bevat het hele unsigned bereik, en de check houdt negatieve waarden buiten, zoals MySQL deed.unsigned_widened
ZEROFILL and display widthsweggelatenOpvulling met nullen is een manier om een getal te tonen, geen deel ervan. De getallen blijven; formatteer ze bij het lezen als de opvulling ertoe doet.zerofill_dropped
FLOAT(M,D)real of double precision, zonder de schaalDrijvendekommagetallen in PostgreSQL hebben geen vaste schaal. De opgeslagen waarden worden ongewijzigd gekopieerd.float_scale_dropped
BIT(1), BIT(n)boolean, bit(n)Eén bit is een ja of nee; bredere bitvelden behouden elke bit.
zero, partial or impossible dates (0000-00-00, 2024-02-30)NULLPostgreSQL slaat alleen echte kalenderdatums op. MySQL bewaart nul- en onmogelijke datums als de SQL-modus dat toestaat; ze worden NULL en het rapport telt ze.invalid_date_null
NOT NULL columns with values that become NULLNOT NULL weggelatenEen kolom waarvan waarden NULL moesten worden, kan NOT NULL niet houden, anders zou het laden stoppen.not_null_relaxed
DEFAULT '0000-00-00'geen standaardwaardeEen standaardwaarde die PostgreSQL niet kan opslaan, wordt weggelaten in plaats van vervangen door een verzonnen datum.zero_date_default
TIMEtime als elke waarde binnen een dag valt, anders intervalTIME in MySQL is een duur van -838:59:59 tot 838:59:59. Een kolom waarvan de waarden binnen een dag passen, blijft een tijdstip; een kolom met langere of negatieve waarden wordt interval.time_as_interval
YEARsmallint met een CHECK op het bereikPostgreSQL heeft geen jaartype; de check houdt dezelfde waarden die MySQL toestaat.
CHAR(n)varchar(n)MySQL verwijdert de spaties aan het einde van CHAR-waarden bij het lezen, en char(n) in PostgreSQL zou ze weer toevoegen, dus varchar(n) houdt de waarden zoals MySQL ze teruggeeft.
DATETIMEtimestampEen DATETIME is een kloktijd zonder tijdzone, net als timestamp in PostgreSQL.
TIMESTAMPtimestamptz in de tijdzone die de dump vermeldt, UTC als die er geen vermeldtEen TIMESTAMP is een moment. Dumps vermelden de zone waarin ze het schreven (mysqldump schrijft UTC); zonder die vermelding worden de waarden gelezen als UTC en meldt het rapport dat.timestamp_zone_assumed
ENUMtext met een CHECK op de waardenText met een check houdt dezelfde waarden en is makkelijker te wijzigen dan een enum type in PostgreSQL.
ENUM holding '' (MySQL's error value)de lege waarde, toegestaan door de checkMySQL slaat een lege string op als er in een tolerante SQL-modus een ongeldige waarde in een ENUM komt. Die rijen blijven behouden en de check staat ze toe.enum_invalid_value
ENUM or SET holding values outside its listgeen CHECKWaarden die niet in de lijst staan, zouden de check laten mislukken, dus de check wordt niet aangemaakt.enum_check_skipped
SETtext[] met een CHECK dat elk element in de lijst staatEen array houdt elk element apart, zodat query's op lidmaatschap kunnen testen.
JSONjsonb; json als een waarde \u0000 bevatjsonb is het type dat PostgreSQL goed indexeert en bevraagt, maar het weigert de escape \u0000, dus een kolom daarmee blijft json.json_not_jsonb
NUL bytes in textverwijderdTekst in PostgreSQL kan geen NUL-byte bevatten. De byte wordt verwijderd en het rapport telt de waarden.nul_removed
text that isn't valid in its character setde foute bytes als het vervangingstekenTekst in PostgreSQL moet geldige UTF-8 zijn.invalid_utf8_replaced
text decoded twice (ñ)ongewijzigd gekopieerdDe dump bevat precies deze tekens, dus ze worden gekopieerd; het rapport wijst ze aan.mojibake_suspected
AUTO_INCREMENTGENERATED BY DEFAULT AS IDENTITY, met de teller voorbij de gekopieerde id'sIdentity-kolommen accepteren de gekopieerde id's, en de teller gaat verder na de hoogste daarvan of na de AUTO_INCREMENT van de dump, welke hoger is.
MyISAM AUTO_INCREMENT on the second column of a keyéén teller voor de tabelMyISAM telt per groep van de eerste kolom van de sleutel; PostgreSQL heeft één teller per kolom.myisam_group_autoincrement
ON UPDATE CURRENT_TIMESTAMPeen BEFORE UPDATE-triggerDe trigger zet de kolom op de huidige tijd als een rij verandert en de update hem niet instelde, zoals MySQL doet.on_update_trigger
generated columnsSTOREDPostgreSQL 13 tot en met 17 slaat gegenereerde kolommen alleen op; de formule is dezelfde.virtual_as_stored
generated columns with functions PostgreSQL lackseen gewone kolomEen formule die niet voor PostgreSQL kan worden geschreven, levert een gewone kolom en een regel in het rapport op.generated_not_translated
INVISIBLE columnsgewone kolommenPostgreSQL heeft geen onzichtbare kolommen.invisible_column
CHECK constraintsvertaald, inline in CREATE TABLEInline checks worden getest terwijl elke rij laadt, in plaats van achteraf in een scan per check.
CHECK constraints with functions PostgreSQL lacksniet aangemaaktEen check die niet voor PostgreSQL kan worden geschreven, wordt weggelaten en gemeld.check_skipped
CHECK ... NOT ENFORCEDniet aangemaaktMySQL bewaart deze checks zonder ze toe te passen, dus de rijen kunnen ze schenden.check_not_enforced
defaults with functions PostgreSQL lacksgeen standaardwaardeEen standaardwaarde die niet voor PostgreSQL kan worden geschreven, wordt weggelaten en gemeld.default_not_translated
keys and indexes on a prefix, c(20)een index op left(c, 20)PostgreSQL indexeert expressies, dus hetzelfde prefix wordt geïndexeerd.prefix_unique
FULLTEXT indexesGIN op to_tsvector('simple', ...)Zoeken in volledige tekst werkt met @@ in plaats van MATCH ... AGAINST.fulltext_as_gin
indexes on values over 2,000 byteshashEen btree-entry kan zulke lange waarden niet bevatten; een hash-index vindt gelijke waarden nog steeds.index_on_wide_values
SPATIAL indexesniet aangemaakt zonder PostGISRuimtelijke indexen hebben PostGIS nodig.spatial_index_skipped
functional indexes PostgreSQL can't writeniet aangemaaktEen index op een expressie die niet voor PostgreSQL kan worden geschreven, wordt weggelaten en gemeld.index_skipped
foreign keysna de data, NOT VALID, daarna gevalideerd met een waarschuwing bij wezenDumps laden met FOREIGN_KEY_CHECKS=0 en MyISAM controleert nooit, dus rijen zonder ouder komen vaak voor. De sleutel wordt eerst aangemaakt en als laatste gevalideerd; wezen laten hem NOT VALID met een waarschuwing, in plaats van het laden te stoppen.
foreign keys to columns without a unique keyniet aangemaaktPostgreSQL heeft een unieke sleutel nodig op de kolommen waarnaar wordt verwezen; InnoDB niet.foreign_key_skipped
table and column nameskleine letters als dat uniek blijftNamen in kleine letters kunnen in PostgreSQL zonder aanhalingstekens worden geschreven.identifier_renamed
names over 63 bytesingekort met een hashNamen in PostgreSQL bevatten maximaal 63 bytes; de hash houdt ingekorte namen uit elkaar.name_shortened
GEOMETRY, POINT, POLYGON...WKB in byteaZonder PostGIS behoudt het standaardformaat well-known binary elke geometrie exact.geometry_as_wkb
INET4, INET6, UUID (MariaDB)inet, uuidPostgreSQL heeft dezelfde typen.
BINARY, VARBINARY, BLOBbyteaBytes blijven bytes.
PARTITION BYéén tabelDe rijen zijn dezelfde; partitioneer de tabel opnieuw in PostgreSQL als dat nodig is.partitions_dropped
WITH SYSTEM VERSIONING (MariaDB)een tabel met de huidige rijenEen dump bevat alleen de huidige rijen.system_versioning_dropped
MyISAM, MEMORY and other engineseen gewone tabelPostgreSQL heeft één tabelengine.engine_dropped
views, triggers, procedures, functions and eventsvermeld in het rapportHun code is geschreven in de taal van MySQL, dus ze worden vermeld zodat je ze opnieuw kunt maken.not_converted_objects
dumps without CREATE TABLEtypen afgeleid uit de waardenEen dump gemaakt met --no-create-info bevat alleen rijen, dus de typen komen uit de waarden.no_schema
geometries in a dump without CREATE TABLEWKB in byteaHet geometrieformaat van MySQL wordt herkend aan de waarden en opgeslagen als standaard-WKB.geometry_inferred
_ci collationsexacte vergelijkingPostgreSQL vergelijkt tekst exact, tenzij anders aangegeven.case_insensitive_collation
SET NAMES utf8 from utf8mb4 tablesgeconverteerd, met een waarschuwingmysqldump van MySQL 5.x schrijft standaard 3-byte utf8 en vervangt elke emoji door een vraagteken voordat een converter het bestand ziet.dump_lost_four_byte_chars
dumps without SET NAMES whose text isn't UTF-8gelezen als Latin-1 (Windows-1252)latin1 in MySQL is Windows-1252, de gebruikelijke codering van zulke dumps.charset_guessed
sjis, cp932, gbk, big5 dumpsgelezen als UTF-8, met een waarschuwingDeze coderingen worden nog niet gedecodeerd.unsupported_charset
UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rowsgeëvalueerdSommige dumptools schrijven waarden als deze aanroepen; ze worden gelezen als de waarden die ze opleveren, met geografische WKT omgezet naar lengtegraad eerst.
other expressions in rowsNULLEen converter die geen database draait, kan ze niet evalueren.expression_value
DEFAULT in VALUESde standaardwaarde van de kolomEen letterlijke standaardwaarde wordt in de rij gekopieerd.default_keyword_filled
DEFAULT in VALUES without a literal defaultNULLEr is geen waarde om te kopiëren.default_keyword_null
REPLACE and INSERT IGNOREgewone rijenEen dump heeft één rij per sleutel, dus de rijen zijn dezelfde.replace_as_insert
types IntoSQL doesn't knowtextText kan elke waarde bevatten; wijzig het type na het laden.unknown_type
values that don't fit their columnNULLEen beschadigde of met de hand geschreven dump kan waarden bevatten die de kolom niet aankan; ze worden NULL zodat de rest laadt.invalid_value_null
unreadable geometriesNULLBytes die geen geometrie zijn, kunnen niet als geometrie worden opgeslagen.bad_geometry
CREATE SEQUENCE (MariaDB)CREATE SEQUENCE met de huidige waardeSequences in PostgreSQL nemen dezelfde opties.

Het script in PostgreSQL laden

Het script maakt de tabellen aan, kopieert de rijen en voegt daarna sleutels, indexen en refererende sleutels toe, zodat het snel laadt en bij de eerste fout stopt.

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

Refererende sleutels worden toegevoegd als NOT VALID en daarna gevalideerd. Verwijst een rij naar een rij die niet bestaat, dan vertelt het script je welke constraint het is, in plaats van halverwege te mislukken.

Maak voor een grote dump eerst de database aan en voer het script uit met psql vanaf dezelfde machine als de server.

Een dump van MySQL maken voor het beste resultaat

De converter leest wat de dump bevat. Met deze opties blijft er meer van je data in staan:

  • --default-character-set=utf8mb4 behoudt emoji en andere 4-bytestekens, die een utf8-dump kwijtraakt voordat de converter hem ziet.
  • --hex-blob schrijft binaire kolommen als hexadecimaal, wat kopiëren tussen systemen overleeft.
  • --routines --triggers --events voegen die objecten aan de dump toe, zodat het rapport ze kan vermelden.

Wat niet wordt geconverteerd

Weergaven, triggers, opgeslagen procedures, functies en gebeurtenissen worden geschreven in de eigen taal van elke database. Ze staan op naam in het rapport en worden nooit stilletjes weggelaten, zodat je ze kunt herschrijven voor PostgreSQL.

Gemeten tegen pgloader

Dezelfde dumps, geconverteerd en geladen door IntoSQL en door pgloader 3.6.7 op één machine (een i9-10900K met 10 cores en een SATA SSD), in oktober 2026.

  • Een TINYINT(1)-kolom met 2, -1 en -5: pgloader laadt voor elk ervan true, zonder waarschuwing. IntoSQL behoudt de getallen en het rapport legt uit waarom.
  • Een dump met een standaardwaarde 'it's', een tabel met een backtick in de naam, een ENUM met de lege waarde en een TIME van meer dan 24 uur: pgloader stopt. IntoSQL converteert en laadt hem.
  • Wat pgloader laadt, is zijn primaire, unieke en refererende sleutels en zijn identities kwijt. IntoSQL behoudt ze allemaal.
  • Een dump van 5.3 GB met 30 miljoen rijen: pgloader moet hem eerst terugzetten in een draaiende MySQL, wat 18 minuten duurt, en heeft daarna met de standaardinstellingen te weinig geheugen. IntoSQL converteert de dump in 10 seconden, en psql laadt het script in ongeveer 3 minuten.
  • Over 476 dumps van MySQL 5.5 tot en met 9 en MariaDB 10.3 tot en met 12, geladen in PostgreSQL 13 en 18: geen laadfouten, en geen waarde veranderd zonder een regel in het rapport.

Vragen over MySQL naar PostgreSQL

Staat je vraag er niet bij? Schrijf ons via de contactpagina en een mens antwoordt. Contactpagina

Wordt mijn dump geüpload?

Nee. De converter draait in je browser en leest de dump in delen van je schijf, dus hij verlaat je computer nooit. De desktopapp en de opdrachtregel converteren ook op je computer.

Hoe groot mag de dump zijn?

De converter houdt nooit de hele dump in het geheugen: hij leest het bestand twee keer, in bereiken, en schrijft het script gaandeweg. Dumps van meerdere gigabytes worden geconverteerd in de browser, in de desktopapp en met de opdrachtregel.

Leest het MariaDB-dumps?

Ja. Dumps van mariadb-dump en van MariaDB 10.3 tot en met 12 worden op dezelfde manier geconverteerd, inclusief de INET6-, INET4- en UUID-kolommen van MariaDB.

Wat gebeurt er met datums als 0000-00-00?

PostgreSQL kent geen nuldatum, dus ze worden NULL en de kolom accepteert NULL. Het rapport vermeldt hoeveel er in elke kolom waren.

Blijven AUTO_INCREMENT-waarden behouden?

Ja. Elke AUTO_INCREMENT-kolom wordt een identity-kolom, elke rij behoudt zijn id, en het volgende id gaat verder na het hoogste, of na de AUTO_INCREMENT van de tabel als die hoger is.

Wordt TINYINT(1) boolean?

Alleen als elke waarde in de kolom 0 of 1 is. Bevat hij andere getallen, zoals 2 of -1, dan blijft het een getal en legt het rapport uit waarom, zodat geen waarde per ongeluk true wordt.

En opgeslagen procedures en triggers?

Ze staan op naam in het rapport en worden niet geconverteerd, omdat elke database ze in een eigen taal schrijft. ON UPDATE CURRENT_TIMESTAMP is de uitzondering: dat wordt een PostgreSQL-trigger.

Op welke PostgreSQL-versies laadt het script?

PostgreSQL 13 tot en met 18. Hetzelfde script laadt op elk ervan en stopt bij de eerste fout als er iets misgaat.