Zum Inhalt springen

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.

Deine Datei
sqlshop_2024.sqlBeispieldatei, 5,1 kB
Deine Datenbank

18 Zeilen konvertiert, 8 Korrekturen, keine abgelehnt

Anzeigen

Tabellen in shop_2024.sqlMySQL-8.0.46-Dump · erstellt mit mysqldump · utf8mb4

  • customers8 Zeilen3
    7 Spalten
    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 Zeilen5
    9 Spalten
    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();

Korrekturen

  1. "id" von customers ist vorzeichenlos und wurde deshalb bigint mit einer CHECK-Bedingung gegen negative Werte.
  2. "signed_up" von customers erlaubt jetzt NULL: Einige Werte ließen sich nicht speichern und sind NULL.
  3. Der Standardwert von "signed_up" in customers, 0000‑00‑00, ist kein Datum, das PostgreSQL akzeptiert, daher hat die Spalte keinen Standardwert.
  4. "customer_id" von orders ist vorzeichenlos und wurde deshalb bigint mit einer CHECK-Bedingung gegen negative Werte.
  5. "id" von orders ist vorzeichenlos und wurde deshalb bigint mit einer CHECK-Bedingung gegen negative Werte.
  6. "items" von orders ist vorzeichenlos und wurde deshalb integer mit einer CHECK-Bedingung gegen negative Werte.
  7. 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.
  8. "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 MySQLIn PostgreSQLBerichtszeile
TINYINT(1), BOOLboolean, 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 integersder 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 widthsentferntDas 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 NULLNOT 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
TIMEtime, 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
YEARsmallint 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.
DATETIMEtimestampEin DATETIME ist eine Datums- und Uhrzeitangabe ohne Zeitzone, wie timestamp in PostgreSQL.
TIMESTAMPtimestamptz 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
ENUMtext 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 listkein CHECKWerte, die die Liste nicht nennt, würden die CHECK-Bedingung verletzen, daher wird sie nicht angelegt.enum_check_skipped
SETtext[] 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.
JSONjsonb; 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 textentferntPostgreSQL-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 setdie 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_INCREMENTGENERATED 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 keyein 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_TIMESTAMPein 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 columnsSTOREDPostgreSQL 13 bis 17 kennen nur gespeicherte generierte Spalten; die Formel bleibt dieselbe.virtual_as_stored
generated columns with functions PostgreSQL lackseine 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 columnsnormale 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 lacksnicht angelegtEine CHECK-Bedingung, die sich nicht für PostgreSQL schreiben lässt, wird weggelassen und gemeldet.check_skipped
CHECK ... NOT ENFORCEDnicht angelegtMySQL behält diese CHECK-Bedingungen, ohne sie anzuwenden, daher können Zeilen sie verletzen.check_not_enforced
defaults with functions PostgreSQL lackskein 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 indexesGIN auf to_tsvector('simple', ...)Die Volltextsuche funktioniert mit @@ statt MATCH ... AGAINST.fulltext_as_gin
indexes on values over 2,000 byteshashEin btree-Eintrag kann so lange Werte nicht aufnehmen; ein hash-Index findet gleiche Werte trotzdem.index_on_wide_values
SPATIAL indexesohne PostGIS nicht angelegtRäumliche Indizes brauchen PostGIS.spatial_index_skipped
functional indexes PostgreSQL can't writenicht angelegtEin Index auf einen Ausdruck, der sich nicht für PostgreSQL schreiben lässt, wird weggelassen und gemeldet.index_skipped
foreign keysnach 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 keynicht angelegtPostgreSQL braucht einen eindeutigen Schlüssel auf den referenzierten Spalten; InnoDB nicht.foreign_key_skipped
table and column nameskleingeschrieben, wenn das eindeutig bleibtKleingeschriebene Namen lassen sich in PostgreSQL ohne Anführungszeichen schreiben.identifier_renamed
names over 63 bytesmit 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, BLOBbyteaBytes bleiben Bytes.
PARTITION BYeine 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 engineseine normale TabellePostgreSQL hat eine einzige Tabellen-Engine.engine_dropped
views, triggers, procedures, functions and eventsim 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 TABLETypen 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 TABLEWKB in byteaDas Geometrieformat von MySQL wird an den Werten erkannt und als Standard-WKB gespeichert.geometry_inferred
_ci collationsexakter VergleichPostgreSQL vergleicht Text exakt, sofern nicht anders angegeben.case_insensitive_collation
SET NAMES utf8 from utf8mb4 tableskonvertiert, 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-8als Latin-1 (Windows-1252) gelesenlatin1 in MySQL ist Windows-1252, die übliche Kodierung solcher Dumps.charset_guessed
sjis, cp932, gbk, big5 dumpsals UTF-8 gelesen, mit einer WarnungDiese Kodierungen werden noch nicht dekodiert.unsupported_charset
UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rowsausgewertetManche 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 rowsNULLEin Konverter, der keine Datenbank ausführt, kann sie nicht auswerten.expression_value
DEFAULT in VALUESder Standardwert der SpalteEin literaler Standardwert wird in die Zeile kopiert.default_keyword_filled
DEFAULT in VALUES without a literal defaultNULLEs gibt keinen Wert zum Kopieren.default_keyword_null
REPLACE and INSERT IGNOREnormale ZeilenEin Dump hat eine Zeile pro Schlüssel, daher sind die Zeilen dieselben.replace_as_insert
types IntoSQL doesn't knowtextText nimmt jeden Wert auf; ändere den Typ nach dem Laden.unknown_type
values that don't fit their columnNULLEin 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 geometriesNULLBytes, 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.sql

Fremdschlü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.