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.
18 rijen geconverteerd, 8 correcties, geen afgewezen
Tabellen in shop_2024.sqlMySQL 8.0.46-dump · geschreven door mysqldump · utf8mb4
customers8 rijen3
7 kolommen 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 rijen5
9 kolommen 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();
Correcties
- "id" van customers is unsigned, dus het is bigint geworden met een check dat het niet negatief is.
- "signed_up" van customers staat nu NULL toe: sommige waarden konden niet worden opgeslagen en zijn NULL.
- De standaardwaarde van "signed_up" in customers, 0000‑00‑00, is geen datum die PostgreSQL accepteert, dus de kolom heeft geen standaardwaarde.
- "customer_id" van orders is unsigned, dus het is bigint geworden met een check dat het niet negatief is.
- "id" van orders is unsigned, dus het is bigint geworden met een check dat het niet negatief is.
- "items" van orders is unsigned, dus het is integer geworden met een check dat het niet negatief is.
- 1 rij van "status" in orders bevat de lege waarde die MySQL opslaat voor ongeldige invoer; de check op de waarden staat die toe.
- "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 MySQL | In PostgreSQL | Rapportregel |
|---|---|---|
| TINYINT(1), BOOL | boolean 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 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. | unsigned_widened |
| ZEROFILL and display widths | weggelatenOpvulling 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 NULL | NOT 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 |
| TIME | time 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 |
| YEAR | smallint 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. | |
| DATETIME | timestampEen 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 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 |
| ENUM | text 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 list | geen CHECKWaarden die niet in de lijst staan, zouden de check laten mislukken, dus de check wordt niet aangemaakt. | enum_check_skipped |
| SET | text[] met een CHECK dat elk element in de lijst staatEen array houdt elk element apart, zodat query's op lidmaatschap kunnen testen. | |
| JSON | jsonb; 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 text | verwijderdTekst 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 set | de 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_INCREMENT | GENERATED 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_TIMESTAMP | een 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 columns | STOREDPostgreSQL 13 tot en met 17 slaat gegenereerde kolommen alleen op; de formule is dezelfde. | virtual_as_stored |
| generated columns with functions PostgreSQL lacks | een 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 columns | gewone kolommenPostgreSQL heeft geen onzichtbare kolommen. | invisible_column |
| CHECK constraints | vertaald, 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 lacks | niet aangemaaktEen check die niet voor PostgreSQL kan worden geschreven, wordt weggelaten en gemeld. | check_skipped |
| CHECK ... NOT ENFORCED | niet aangemaaktMySQL bewaart deze checks zonder ze toe te passen, dus de rijen kunnen ze schenden. | check_not_enforced |
| defaults with functions PostgreSQL lacks | geen 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 indexes | GIN op to_tsvector('simple', ...)Zoeken in volledige tekst werkt met @@ in plaats van MATCH ... AGAINST. | fulltext_as_gin |
| indexes on values over 2,000 bytes | hashEen btree-entry kan zulke lange waarden niet bevatten; een hash-index vindt gelijke waarden nog steeds. | index_on_wide_values |
| SPATIAL indexes | niet aangemaakt zonder PostGISRuimtelijke indexen hebben PostGIS nodig. | spatial_index_skipped |
| functional indexes PostgreSQL can't write | niet aangemaaktEen index op een expressie die niet voor PostgreSQL kan worden geschreven, wordt weggelaten en gemeld. | index_skipped |
| foreign keys | na 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 key | niet aangemaaktPostgreSQL heeft een unieke sleutel nodig op de kolommen waarnaar wordt verwezen; InnoDB niet. | foreign_key_skipped |
| table and column names | kleine letters als dat uniek blijftNamen in kleine letters kunnen in PostgreSQL zonder aanhalingstekens worden geschreven. | identifier_renamed |
| names over 63 bytes | ingekort 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, BLOB | byteaBytes 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 engines | een gewone tabelPostgreSQL heeft één tabelengine. | engine_dropped |
| views, triggers, procedures, functions and events | vermeld 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 TABLE | typen 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 TABLE | WKB in byteaHet geometrieformaat van MySQL wordt herkend aan de waarden en opgeslagen als standaard-WKB. | geometry_inferred |
| _ci collations | exacte vergelijkingPostgreSQL vergelijkt tekst exact, tenzij anders aangegeven. | case_insensitive_collation |
| SET NAMES utf8 from utf8mb4 tables | geconverteerd, 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-8 | gelezen als Latin-1 (Windows-1252)latin1 in MySQL is Windows-1252, de gebruikelijke codering van zulke dumps. | charset_guessed |
| sjis, cp932, gbk, big5 dumps | gelezen als UTF-8, met een waarschuwingDeze coderingen worden nog niet gedecodeerd. | unsupported_charset |
| UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows | geë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 rows | NULLEen converter die geen database draait, kan ze niet evalueren. | expression_value |
| DEFAULT in VALUES | de standaardwaarde van de kolomEen letterlijke standaardwaarde wordt in de rij gekopieerd. | default_keyword_filled |
| DEFAULT in VALUES without a literal default | NULLEr is geen waarde om te kopiëren. | default_keyword_null |
| REPLACE and INSERT IGNORE | gewone rijenEen dump heeft één rij per sleutel, dus de rijen zijn dezelfde. | replace_as_insert |
| types IntoSQL doesn't know | textText kan elke waarde bevatten; wijzig het type na het laden. | unknown_type |
| values that don't fit their column | NULLEen 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 geometries | NULLBytes 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.sqlRefererende 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.