MySQL in PostgreSQL umwandeln
Leg einen Dump aus mysqldump, mariadb-dump, mysqlpump, phpMyAdmin oder Adminer ab, als .sql oder .sql.gz, und lade ein PostgreSQL-Skript mit denselben Tabellen, Schlüsseln, Indizes und Daten herunter. Jeder Wert, den PostgreSQL nicht unverändert aufnehmen kann, und jedes Objekt, das es nicht übernimmt, steht im Bericht.
18 Zeilen konvertiert, 8 Korrekturen, keine abgelehnt
Tabellen in shop_2024.sqlMySQL-8.0.46-Dump · erstellt mit mysqldump · utf8mb4
customers8 Zeilen3
7 Spalten 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 Zeilen5
9 Spalten 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();
Korrekturen
- "id" von customers ist vorzeichenlos und wurde deshalb bigint mit einer CHECK-Bedingung gegen negative Werte.
- "signed_up" von customers erlaubt jetzt NULL: Einige Werte ließen sich nicht speichern und sind NULL.
- Der Standardwert von "signed_up" in customers, 0000‑00‑00, ist kein Datum, das PostgreSQL akzeptiert, daher hat die Spalte keinen Standardwert.
- "customer_id" von orders ist vorzeichenlos und wurde deshalb bigint mit einer CHECK-Bedingung gegen negative Werte.
- "id" von orders ist vorzeichenlos und wurde deshalb bigint mit einer CHECK-Bedingung gegen negative Werte.
- "items" von orders ist vorzeichenlos und wurde deshalb integer mit einer CHECK-Bedingung gegen negative Werte.
- 1 Zeile von "status" in orders enthält den leeren Wert, den MySQL für ungültige Einträge speichert; die CHECK-Bedingung der Spalte erlaubt ihn.
- "updated_at" von orders wird bei jeder Änderung auf die aktuelle Zeit gesetzt; in PostgreSQL übernimmt das ein Trigger.
Zu prüfen
- 1 Wert in "signed_up" von customers ist ein Null- oder unmögliches Datum wie 0000‑00‑00, was PostgreSQL nicht speichern kann, daher ist er NULL. Wenn diese Datumsangaben etwas bedeuten, ersetze sie vor dem Dump in der Quelle durch echte.
- 1 Wert in "shipped_at" von orders ist ein Null- oder unmögliches Datum wie 0000‑00‑00 00:00:00, was PostgreSQL nicht speichern kann, daher ist er NULL. Wenn diese Datumsangaben etwas bedeuten, ersetze sie vor dem Dump in der Quelle durch echte.
Deine Datei wird auf diesem Computer umgewandelt und nie hochgeladen.
Weitere Beispiele:
Was sich von MySQL zu PostgreSQL ändert
Jede Zeile ist eine Regel, die der Konverter anwendet, mit der Berichtszeile, die er schreibt, wenn die Regel etwas an deinen Daten ändert.
TINYINT(1), BOOL
boolean, wenn jeder Wert 0 oder 1 ist, sonst smallint
MySQLs BOOL ist ein TINYINT(1), das jede Zahl von -128 bis 127 aufnehmen kann. Eine Spalte, die nur 0 und 1 enthält, wird boolean; eine mit 2 oder -1 bleibt eine Zahl, damit kein Wert seine Bedeutung ändert.
Berichtszeile: bool_kept_numeric
UNSIGNED integers
der nächstgrößere Ganzzahltyp mit einem CHECK gegen negative Werte; BIGINT UNSIGNED wird bigint, wenn jeder Wert passt, sonst numeric(20,0)
PostgreSQL hat keine vorzeichenlosen Ganzzahlen. Ein größerer Typ fasst den ganzen vorzeichenlosen Bereich, und die CHECK-Bedingung hält negative Werte fern wie in MySQL.
Berichtszeile: unsigned_widened
ZEROFILL and display widths
entfernt
Das Auffüllen mit Nullen ist eine Darstellung der Zahl, kein Teil davon. Die Zahlen bleiben erhalten; formatiere sie beim Lesen, wenn die Auffüllung wichtig ist.
Berichtszeile: zerofill_dropped
FLOAT(M,D)
real oder double precision, ohne die Nachkommastellen
Gleitkommazahlen in PostgreSQL haben keine feste Anzahl Nachkommastellen. Die gespeicherten Werte werden unverändert kopiert.
Berichtszeile: float_scale_dropped
BIT(1), BIT(n)
boolean, bit(n)
Ein einzelnes Bit ist ein Ja oder Nein; breitere Bitfelder behalten jedes Bit.
zero, partial or impossible dates (0000-00-00, 2024-02-30)
NULL
PostgreSQL speichert nur echte Kalenderdaten. MySQL behält Null- und unmögliche Datumsangaben, wenn sein SQL-Modus sie erlaubt; sie werden NULL, und der Bericht zählt sie.
Berichtszeile: invalid_date_null
NOT NULL columns with values that become NULL
NOT NULL entfernt
Eine Spalte, deren Werte NULL werden mussten, kann NOT NULL nicht behalten, sonst würde das Laden abbrechen.
Berichtszeile: not_null_relaxed
DEFAULT '0000-00-00'
kein Standardwert
Ein Standardwert, den PostgreSQL nicht speichern kann, wird weggelassen, statt durch ein erfundenes Datum ersetzt zu werden.
Berichtszeile: zero_date_default
TIME
time, wenn jeder Wert innerhalb eines Tages liegt, sonst interval
MySQLs TIME ist eine Dauer von -838:59:59 bis 838:59:59. Eine Spalte, deren Werte in einen Tag passen, bleibt eine Uhrzeit; eine mit längeren oder negativen Werten wird interval.
Berichtszeile: time_as_interval
YEAR
smallint mit einem CHECK für den Wertebereich
PostgreSQL hat keinen Jahrestyp; die CHECK-Bedingung erlaubt dieselben Werte wie MySQL.
CHAR(n)
varchar(n)
MySQL entfernt beim Lesen die Leerzeichen am Ende von CHAR-Werten, und char(n) in PostgreSQL würde sie wieder anfügen, daher behält varchar(n) die Werte so, wie MySQL sie liefert.
DATETIME
timestamp
Ein DATETIME ist eine Datums- und Uhrzeitangabe ohne Zeitzone, wie timestamp in PostgreSQL.
TIMESTAMP
timestamptz in der Zeitzone, die der Dump angibt, UTC, wenn er keine angibt
Ein TIMESTAMP ist ein Zeitpunkt. Dumps geben die Zone an, in der sie ihn geschrieben haben (mysqldump schreibt UTC); fehlt sie, werden die Werte als UTC gelesen, und der Bericht sagt das.
Berichtszeile: timestamp_zone_assumed
ENUM
text mit einem CHECK der erlaubten Werte
Text mit einer CHECK-Bedingung behält dieselben Werte und lässt sich leichter ändern als ein enum type in PostgreSQL.
ENUM holding '' (MySQL's error value)
der leere Wert, von der CHECK-Bedingung erlaubt
MySQL speichert einen leeren String, wenn in einem nachsichtigen SQL-Modus ein ungültiger Wert in ein ENUM geschrieben wird. Diese Zeilen bleiben erhalten, und die CHECK-Bedingung erlaubt sie.
Berichtszeile: enum_invalid_value
ENUM or SET holding values outside its list
kein CHECK
Werte, die die Liste nicht nennt, würden die CHECK-Bedingung verletzen, daher wird sie nicht angelegt.
Berichtszeile: enum_check_skipped
SET
text[] mit einem CHECK, dass jedes Element in der Liste steht
Ein Array hält jedes Element getrennt, sodass Abfragen die Zugehörigkeit prüfen können.
JSON
jsonb; json, wenn ein Wert \u0000 enthält
jsonb ist der Typ, den PostgreSQL gut indiziert und abfragt, aber er lehnt das Escape \u0000 ab, daher bleibt eine Spalte damit json.
Berichtszeile: json_not_jsonb
NUL bytes in text
entfernt
PostgreSQL-Text kann kein NUL-Byte enthalten. Das Byte wird entfernt, und der Bericht zählt die Werte.
Berichtszeile: nul_removed
text that isn't valid in its character set
die fehlerhaften Bytes als Ersatzzeichen
Text in PostgreSQL muss gültiges UTF-8 sein.
Berichtszeile: invalid_utf8_replaced
text decoded twice (ñ)
unverändert kopiert
Der Dump enthält genau diese Zeichen, daher werden sie kopiert; der Bericht weist auf sie hin.
Berichtszeile: mojibake_suspected
AUTO_INCREMENT
GENERATED BY DEFAULT AS IDENTITY, mit dem Zähler hinter den kopierten IDs
Identity-Spalten nehmen die kopierten IDs an, und der Zähler macht nach der höchsten weiter oder nach dem AUTO_INCREMENT des Dumps, je nachdem, was höher ist.
MyISAM AUTO_INCREMENT on the second column of a key
ein Zähler für die Tabelle
MyISAM zählt pro Gruppe der ersten Schlüsselspalte; PostgreSQL hat einen Zähler pro Spalte.
Berichtszeile: myisam_group_autoincrement
ON UPDATE CURRENT_TIMESTAMP
ein Trigger mit BEFORE UPDATE
Der Trigger setzt die Spalte auf die aktuelle Zeit, wenn sich eine Zeile ändert und die Änderung sie nicht gesetzt hat, wie in MySQL.
Berichtszeile: on_update_trigger
generated columns
STORED
PostgreSQL 13 bis 17 kennen nur gespeicherte generierte Spalten; die Formel bleibt dieselbe.
Berichtszeile: virtual_as_stored
generated columns with functions PostgreSQL lacks
eine normale Spalte
Eine Formel, die sich nicht für PostgreSQL schreiben lässt, hinterlässt eine normale Spalte und eine Zeile im Bericht.
Berichtszeile: generated_not_translated
INVISIBLE columns
normale Spalten
PostgreSQL hat keine unsichtbaren Spalten.
Berichtszeile: invisible_column
CHECK constraints
übersetzt, inline in CREATE TABLE
Inline-CHECK-Bedingungen werden beim Laden jeder Zeile geprüft statt danach in einem eigenen Durchlauf pro Bedingung.
CHECK constraints with functions PostgreSQL lacks
nicht angelegt
Eine CHECK-Bedingung, die sich nicht für PostgreSQL schreiben lässt, wird weggelassen und gemeldet.
Berichtszeile: check_skipped
CHECK ... NOT ENFORCED
nicht angelegt
MySQL behält diese CHECK-Bedingungen, ohne sie anzuwenden, daher können Zeilen sie verletzen.
Berichtszeile: check_not_enforced
defaults with functions PostgreSQL lacks
kein Standardwert
Ein Standardwert, der sich nicht für PostgreSQL schreiben lässt, wird weggelassen und gemeldet.
Berichtszeile: default_not_translated
keys and indexes on a prefix, c(20)
ein Index auf left(c, 20)
PostgreSQL indiziert Ausdrücke, daher wird derselbe Präfix indiziert.
Berichtszeile: prefix_unique
FULLTEXT indexes
GIN auf to_tsvector('simple', ...)
Die Volltextsuche funktioniert mit @@ statt MATCH ... AGAINST.
Berichtszeile: fulltext_as_gin
indexes on values over 2,000 bytes
hash
Ein btree-Eintrag kann so lange Werte nicht aufnehmen; ein hash-Index findet gleiche Werte trotzdem.
Berichtszeile: index_on_wide_values
SPATIAL indexes
ohne PostGIS nicht angelegt
Räumliche Indizes brauchen PostGIS.
Berichtszeile: spatial_index_skipped
functional indexes PostgreSQL can't write
nicht angelegt
Ein Index auf einen Ausdruck, der sich nicht für PostgreSQL schreiben lässt, wird weggelassen und gemeldet.
Berichtszeile: index_skipped
foreign keys
nach den Daten, NOT VALID, dann validiert, mit einer Warnung bei verwaisten Zeilen
Dumps laden mit FOREIGN_KEY_CHECKS=0, und MyISAM prüft nie, daher sind Zeilen ohne Elternzeile häufig. Der Schlüssel wird zuerst angelegt und zuletzt validiert; verwaiste Zeilen lassen ihn mit einer Warnung NOT VALID, statt das Laden abzubrechen.
foreign keys to columns without a unique key
nicht angelegt
PostgreSQL braucht einen eindeutigen Schlüssel auf den referenzierten Spalten; InnoDB nicht.
Berichtszeile: foreign_key_skipped
table and column names
kleingeschrieben, wenn das eindeutig bleibt
Kleingeschriebene Namen lassen sich in PostgreSQL ohne Anführungszeichen schreiben.
Berichtszeile: identifier_renamed
names over 63 bytes
mit einem Hash gekürzt
Namen in PostgreSQL fassen 63 Bytes; der Hash hält gekürzte Namen unterscheidbar.
Berichtszeile: name_shortened
GEOMETRY, POINT, POLYGON...
WKB in bytea
Ohne PostGIS bewahrt das standardisierte Well-known Binary jede Geometrie exakt.
Berichtszeile: geometry_as_wkb
INET4, INET6, UUID (MariaDB)
inet, uuid
PostgreSQL hat dieselben Typen.
BINARY, VARBINARY, BLOB
bytea
Bytes bleiben Bytes.
PARTITION BY
eine Tabelle
Die Zeilen bleiben dieselben; partitioniere die Tabelle in PostgreSQL neu, wenn nötig.
Berichtszeile: partitions_dropped
WITH SYSTEM VERSIONING (MariaDB)
eine Tabelle mit den aktuellen Zeilen
Ein Dump enthält nur die aktuellen Zeilen.
Berichtszeile: system_versioning_dropped
MyISAM, MEMORY and other engines
eine normale Tabelle
PostgreSQL hat eine einzige Tabellen-Engine.
Berichtszeile: engine_dropped
views, triggers, procedures, functions and events
im Bericht aufgeführt
Ihr Code ist in der Sprache von MySQL geschrieben, daher werden sie aufgeführt, damit du sie neu anlegst.
Berichtszeile: not_converted_objects
dumps without CREATE TABLE
Typen aus den Werten gelesen
Ein mit --no-create-info erstellter Dump enthält nur Zeilen, daher kommen die Typen aus den Werten.
Berichtszeile: no_schema
geometries in a dump without CREATE TABLE
WKB in bytea
Das Geometrieformat von MySQL wird an den Werten erkannt und als Standard-WKB gespeichert.
Berichtszeile: geometry_inferred
_ci collations
exakter Vergleich
PostgreSQL vergleicht Text exakt, sofern nicht anders angegeben.
Berichtszeile: case_insensitive_collation
SET NAMES utf8 from utf8mb4 tables
konvertiert, mit einer Warnung
mysqldump aus MySQL 5.x schreibt standardmäßig 3-Byte-utf8 und ersetzt jedes Emoji durch ein Fragezeichen, bevor ein Konverter die Datei sieht.
Berichtszeile: dump_lost_four_byte_chars
dumps without SET NAMES whose text isn't UTF-8
als Latin-1 (Windows-1252) gelesen
latin1 in MySQL ist Windows-1252, die übliche Kodierung solcher Dumps.
Berichtszeile: charset_guessed
sjis, cp932, gbk, big5 dumps
als UTF-8 gelesen, mit einer Warnung
Diese Kodierungen werden noch nicht dekodiert.
Berichtszeile: unsupported_charset
UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows
ausgewertet
Manche Dump-Werkzeuge schreiben Werte als solche Aufrufe; sie werden als die Werte gelesen, die sie ergeben, wobei geografisches WKT auf Längengrad zuerst umgestellt wird.
other expressions in rows
NULL
Ein Konverter, der keine Datenbank ausführt, kann sie nicht auswerten.
Berichtszeile: expression_value
DEFAULT in VALUES
der Standardwert der Spalte
Ein literaler Standardwert wird in die Zeile kopiert.
Berichtszeile: default_keyword_filled
DEFAULT in VALUES without a literal default
NULL
Es gibt keinen Wert zum Kopieren.
Berichtszeile: default_keyword_null
REPLACE and INSERT IGNORE
normale Zeilen
Ein Dump hat eine Zeile pro Schlüssel, daher sind die Zeilen dieselben.
Berichtszeile: replace_as_insert
types IntoSQL doesn't know
text
Text nimmt jeden Wert auf; ändere den Typ nach dem Laden.
Berichtszeile: unknown_type
values that don't fit their column
NULL
Ein beschädigter oder von Hand geschriebener Dump kann Werte enthalten, die seine Spalte nicht aufnehmen kann; sie werden NULL, damit der Rest lädt.
Berichtszeile: invalid_value_null
unreadable geometries
NULL
Bytes, die keine Geometrie sind, lassen sich nicht als solche speichern.
Berichtszeile: bad_geometry
CREATE SEQUENCE (MariaDB)
CREATE SEQUENCE mit ihrem aktuellen Wert
Sequenzen in PostgreSQL nehmen dieselben Optionen an.
| In MySQL | In PostgreSQL | Berichtszeile |
|---|---|---|
| TINYINT(1), BOOL | boolean, wenn jeder Wert 0 oder 1 ist, sonst smallintMySQLs BOOL ist ein TINYINT(1), das jede Zahl von -128 bis 127 aufnehmen kann. Eine Spalte, die nur 0 und 1 enthält, wird boolean; eine mit 2 oder -1 bleibt eine Zahl, damit kein Wert seine Bedeutung ändert. | bool_kept_numeric |
| UNSIGNED integers | der nächstgrößere Ganzzahltyp mit einem CHECK gegen negative Werte; BIGINT UNSIGNED wird bigint, wenn jeder Wert passt, sonst numeric(20,0)PostgreSQL hat keine vorzeichenlosen Ganzzahlen. Ein größerer Typ fasst den ganzen vorzeichenlosen Bereich, und die CHECK-Bedingung hält negative Werte fern wie in MySQL. | unsigned_widened |
| ZEROFILL and display widths | entferntDas Auffüllen mit Nullen ist eine Darstellung der Zahl, kein Teil davon. Die Zahlen bleiben erhalten; formatiere sie beim Lesen, wenn die Auffüllung wichtig ist. | zerofill_dropped |
| FLOAT(M,D) | real oder double precision, ohne die NachkommastellenGleitkommazahlen in PostgreSQL haben keine feste Anzahl Nachkommastellen. Die gespeicherten Werte werden unverändert kopiert. | float_scale_dropped |
| BIT(1), BIT(n) | boolean, bit(n)Ein einzelnes Bit ist ein Ja oder Nein; breitere Bitfelder behalten jedes Bit. | |
| zero, partial or impossible dates (0000-00-00, 2024-02-30) | NULLPostgreSQL speichert nur echte Kalenderdaten. MySQL behält Null- und unmögliche Datumsangaben, wenn sein SQL-Modus sie erlaubt; sie werden NULL, und der Bericht zählt sie. | invalid_date_null |
| NOT NULL columns with values that become NULL | NOT NULL entferntEine Spalte, deren Werte NULL werden mussten, kann NOT NULL nicht behalten, sonst würde das Laden abbrechen. | not_null_relaxed |
| DEFAULT '0000-00-00' | kein StandardwertEin Standardwert, den PostgreSQL nicht speichern kann, wird weggelassen, statt durch ein erfundenes Datum ersetzt zu werden. | zero_date_default |
| TIME | time, wenn jeder Wert innerhalb eines Tages liegt, sonst intervalMySQLs TIME ist eine Dauer von -838:59:59 bis 838:59:59. Eine Spalte, deren Werte in einen Tag passen, bleibt eine Uhrzeit; eine mit längeren oder negativen Werten wird interval. | time_as_interval |
| YEAR | smallint mit einem CHECK für den WertebereichPostgreSQL hat keinen Jahrestyp; die CHECK-Bedingung erlaubt dieselben Werte wie MySQL. | |
| CHAR(n) | varchar(n)MySQL entfernt beim Lesen die Leerzeichen am Ende von CHAR-Werten, und char(n) in PostgreSQL würde sie wieder anfügen, daher behält varchar(n) die Werte so, wie MySQL sie liefert. | |
| DATETIME | timestampEin DATETIME ist eine Datums- und Uhrzeitangabe ohne Zeitzone, wie timestamp in PostgreSQL. | |
| TIMESTAMP | timestamptz in der Zeitzone, die der Dump angibt, UTC, wenn er keine angibtEin TIMESTAMP ist ein Zeitpunkt. Dumps geben die Zone an, in der sie ihn geschrieben haben (mysqldump schreibt UTC); fehlt sie, werden die Werte als UTC gelesen, und der Bericht sagt das. | timestamp_zone_assumed |
| ENUM | text mit einem CHECK der erlaubten WerteText mit einer CHECK-Bedingung behält dieselben Werte und lässt sich leichter ändern als ein enum type in PostgreSQL. | |
| ENUM holding '' (MySQL's error value) | der leere Wert, von der CHECK-Bedingung erlaubtMySQL speichert einen leeren String, wenn in einem nachsichtigen SQL-Modus ein ungültiger Wert in ein ENUM geschrieben wird. Diese Zeilen bleiben erhalten, und die CHECK-Bedingung erlaubt sie. | enum_invalid_value |
| ENUM or SET holding values outside its list | kein CHECKWerte, die die Liste nicht nennt, würden die CHECK-Bedingung verletzen, daher wird sie nicht angelegt. | enum_check_skipped |
| SET | text[] mit einem CHECK, dass jedes Element in der Liste stehtEin Array hält jedes Element getrennt, sodass Abfragen die Zugehörigkeit prüfen können. | |
| JSON | jsonb; json, wenn ein Wert \u0000 enthältjsonb ist der Typ, den PostgreSQL gut indiziert und abfragt, aber er lehnt das Escape \u0000 ab, daher bleibt eine Spalte damit json. | json_not_jsonb |
| NUL bytes in text | entferntPostgreSQL-Text kann kein NUL-Byte enthalten. Das Byte wird entfernt, und der Bericht zählt die Werte. | nul_removed |
| text that isn't valid in its character set | die fehlerhaften Bytes als ErsatzzeichenText in PostgreSQL muss gültiges UTF-8 sein. | invalid_utf8_replaced |
| text decoded twice (ñ) | unverändert kopiertDer Dump enthält genau diese Zeichen, daher werden sie kopiert; der Bericht weist auf sie hin. | mojibake_suspected |
| AUTO_INCREMENT | GENERATED BY DEFAULT AS IDENTITY, mit dem Zähler hinter den kopierten IDsIdentity-Spalten nehmen die kopierten IDs an, und der Zähler macht nach der höchsten weiter oder nach dem AUTO_INCREMENT des Dumps, je nachdem, was höher ist. | |
| MyISAM AUTO_INCREMENT on the second column of a key | ein Zähler für die TabelleMyISAM zählt pro Gruppe der ersten Schlüsselspalte; PostgreSQL hat einen Zähler pro Spalte. | myisam_group_autoincrement |
| ON UPDATE CURRENT_TIMESTAMP | ein Trigger mit BEFORE UPDATEDer Trigger setzt die Spalte auf die aktuelle Zeit, wenn sich eine Zeile ändert und die Änderung sie nicht gesetzt hat, wie in MySQL. | on_update_trigger |
| generated columns | STOREDPostgreSQL 13 bis 17 kennen nur gespeicherte generierte Spalten; die Formel bleibt dieselbe. | virtual_as_stored |
| generated columns with functions PostgreSQL lacks | eine normale SpalteEine Formel, die sich nicht für PostgreSQL schreiben lässt, hinterlässt eine normale Spalte und eine Zeile im Bericht. | generated_not_translated |
| INVISIBLE columns | normale SpaltenPostgreSQL hat keine unsichtbaren Spalten. | invisible_column |
| CHECK constraints | übersetzt, inline in CREATE TABLEInline-CHECK-Bedingungen werden beim Laden jeder Zeile geprüft statt danach in einem eigenen Durchlauf pro Bedingung. | |
| CHECK constraints with functions PostgreSQL lacks | nicht angelegtEine CHECK-Bedingung, die sich nicht für PostgreSQL schreiben lässt, wird weggelassen und gemeldet. | check_skipped |
| CHECK ... NOT ENFORCED | nicht angelegtMySQL behält diese CHECK-Bedingungen, ohne sie anzuwenden, daher können Zeilen sie verletzen. | check_not_enforced |
| defaults with functions PostgreSQL lacks | kein StandardwertEin Standardwert, der sich nicht für PostgreSQL schreiben lässt, wird weggelassen und gemeldet. | default_not_translated |
| keys and indexes on a prefix, c(20) | ein Index auf left(c, 20)PostgreSQL indiziert Ausdrücke, daher wird derselbe Präfix indiziert. | prefix_unique |
| FULLTEXT indexes | GIN auf to_tsvector('simple', ...)Die Volltextsuche funktioniert mit @@ statt MATCH ... AGAINST. | fulltext_as_gin |
| indexes on values over 2,000 bytes | hashEin btree-Eintrag kann so lange Werte nicht aufnehmen; ein hash-Index findet gleiche Werte trotzdem. | index_on_wide_values |
| SPATIAL indexes | ohne PostGIS nicht angelegtRäumliche Indizes brauchen PostGIS. | spatial_index_skipped |
| functional indexes PostgreSQL can't write | nicht angelegtEin Index auf einen Ausdruck, der sich nicht für PostgreSQL schreiben lässt, wird weggelassen und gemeldet. | index_skipped |
| foreign keys | nach den Daten, NOT VALID, dann validiert, mit einer Warnung bei verwaisten ZeilenDumps laden mit FOREIGN_KEY_CHECKS=0, und MyISAM prüft nie, daher sind Zeilen ohne Elternzeile häufig. Der Schlüssel wird zuerst angelegt und zuletzt validiert; verwaiste Zeilen lassen ihn mit einer Warnung NOT VALID, statt das Laden abzubrechen. | |
| foreign keys to columns without a unique key | nicht angelegtPostgreSQL braucht einen eindeutigen Schlüssel auf den referenzierten Spalten; InnoDB nicht. | foreign_key_skipped |
| table and column names | kleingeschrieben, wenn das eindeutig bleibtKleingeschriebene Namen lassen sich in PostgreSQL ohne Anführungszeichen schreiben. | identifier_renamed |
| names over 63 bytes | mit einem Hash gekürztNamen in PostgreSQL fassen 63 Bytes; der Hash hält gekürzte Namen unterscheidbar. | name_shortened |
| GEOMETRY, POINT, POLYGON... | WKB in byteaOhne PostGIS bewahrt das standardisierte Well-known Binary jede Geometrie exakt. | geometry_as_wkb |
| INET4, INET6, UUID (MariaDB) | inet, uuidPostgreSQL hat dieselben Typen. | |
| BINARY, VARBINARY, BLOB | byteaBytes bleiben Bytes. | |
| PARTITION BY | eine TabelleDie Zeilen bleiben dieselben; partitioniere die Tabelle in PostgreSQL neu, wenn nötig. | partitions_dropped |
| WITH SYSTEM VERSIONING (MariaDB) | eine Tabelle mit den aktuellen ZeilenEin Dump enthält nur die aktuellen Zeilen. | system_versioning_dropped |
| MyISAM, MEMORY and other engines | eine normale TabellePostgreSQL hat eine einzige Tabellen-Engine. | engine_dropped |
| views, triggers, procedures, functions and events | im Bericht aufgeführtIhr Code ist in der Sprache von MySQL geschrieben, daher werden sie aufgeführt, damit du sie neu anlegst. | not_converted_objects |
| dumps without CREATE TABLE | Typen aus den Werten gelesenEin mit --no-create-info erstellter Dump enthält nur Zeilen, daher kommen die Typen aus den Werten. | no_schema |
| geometries in a dump without CREATE TABLE | WKB in byteaDas Geometrieformat von MySQL wird an den Werten erkannt und als Standard-WKB gespeichert. | geometry_inferred |
| _ci collations | exakter VergleichPostgreSQL vergleicht Text exakt, sofern nicht anders angegeben. | case_insensitive_collation |
| SET NAMES utf8 from utf8mb4 tables | konvertiert, mit einer Warnungmysqldump aus MySQL 5.x schreibt standardmäßig 3-Byte-utf8 und ersetzt jedes Emoji durch ein Fragezeichen, bevor ein Konverter die Datei sieht. | dump_lost_four_byte_chars |
| dumps without SET NAMES whose text isn't UTF-8 | als Latin-1 (Windows-1252) gelesenlatin1 in MySQL ist Windows-1252, die übliche Kodierung solcher Dumps. | charset_guessed |
| sjis, cp932, gbk, big5 dumps | als UTF-8 gelesen, mit einer WarnungDiese Kodierungen werden noch nicht dekodiert. | unsupported_charset |
| UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows | ausgewertetManche Dump-Werkzeuge schreiben Werte als solche Aufrufe; sie werden als die Werte gelesen, die sie ergeben, wobei geografisches WKT auf Längengrad zuerst umgestellt wird. | |
| other expressions in rows | NULLEin Konverter, der keine Datenbank ausführt, kann sie nicht auswerten. | expression_value |
| DEFAULT in VALUES | der Standardwert der SpalteEin literaler Standardwert wird in die Zeile kopiert. | default_keyword_filled |
| DEFAULT in VALUES without a literal default | NULLEs gibt keinen Wert zum Kopieren. | default_keyword_null |
| REPLACE and INSERT IGNORE | normale ZeilenEin Dump hat eine Zeile pro Schlüssel, daher sind die Zeilen dieselben. | replace_as_insert |
| types IntoSQL doesn't know | textText nimmt jeden Wert auf; ändere den Typ nach dem Laden. | unknown_type |
| values that don't fit their column | NULLEin beschädigter oder von Hand geschriebener Dump kann Werte enthalten, die seine Spalte nicht aufnehmen kann; sie werden NULL, damit der Rest lädt. | invalid_value_null |
| unreadable geometries | NULLBytes, die keine Geometrie sind, lassen sich nicht als solche speichern. | bad_geometry |
| CREATE SEQUENCE (MariaDB) | CREATE SEQUENCE mit ihrem aktuellen WertSequenzen in PostgreSQL nehmen dieselben Optionen an. |
Das Skript in PostgreSQL laden
Das Skript legt die Tabellen an, kopiert die Zeilen und fügt dann Schlüssel, Indizes und Fremdschlüssel hinzu, so lädt es schnell und hält beim ersten Fehler an.
psql -v ON_ERROR_STOP=1 -d mydb -f mysql.postgresql.sqlFremdschlüssel werden als NOT VALID hinzugefügt und dann validiert. Verweist eine Zeile auf eine Zeile, die es nicht gibt, nennt dir das Skript den betroffenen Fremdschlüssel, statt auf halbem Weg abzubrechen.
Lege bei einem großen Dump zuerst die Datenbank an und führe das Skript mit psql auf dem Rechner aus, auf dem der Server läuft.
Den MySQL-Dump für das beste Ergebnis erstellen
Der Konverter liest, was der Dump enthält. Mit diesen Optionen bleibt mehr von deinen Daten darin:
- --default-character-set=utf8mb4 bewahrt Emoji und andere 4-Byte-Zeichen, die ein utf8-Dump verliert, bevor der Konverter ihn sieht.
- --hex-blob schreibt Binärspalten hexadezimal, was das Kopieren zwischen Systemen übersteht.
- --routines --triggers --events fügen diese Objekte dem Dump hinzu, damit der Bericht sie auflisten kann.
Was nicht konvertiert wird
Sichten, Trigger, gespeicherte Prozeduren, Funktionen und Ereignisse sind in der eigenen Sprache jeder Datenbank geschrieben. Sie stehen mit Namen im Bericht und werden nie stillschweigend weggelassen, damit du sie für PostgreSQL neu schreiben kannst.
Im Vergleich mit pgloader gemessen
Dieselben Dumps, konvertiert und geladen von IntoSQL und von pgloader 3.6.7 auf einem Rechner (ein i9-10900K mit 10 Kernen und einer SATA SSD), im Oktober 2026.
- Eine TINYINT(1)-Spalte mit 2, -1 und -5: pgloader lädt für jeden Wert true, ohne Warnung. IntoSQL behält die Zahlen, und der Bericht sagt warum.
- Ein Dump mit dem Standardwert 'it's', einer Tabelle mit einem Backtick im Namen, einem ENUM mit dem leeren Wert und einer TIME über 24 Stunden: pgloader bricht ab. IntoSQL konvertiert und lädt ihn.
- Was pgloader lädt, hat seine Primärschlüssel, eindeutigen Schlüssel, Fremdschlüssel und Identity-Spalten verloren. IntoSQL behält sie alle.
- Ein Dump von 5.3 GB mit 30 Millionen Zeilen: pgloader braucht ihn zuerst in einem laufenden MySQL wiederhergestellt, was 18 Minuten dauert, und dann geht ihm mit den Standardeinstellungen der Speicher aus. IntoSQL konvertiert den Dump in 10 Sekunden, und psql lädt das Skript in etwa 3 Minuten.
- Über 476 Dumps aus MySQL 5.5 bis 9 und MariaDB 10.3 bis 12, geladen in PostgreSQL 13 und 18: keine Ladefehler und kein geänderter Wert ohne eine Zeile im Bericht.
Fragen zu MySQL zu PostgreSQL
Keine Antwort gefunden? Schreib uns über die Kontaktseite, und ein Mensch antwortet. Kontaktseite
Wird mein Dump hochgeladen?
Nein. Der Konverter läuft in deinem Browser und liest den Dump stückweise von deiner Festplatte, daher verlässt er nie deinen Computer. Die Desktop-App und die Kommandozeile konvertieren ebenfalls auf deinem Computer.
Wie groß darf der Dump sein?
Der Konverter hält nie den ganzen Dump im Speicher: Er liest die Datei zweimal, in Abschnitten, und schreibt das Skript fortlaufend. Dumps von mehreren Gigabyte werden im Browser, in der Desktop-App und mit der Kommandozeile konvertiert.
Liest es MariaDB-Dumps?
Ja. Dumps aus mariadb-dump und aus MariaDB 10.3 bis 12 werden genauso konvertiert, einschließlich der INET6-, INET4- und UUID-Spalten von MariaDB.
Was passiert mit dem Datum 0000-00-00?
PostgreSQL kennt kein Null-Datum, daher werden diese Werte NULL, und die Spalte akzeptiert NULL. Der Bericht nennt, wie viele es in jeder Spalte waren.
Bleiben AUTO_INCREMENT-Werte erhalten?
Ja. Jede AUTO_INCREMENT-Spalte wird eine Identity-Spalte, jede Zeile behält ihre ID, und die nächste ID macht nach der höchsten weiter oder nach dem AUTO_INCREMENT der Tabelle, wenn das höher ist.
Wird TINYINT(1) zu boolean?
Nur wenn jeder Wert der Spalte 0 oder 1 ist. Enthält sie andere Zahlen wie 2 oder -1, bleibt sie eine Zahl, und der Bericht sagt warum, damit kein Wert versehentlich zu true wird.
Was ist mit gespeicherten Prozeduren und Triggern?
Sie stehen mit Namen im Bericht und werden nicht konvertiert, weil jede Datenbank sie in ihrer eigenen Sprache schreibt. ON UPDATE CURRENT_TIMESTAMP ist die Ausnahme: Es wird ein PostgreSQL-Trigger.
Auf welchen PostgreSQL-Versionen lädt das Skript?
PostgreSQL 13 bis 18. Dasselbe Skript lädt auf jeder davon und hält beim ersten Fehler an, falls etwas schiefgeht.