Przejdź do treści

Konwersja MySQL do PostgreSQL

Upuść zrzut z mysqldump, mariadb-dump, mysqlpump, phpMyAdmin lub Adminer jako .sql lub .sql.gz i pobierz skrypt PostgreSQL z tymi samymi tabelami, kluczami, indeksami i danymi. Każda wartość, której PostgreSQL nie może przechować bez zmian, i każdy obiekt, którego nie przenosi, są wymienione w raporcie.

Twój plik
sqlshop_2024.sqlPlik przykładowy, 5,1 kB
Twoja baza danych

18 wierszy przekonwertowanych, 8 poprawek, brak odrzuconych

Pokaż

Tabele w pliku shop_2024.sqlZrzut MySQL 8.0.46 · zapisany przez mysqldump · utf8mb4

  • customers8 wierszy3
    7 kolumn
    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 wierszy5
    9 kolumn
    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();

Poprawki

  1. "id" w tabeli customers jest bez znaku, więc ma typ bigint z ograniczeniem CHECK, że nie jest ujemna.
  2. "signed_up" w tabeli customers dopuszcza teraz NULL: części jej wartości nie dało się zapisać i są NULL.
  3. Wartość domyślna "signed_up" w tabeli customers, 0000‑00‑00, nie jest datą akceptowaną przez PostgreSQL, więc kolumna nie ma wartości domyślnej.
  4. "customer_id" w tabeli orders jest bez znaku, więc ma typ bigint z ograniczeniem CHECK, że nie jest ujemna.
  5. "id" w tabeli orders jest bez znaku, więc ma typ bigint z ograniczeniem CHECK, że nie jest ujemna.
  6. "items" w tabeli orders jest bez znaku, więc ma typ integer z ograniczeniem CHECK, że nie jest ujemna.
  7. W "status" tabeli orders 1 wiersz zawiera pustą wartość, którą MySQL zapisuje dla błędnych danych; ograniczenie CHECK kolumny ją dopuszcza.
  8. "updated_at" w tabeli orders zmienia się na bieżący czas przy każdej aktualizacji; w PostgreSQL robi to wyzwalacz.

Do sprawdzenia

  • W "signed_up" tabeli customers zamieniono na NULL 1 wartość z zerową lub niemożliwą datą, np. 0000‑00‑00, bo PostgreSQL nie zapisuje takich dat. Jeśli te daty coś znaczą, zastąp je w źródle prawdziwymi, zanim zrobisz zrzut.
  • W "shipped_at" tabeli orders zamieniono na NULL 1 wartość z zerową lub niemożliwą datą, np. 0000‑00‑00 00:00:00, bo PostgreSQL nie zapisuje takich dat. Jeśli te daty coś znaczą, zastąp je w źródle prawdziwymi, zanim zrobisz zrzut.

Plik jest konwertowany na tym komputerze i nigdy nie jest wysyłany.

Inne przykłady:

Co zmienia się między MySQL a PostgreSQL

Każdy wiersz to reguła stosowana przez konwerter, z wpisem, który dodaje do raportu, gdy reguła coś zmienia w twoich danych.

  • TINYINT(1), BOOL

    boolean, gdy każda wartość to 0 lub 1, w przeciwnym razie smallint

    BOOL w MySQL to TINYINT(1), który mieści dowolną liczbę od -128 do 127. Kolumna z samymi 0 i 1 staje się boolean; kolumna z 2 lub -1 pozostaje liczbą, aby żadna wartość nie zmieniła znaczenia.

    Wpis w raporcie: bool_kept_numeric

  • UNSIGNED integers

    następny szerszy typ całkowity z CHECK, że wartość nie jest ujemna; BIGINT UNSIGNED to bigint, gdy każda wartość się mieści, w przeciwnym razie numeric(20,0)

    PostgreSQL nie ma liczb całkowitych bez znaku. Szerszy typ mieści cały zakres bez znaku, a ograniczenie CHECK blokuje wartości ujemne, tak jak MySQL.

    Wpis w raporcie: unsigned_widened

  • ZEROFILL and display widths

    usunięte

    Dopełnienie zerami to sposób wyświetlania liczby, a nie jej część. Liczby zostają zachowane; jeśli dopełnienie jest ważne, formatuj je przy odczycie.

    Wpis w raporcie: zerofill_dropped

  • FLOAT(M,D)

    real lub double precision, bez skali

    Liczby zmiennoprzecinkowe w PostgreSQL nie mają stałej skali. Zapisane wartości są kopiowane bez zmian.

    Wpis w raporcie: float_scale_dropped

  • BIT(1), BIT(n)

    boolean, bit(n)

    Pojedynczy bit to tak lub nie; szersze pola bitowe zachowują każdy bit.

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

    NULL

    PostgreSQL przechowuje tylko prawdziwe daty kalendarzowe. MySQL zachowuje daty zerowe i niemożliwe, gdy pozwala na to tryb SQL; stają się NULL, a raport je liczy.

    Wpis w raporcie: invalid_date_null

  • NOT NULL columns with values that become NULL

    NOT NULL usunięte

    Kolumna, której wartości musiały stać się NULL, nie może zachować NOT NULL, bo ładowanie by się zatrzymało.

    Wpis w raporcie: not_null_relaxed

  • DEFAULT '0000-00-00'

    brak wartości domyślnej

    Wartość domyślna, której PostgreSQL nie może zapisać, jest pomijana, a nie zastępowana zmyśloną datą.

    Wpis w raporcie: zero_date_default

  • TIME

    time, gdy każda wartość mieści się w dobie, w przeciwnym razie interval

    TIME w MySQL to czas trwania od -838:59:59 do 838:59:59. Kolumna, której wartości mieszczą się w dobie, pozostaje godziną dnia; kolumna z dłuższymi lub ujemnymi wartościami staje się interval.

    Wpis w raporcie: time_as_interval

  • YEAR

    smallint z CHECK zakresu

    PostgreSQL nie ma typu roku; ograniczenie CHECK zachowuje te same wartości, na które pozwala MySQL.

  • CHAR(n)

    varchar(n)

    MySQL usuwa końcowe spacje z wartości CHAR przy odczycie, a char(n) w PostgreSQL dodałby je z powrotem, więc varchar(n) zachowuje wartości tak, jak zwraca je MySQL.

  • DATETIME

    timestamp

    DATETIME to wskazanie zegara bez strefy czasowej, jak timestamp w PostgreSQL.

  • TIMESTAMP

    timestamptz w strefie czasowej podanej w zrzucie, UTC, gdy jej nie podaje

    TIMESTAMP to moment w czasie. Zrzuty podają strefę, w której go zapisały (mysqldump zapisuje UTC); bez niej wartości są odczytywane jako UTC, a raport o tym mówi.

    Wpis w raporcie: timestamp_zone_assumed

  • ENUM

    text z CHECK dozwolonych wartości

    Tekst z ograniczeniem CHECK zachowuje te same wartości i łatwiej go zmienić niż typ enum w PostgreSQL.

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

    pusta wartość dopuszczona przez CHECK

    MySQL zapisuje pusty ciąg, gdy w łagodnym trybie SQL do ENUM trafia błędna wartość. Te wiersze są zachowywane, a ograniczenie CHECK je dopuszcza.

    Wpis w raporcie: enum_invalid_value

  • ENUM or SET holding values outside its list

    bez CHECK

    Wartości spoza listy nie przeszłyby ograniczenia CHECK, więc nie jest ono tworzone.

    Wpis w raporcie: enum_check_skipped

  • SET

    text[] z CHECK, że każdy element jest na liście

    Tablica przechowuje każdy element osobno, więc zapytania mogą sprawdzać przynależność.

  • JSON

    jsonb; json, gdy wartość zawiera \u0000

    jsonb to typ, który PostgreSQL dobrze indeksuje i przeszukuje, ale odrzuca sekwencję \u0000, więc kolumna z nią pozostaje json.

    Wpis w raporcie: json_not_jsonb

  • NUL bytes in text

    usunięte

    Tekst w PostgreSQL nie może zawierać bajtu NUL. Bajt jest usuwany, a raport liczy takie wartości.

    Wpis w raporcie: nul_removed

  • text that isn't valid in its character set

    błędne bajty jako znak zastępczy

    Tekst w PostgreSQL musi być poprawnym UTF-8.

    Wpis w raporcie: invalid_utf8_replaced

  • text decoded twice (ñ)

    skopiowane bez zmian

    Zrzut zawiera dokładnie te znaki, więc są kopiowane; raport je wskazuje.

    Wpis w raporcie: mojibake_suspected

  • AUTO_INCREMENT

    GENERATED BY DEFAULT AS IDENTITY, z licznikiem ustawionym za skopiowanymi id

    Kolumny identity przyjmują skopiowane id, a licznik idzie dalej od najwyższego z nich lub od AUTO_INCREMENT ze zrzutu, zależnie od tego, która wartość jest wyższa.

  • MyISAM AUTO_INCREMENT on the second column of a key

    jeden licznik dla tabeli

    MyISAM liczy osobno w każdej grupie pierwszej kolumny klucza; PostgreSQL ma jeden licznik na kolumnę.

    Wpis w raporcie: myisam_group_autoincrement

  • ON UPDATE CURRENT_TIMESTAMP

    wyzwalacz BEFORE UPDATE

    Wyzwalacz ustawia kolumnę na bieżący czas, gdy wiersz się zmienia, a aktualizacja jej nie ustawiła, tak jak MySQL.

    Wpis w raporcie: on_update_trigger

  • generated columns

    STORED

    PostgreSQL od 13 do 17 tylko przechowuje kolumny generowane; formuła jest ta sama.

    Wpis w raporcie: virtual_as_stored

  • generated columns with functions PostgreSQL lacks

    zwykła kolumna

    Formuła, której nie da się zapisać dla PostgreSQL, zostawia zwykłą kolumnę i wpis w raporcie.

    Wpis w raporcie: generated_not_translated

  • INVISIBLE columns

    zwykłe kolumny

    PostgreSQL nie ma niewidocznych kolumn.

    Wpis w raporcie: invisible_column

  • CHECK constraints

    przetłumaczone, wewnątrz CREATE TABLE

    Ograniczenia CHECK wewnątrz definicji są sprawdzane przy ładowaniu każdego wiersza, a nie osobnym skanem dla każdego z nich na końcu.

  • CHECK constraints with functions PostgreSQL lacks

    nie jest tworzone

    Ograniczenie CHECK, którego nie da się zapisać dla PostgreSQL, jest pomijane i zgłaszane w raporcie.

    Wpis w raporcie: check_skipped

  • CHECK ... NOT ENFORCED

    nie jest tworzone

    MySQL przechowuje te ograniczenia bez ich stosowania, więc wiersze mogą je łamać.

    Wpis w raporcie: check_not_enforced

  • defaults with functions PostgreSQL lacks

    brak wartości domyślnej

    Wartość domyślna, której nie da się zapisać dla PostgreSQL, jest pomijana i zgłaszana w raporcie.

    Wpis w raporcie: default_not_translated

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

    indeks na left(c, 20)

    PostgreSQL indeksuje wyrażenia, więc indeksowany jest ten sam prefiks.

    Wpis w raporcie: prefix_unique

  • FULLTEXT indexes

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

    Wyszukiwanie pełnotekstowe działa z @@ zamiast MATCH ... AGAINST.

    Wpis w raporcie: fulltext_as_gin

  • indexes on values over 2,000 bytes

    hash

    Wpis btree nie pomieści tak długich wartości; indeks hash nadal znajduje równe wartości.

    Wpis w raporcie: index_on_wide_values

  • SPATIAL indexes

    nie jest tworzony bez PostGIS

    Indeksy przestrzenne wymagają PostGIS.

    Wpis w raporcie: spatial_index_skipped

  • functional indexes PostgreSQL can't write

    nie jest tworzony

    Indeks na wyrażeniu, którego nie da się zapisać dla PostgreSQL, jest pomijany i zgłaszany w raporcie.

    Wpis w raporcie: index_skipped

  • foreign keys

    po danych, jako NOT VALID, potem sprawdzany z ostrzeżeniem o osieroconych wierszach

    Zrzuty ładują się z FOREIGN_KEY_CHECKS=0, a MyISAM nigdy nie sprawdza kluczy, więc wiersze bez rodzica są częste. Klucz jest tworzony najpierw i sprawdzany na końcu; osierocone wiersze zostawiają go jako NOT VALID z ostrzeżeniem, zamiast zatrzymać ładowanie.

  • foreign keys to columns without a unique key

    nie jest tworzony

    PostgreSQL wymaga klucza unikalnego na kolumnach, do których się odwołuje; InnoDB nie.

    Wpis w raporcie: foreign_key_skipped

  • table and column names

    małe litery, gdy nazwy pozostają unikalne

    Nazwy z małych liter można w PostgreSQL pisać bez cudzysłowów.

    Wpis w raporcie: identifier_renamed

  • names over 63 bytes

    skrócone z hashem

    Nazwy w PostgreSQL mają do 63 bajtów; hash odróżnia skrócone nazwy od siebie.

    Wpis w raporcie: name_shortened

  • GEOMETRY, POINT, POLYGON...

    WKB w bytea

    Bez PostGIS standardowy format well-known binary zachowuje dokładnie każdą geometrię.

    Wpis w raporcie: geometry_as_wkb

  • INET4, INET6, UUID (MariaDB)

    inet, uuid

    PostgreSQL ma te same typy.

  • BINARY, VARBINARY, BLOB

    bytea

    Bajty pozostają bajtami.

  • PARTITION BY

    jedna tabela

    Wiersze są te same; jeśli trzeba, podziel tabelę na partycje ponownie w PostgreSQL.

    Wpis w raporcie: partitions_dropped

  • WITH SYSTEM VERSIONING (MariaDB)

    tabela z bieżącymi wierszami

    Zrzut zawiera tylko bieżące wiersze.

    Wpis w raporcie: system_versioning_dropped

  • MyISAM, MEMORY and other engines

    zwykła tabela

    PostgreSQL ma jeden silnik tabel.

    Wpis w raporcie: engine_dropped

  • views, triggers, procedures, functions and events

    wymienione w raporcie

    Ich kod jest napisany w języku MySQL, więc trafiają na listę do ręcznego odtworzenia.

    Wpis w raporcie: not_converted_objects

  • dumps without CREATE TABLE

    typy odczytane z wartości

    Zrzut zrobiony z --no-create-info zawiera tylko wiersze, więc typy pochodzą z wartości.

    Wpis w raporcie: no_schema

  • geometries in a dump without CREATE TABLE

    WKB w bytea

    Format geometrii MySQL jest rozpoznawany po wartościach i zapisywany jako standardowe WKB.

    Wpis w raporcie: geometry_inferred

  • _ci collations

    dokładne porównanie

    PostgreSQL porównuje tekst dokładnie, chyba że ustawisz inaczej.

    Wpis w raporcie: case_insensitive_collation

  • SET NAMES utf8 from utf8mb4 tables

    przekonwertowane, z ostrzeżeniem

    mysqldump z MySQL 5.x domyślnie zapisuje 3-bajtowe utf8 i zastępuje każde emoji znakiem zapytania, zanim jakikolwiek konwerter zobaczy plik.

    Wpis w raporcie: dump_lost_four_byte_chars

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

    odczytane jako Latin-1 (Windows-1252)

    latin1 w MySQL to Windows-1252, zwykłe kodowanie takich zrzutów.

    Wpis w raporcie: charset_guessed

  • sjis, cp932, gbk, big5 dumps

    odczytane jako UTF-8, z ostrzeżeniem

    Te kodowania nie są jeszcze dekodowane.

    Wpis w raporcie: unsupported_charset

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

    obliczone

    Niektóre narzędzia do zrzutów zapisują wartości jako takie wywołania; są odczytywane jako wartości, które zwracają, przy czym w geograficznym WKT najpierw jest długość geograficzna.

  • other expressions in rows

    NULL

    Konwerter, który nie uruchamia bazy danych, nie może ich obliczyć.

    Wpis w raporcie: expression_value

  • DEFAULT in VALUES

    wartość domyślna kolumny

    Dosłowna wartość domyślna jest kopiowana do wiersza.

    Wpis w raporcie: default_keyword_filled

  • DEFAULT in VALUES without a literal default

    NULL

    Nie ma wartości do skopiowania.

    Wpis w raporcie: default_keyword_null

  • REPLACE and INSERT IGNORE

    zwykłe wiersze

    Zrzut ma jeden wiersz na klucz, więc wiersze są te same.

    Wpis w raporcie: replace_as_insert

  • types IntoSQL doesn't know

    text

    Tekst mieści każdą wartość; zmień typ po załadowaniu.

    Wpis w raporcie: unknown_type

  • values that don't fit their column

    NULL

    Uszkodzony lub ręcznie pisany zrzut może zawierać wartości, których kolumna nie przyjmie; stają się NULL, aby reszta się załadowała.

    Wpis w raporcie: invalid_value_null

  • unreadable geometries

    NULL

    Bajtów, które nie są geometrią, nie da się zapisać jako geometrii.

    Wpis w raporcie: bad_geometry

  • CREATE SEQUENCE (MariaDB)

    CREATE SEQUENCE z bieżącą wartością

    Sekwencje w PostgreSQL przyjmują te same opcje.

W MySQLW PostgreSQLWpis w raporcie
TINYINT(1), BOOLboolean, gdy każda wartość to 0 lub 1, w przeciwnym razie smallintBOOL w MySQL to TINYINT(1), który mieści dowolną liczbę od -128 do 127. Kolumna z samymi 0 i 1 staje się boolean; kolumna z 2 lub -1 pozostaje liczbą, aby żadna wartość nie zmieniła znaczenia.bool_kept_numeric
UNSIGNED integersnastępny szerszy typ całkowity z CHECK, że wartość nie jest ujemna; BIGINT UNSIGNED to bigint, gdy każda wartość się mieści, w przeciwnym razie numeric(20,0)PostgreSQL nie ma liczb całkowitych bez znaku. Szerszy typ mieści cały zakres bez znaku, a ograniczenie CHECK blokuje wartości ujemne, tak jak MySQL.unsigned_widened
ZEROFILL and display widthsusunięteDopełnienie zerami to sposób wyświetlania liczby, a nie jej część. Liczby zostają zachowane; jeśli dopełnienie jest ważne, formatuj je przy odczycie.zerofill_dropped
FLOAT(M,D)real lub double precision, bez skaliLiczby zmiennoprzecinkowe w PostgreSQL nie mają stałej skali. Zapisane wartości są kopiowane bez zmian.float_scale_dropped
BIT(1), BIT(n)boolean, bit(n)Pojedynczy bit to tak lub nie; szersze pola bitowe zachowują każdy bit.
zero, partial or impossible dates (0000-00-00, 2024-02-30)NULLPostgreSQL przechowuje tylko prawdziwe daty kalendarzowe. MySQL zachowuje daty zerowe i niemożliwe, gdy pozwala na to tryb SQL; stają się NULL, a raport je liczy.invalid_date_null
NOT NULL columns with values that become NULLNOT NULL usunięteKolumna, której wartości musiały stać się NULL, nie może zachować NOT NULL, bo ładowanie by się zatrzymało.not_null_relaxed
DEFAULT '0000-00-00'brak wartości domyślnejWartość domyślna, której PostgreSQL nie może zapisać, jest pomijana, a nie zastępowana zmyśloną datą.zero_date_default
TIMEtime, gdy każda wartość mieści się w dobie, w przeciwnym razie intervalTIME w MySQL to czas trwania od -838:59:59 do 838:59:59. Kolumna, której wartości mieszczą się w dobie, pozostaje godziną dnia; kolumna z dłuższymi lub ujemnymi wartościami staje się interval.time_as_interval
YEARsmallint z CHECK zakresuPostgreSQL nie ma typu roku; ograniczenie CHECK zachowuje te same wartości, na które pozwala MySQL.
CHAR(n)varchar(n)MySQL usuwa końcowe spacje z wartości CHAR przy odczycie, a char(n) w PostgreSQL dodałby je z powrotem, więc varchar(n) zachowuje wartości tak, jak zwraca je MySQL.
DATETIMEtimestampDATETIME to wskazanie zegara bez strefy czasowej, jak timestamp w PostgreSQL.
TIMESTAMPtimestamptz w strefie czasowej podanej w zrzucie, UTC, gdy jej nie podajeTIMESTAMP to moment w czasie. Zrzuty podają strefę, w której go zapisały (mysqldump zapisuje UTC); bez niej wartości są odczytywane jako UTC, a raport o tym mówi.timestamp_zone_assumed
ENUMtext z CHECK dozwolonych wartościTekst z ograniczeniem CHECK zachowuje te same wartości i łatwiej go zmienić niż typ enum w PostgreSQL.
ENUM holding '' (MySQL's error value)pusta wartość dopuszczona przez CHECKMySQL zapisuje pusty ciąg, gdy w łagodnym trybie SQL do ENUM trafia błędna wartość. Te wiersze są zachowywane, a ograniczenie CHECK je dopuszcza.enum_invalid_value
ENUM or SET holding values outside its listbez CHECKWartości spoza listy nie przeszłyby ograniczenia CHECK, więc nie jest ono tworzone.enum_check_skipped
SETtext[] z CHECK, że każdy element jest na liścieTablica przechowuje każdy element osobno, więc zapytania mogą sprawdzać przynależność.
JSONjsonb; json, gdy wartość zawiera \u0000jsonb to typ, który PostgreSQL dobrze indeksuje i przeszukuje, ale odrzuca sekwencję \u0000, więc kolumna z nią pozostaje json.json_not_jsonb
NUL bytes in textusunięteTekst w PostgreSQL nie może zawierać bajtu NUL. Bajt jest usuwany, a raport liczy takie wartości.nul_removed
text that isn't valid in its character setbłędne bajty jako znak zastępczyTekst w PostgreSQL musi być poprawnym UTF-8.invalid_utf8_replaced
text decoded twice (ñ)skopiowane bez zmianZrzut zawiera dokładnie te znaki, więc są kopiowane; raport je wskazuje.mojibake_suspected
AUTO_INCREMENTGENERATED BY DEFAULT AS IDENTITY, z licznikiem ustawionym za skopiowanymi idKolumny identity przyjmują skopiowane id, a licznik idzie dalej od najwyższego z nich lub od AUTO_INCREMENT ze zrzutu, zależnie od tego, która wartość jest wyższa.
MyISAM AUTO_INCREMENT on the second column of a keyjeden licznik dla tabeliMyISAM liczy osobno w każdej grupie pierwszej kolumny klucza; PostgreSQL ma jeden licznik na kolumnę.myisam_group_autoincrement
ON UPDATE CURRENT_TIMESTAMPwyzwalacz BEFORE UPDATEWyzwalacz ustawia kolumnę na bieżący czas, gdy wiersz się zmienia, a aktualizacja jej nie ustawiła, tak jak MySQL.on_update_trigger
generated columnsSTOREDPostgreSQL od 13 do 17 tylko przechowuje kolumny generowane; formuła jest ta sama.virtual_as_stored
generated columns with functions PostgreSQL lackszwykła kolumnaFormuła, której nie da się zapisać dla PostgreSQL, zostawia zwykłą kolumnę i wpis w raporcie.generated_not_translated
INVISIBLE columnszwykłe kolumnyPostgreSQL nie ma niewidocznych kolumn.invisible_column
CHECK constraintsprzetłumaczone, wewnątrz CREATE TABLEOgraniczenia CHECK wewnątrz definicji są sprawdzane przy ładowaniu każdego wiersza, a nie osobnym skanem dla każdego z nich na końcu.
CHECK constraints with functions PostgreSQL lacksnie jest tworzoneOgraniczenie CHECK, którego nie da się zapisać dla PostgreSQL, jest pomijane i zgłaszane w raporcie.check_skipped
CHECK ... NOT ENFORCEDnie jest tworzoneMySQL przechowuje te ograniczenia bez ich stosowania, więc wiersze mogą je łamać.check_not_enforced
defaults with functions PostgreSQL lacksbrak wartości domyślnejWartość domyślna, której nie da się zapisać dla PostgreSQL, jest pomijana i zgłaszana w raporcie.default_not_translated
keys and indexes on a prefix, c(20)indeks na left(c, 20)PostgreSQL indeksuje wyrażenia, więc indeksowany jest ten sam prefiks.prefix_unique
FULLTEXT indexesGIN na to_tsvector('simple', ...)Wyszukiwanie pełnotekstowe działa z @@ zamiast MATCH ... AGAINST.fulltext_as_gin
indexes on values over 2,000 byteshashWpis btree nie pomieści tak długich wartości; indeks hash nadal znajduje równe wartości.index_on_wide_values
SPATIAL indexesnie jest tworzony bez PostGISIndeksy przestrzenne wymagają PostGIS.spatial_index_skipped
functional indexes PostgreSQL can't writenie jest tworzonyIndeks na wyrażeniu, którego nie da się zapisać dla PostgreSQL, jest pomijany i zgłaszany w raporcie.index_skipped
foreign keyspo danych, jako NOT VALID, potem sprawdzany z ostrzeżeniem o osieroconych wierszachZrzuty ładują się z FOREIGN_KEY_CHECKS=0, a MyISAM nigdy nie sprawdza kluczy, więc wiersze bez rodzica są częste. Klucz jest tworzony najpierw i sprawdzany na końcu; osierocone wiersze zostawiają go jako NOT VALID z ostrzeżeniem, zamiast zatrzymać ładowanie.
foreign keys to columns without a unique keynie jest tworzonyPostgreSQL wymaga klucza unikalnego na kolumnach, do których się odwołuje; InnoDB nie.foreign_key_skipped
table and column namesmałe litery, gdy nazwy pozostają unikalneNazwy z małych liter można w PostgreSQL pisać bez cudzysłowów.identifier_renamed
names over 63 bytesskrócone z hashemNazwy w PostgreSQL mają do 63 bajtów; hash odróżnia skrócone nazwy od siebie.name_shortened
GEOMETRY, POINT, POLYGON...WKB w byteaBez PostGIS standardowy format well-known binary zachowuje dokładnie każdą geometrię.geometry_as_wkb
INET4, INET6, UUID (MariaDB)inet, uuidPostgreSQL ma te same typy.
BINARY, VARBINARY, BLOBbyteaBajty pozostają bajtami.
PARTITION BYjedna tabelaWiersze są te same; jeśli trzeba, podziel tabelę na partycje ponownie w PostgreSQL.partitions_dropped
WITH SYSTEM VERSIONING (MariaDB)tabela z bieżącymi wierszamiZrzut zawiera tylko bieżące wiersze.system_versioning_dropped
MyISAM, MEMORY and other engineszwykła tabelaPostgreSQL ma jeden silnik tabel.engine_dropped
views, triggers, procedures, functions and eventswymienione w raporcieIch kod jest napisany w języku MySQL, więc trafiają na listę do ręcznego odtworzenia.not_converted_objects
dumps without CREATE TABLEtypy odczytane z wartościZrzut zrobiony z --no-create-info zawiera tylko wiersze, więc typy pochodzą z wartości.no_schema
geometries in a dump without CREATE TABLEWKB w byteaFormat geometrii MySQL jest rozpoznawany po wartościach i zapisywany jako standardowe WKB.geometry_inferred
_ci collationsdokładne porównaniePostgreSQL porównuje tekst dokładnie, chyba że ustawisz inaczej.case_insensitive_collation
SET NAMES utf8 from utf8mb4 tablesprzekonwertowane, z ostrzeżeniemmysqldump z MySQL 5.x domyślnie zapisuje 3-bajtowe utf8 i zastępuje każde emoji znakiem zapytania, zanim jakikolwiek konwerter zobaczy plik.dump_lost_four_byte_chars
dumps without SET NAMES whose text isn't UTF-8odczytane jako Latin-1 (Windows-1252)latin1 w MySQL to Windows-1252, zwykłe kodowanie takich zrzutów.charset_guessed
sjis, cp932, gbk, big5 dumpsodczytane jako UTF-8, z ostrzeżeniemTe kodowania nie są jeszcze dekodowane.unsupported_charset
UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rowsobliczoneNiektóre narzędzia do zrzutów zapisują wartości jako takie wywołania; są odczytywane jako wartości, które zwracają, przy czym w geograficznym WKT najpierw jest długość geograficzna.
other expressions in rowsNULLKonwerter, który nie uruchamia bazy danych, nie może ich obliczyć.expression_value
DEFAULT in VALUESwartość domyślna kolumnyDosłowna wartość domyślna jest kopiowana do wiersza.default_keyword_filled
DEFAULT in VALUES without a literal defaultNULLNie ma wartości do skopiowania.default_keyword_null
REPLACE and INSERT IGNOREzwykłe wierszeZrzut ma jeden wiersz na klucz, więc wiersze są te same.replace_as_insert
types IntoSQL doesn't knowtextTekst mieści każdą wartość; zmień typ po załadowaniu.unknown_type
values that don't fit their columnNULLUszkodzony lub ręcznie pisany zrzut może zawierać wartości, których kolumna nie przyjmie; stają się NULL, aby reszta się załadowała.invalid_value_null
unreadable geometriesNULLBajtów, które nie są geometrią, nie da się zapisać jako geometrii.bad_geometry
CREATE SEQUENCE (MariaDB)CREATE SEQUENCE z bieżącą wartościąSekwencje w PostgreSQL przyjmują te same opcje.

Załaduj skrypt do PostgreSQL

Skrypt tworzy tabele, kopiuje wiersze, a potem dodaje klucze, indeksy i klucze obce, więc ładuje się szybko i zatrzymuje przy pierwszym błędzie.

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

Klucze obce są dodawane jako NOT VALID, a potem sprawdzane. Jeśli wiersz wskazuje na nieistniejący wiersz, skrypt podaje, którego ograniczenia to dotyczy, zamiast przerwać w połowie.

Przy dużym zrzucie najpierw utwórz bazę danych i uruchom skrypt przez psql na tej samej maszynie co serwer.

Jak zrobić zrzut MySQL dla najlepszego wyniku

Konwerter odczytuje to, co zawiera zrzut. Te opcje zachowują w nim więcej twoich danych:

  • --default-character-set=utf8mb4 zachowuje emoji i inne 4-bajtowe znaki, które zrzut utf8 traci, zanim zobaczy go konwerter.
  • --hex-blob zapisuje kolumny binarne szesnastkowo, co przetrwa kopiowanie między systemami.
  • --routines --triggers --events dodają te obiekty do zrzutu, aby raport mógł je wymienić.

Co nie jest konwertowane

Widoki, wyzwalacze, procedury składowane, funkcje i zdarzenia są pisane we własnym języku każdej bazy danych. Raport wymienia je z nazwy i nigdy nie pomija ich po cichu, więc możesz je przepisać dla PostgreSQL.

Porównanie z pgloader

Te same zrzuty, przekonwertowane i załadowane przez IntoSQL i przez pgloader 3.6.7 na jednej maszynie (10-rdzeniowy i9-10900K z SATA SSD), w październiku 2026.

  • Kolumna TINYINT(1) z wartościami 2, -1 i -5: pgloader ładuje dla każdej true, bez ostrzeżenia. IntoSQL zachowuje liczby, a raport wyjaśnia dlaczego.
  • Zrzut z wartością domyślną 'it's', tabelą z grawisem w nazwie, ENUM z pustą wartością i TIME powyżej 24 godzin: pgloader się zatrzymuje. IntoSQL go konwertuje i ładuje.
  • To, co ładuje pgloader, traci klucze główne, unikalne i obce oraz kolumny identity. IntoSQL zachowuje każdy z nich.
  • Zrzut 5.3 GB z 30 milionami wierszy: pgloader wymaga najpierw odtworzenia go w działającym MySQL, co zajmuje 18 minut, a potem przy ustawieniach domyślnych brakuje mu pamięci. IntoSQL konwertuje zrzut w 10 sekund, a psql ładuje skrypt w około 3 minuty.
  • W 476 zrzutach z MySQL od 5.5 do 9 i MariaDB od 10.3 do 12, załadowanych do PostgreSQL 13 i 18: bez błędów ładowania, a żadna wartość nie zmieniła się bez wpisu w raporcie.

Pytania o konwersję MySQL do PostgreSQL

Nie ma tu odpowiedzi? Napisz do nas przez stronę kontaktu, a odpowie człowiek. Strona kontaktu

Czy mój zrzut jest wysyłany?

Nie. Konwerter działa w przeglądarce i odczytuje zrzut z dysku po kawałku, więc nigdy nie opuszcza on twojego komputera. Aplikacja na komputer i wiersz poleceń też konwertują na twoim komputerze.

Jak duży może być zrzut?

Konwerter nigdy nie trzyma całego zrzutu w pamięci: czyta plik dwa razy, fragmentami, i zapisuje skrypt na bieżąco. Zrzuty wielogigabajtowe konwertują się w przeglądarce, w aplikacji na komputer i w wierszu poleceń.

Czy odczytuje zrzuty MariaDB?

Tak. Zrzuty z mariadb-dump i z MariaDB od 10.3 do 12 są konwertowane tak samo, łącznie z kolumnami INET6, INET4 i UUID z MariaDB.

Co dzieje się z datami 0000-00-00?

PostgreSQL nie ma daty zerowej, więc stają się NULL, a kolumna przyjmuje NULL. Raport podaje, ile ich było w każdej kolumnie.

Czy wartości AUTO_INCREMENT są zachowywane?

Tak. Każda kolumna AUTO_INCREMENT staje się kolumną identity, każdy wiersz zachowuje swoje id, a kolejne id idzie po najwyższym albo po AUTO_INCREMENT tabeli, jeśli ten jest wyższy.

Czy TINYINT(1) staje się boolean?

Tylko gdy każda wartość w kolumnie to 0 lub 1. Jeśli zawiera inne liczby, np. 2 lub -1, pozostaje liczbą, a raport wyjaśnia dlaczego, więc żadna wartość nie zmieni się przypadkiem w true.

A procedury składowane i wyzwalacze?

Raport wymienia je z nazwy, ale nie są konwertowane, bo każda baza danych zapisuje je we własnym języku. Wyjątkiem jest ON UPDATE CURRENT_TIMESTAMP: staje się wyzwalaczem PostgreSQL.

Na jakich wersjach PostgreSQL ładuje się skrypt?

PostgreSQL od 13 do 18. Ten sam skrypt ładuje się na każdej z nich i zatrzymuje przy pierwszym błędzie, jeśli coś pójdzie nie tak.