Конвертация MySQL в PostgreSQL
Перетащите дамп из mysqldump, mariadb-dump, mysqlpump, phpMyAdmin или Adminer в формате .sql или .sql.gz и скачайте скрипт PostgreSQL с теми же таблицами, ключами, индексами и данными. Каждое значение, которое PostgreSQL не может хранить как есть, и каждый объект, который он не переносит, указаны в отчете.
18 строк конвертировано, 8 исправлений, ни одна не отклонена
Таблицы в shop_2024.sqlДамп MySQL 8.0.46 · создан программой mysqldump · utf8mb4
customers8 строк3
7 столбцов 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 строк5
9 столбцов 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();
Исправления
- Столбец "id" таблицы customers беззнаковый, поэтому он стал bigint с проверкой, что значение не отрицательное.
- Столбец "signed_up" таблицы customers теперь допускает NULL: часть его значений нельзя было сохранить, и они записаны как NULL.
- Значение по умолчанию столбца "signed_up" таблицы customers, 0000‑00‑00, не является датой, которую принимает PostgreSQL, поэтому у столбца его нет.
- Столбец "customer_id" таблицы orders беззнаковый, поэтому он стал bigint с проверкой, что значение не отрицательное.
- Столбец "id" таблицы orders беззнаковый, поэтому он стал bigint с проверкой, что значение не отрицательное.
- Столбец "items" таблицы orders беззнаковый, поэтому он стал integer с проверкой, что значение не отрицательное.
- В 1 строке столбца "status" таблицы orders пустое значение, которое MySQL записывает вместо недопустимых; проверка значений его допускает.
- Столбец "updated_at" таблицы orders получает текущее время при каждом обновлении; в PostgreSQL это делает триггер.
Требует проверки
- В "signed_up" таблицы customers 1 значение - нулевая или невозможная дата, например 0000‑00‑00; PostgreSQL не может их хранить, поэтому записан NULL. Если эти даты что-то значат, замените их в источнике настоящими до создания дампа.
- В "shipped_at" таблицы orders 1 значение - нулевая или невозможная дата, например 0000‑00‑00 00:00:00; PostgreSQL не может их хранить, поэтому записан NULL. Если эти даты что-то значат, замените их в источнике настоящими до создания дампа.
Файл конвертируется на этом компьютере и никуда не загружается.
Другие примеры:
Что меняется при переходе с MySQL на PostgreSQL
Каждая строка - это правило, которое применяет конвертер, и строка отчета, которую он пишет, когда правило что-то меняет в ваших данных.
TINYINT(1), BOOL
boolean, если каждое значение 0 или 1, иначе smallint
BOOL в MySQL - это TINYINT(1), который может хранить любое число от -128 до 127. Столбец только с 0 и 1 становится boolean; столбец с 2 или -1 остается числом, чтобы ни одно значение не изменило смысл.
Строка отчета: bool_kept_numeric
UNSIGNED integers
следующий более широкий целый тип с CHECK на неотрицательность; BIGINT UNSIGNED становится bigint, если все значения помещаются, иначе numeric(20,0)
В PostgreSQL нет беззнаковых целых. Более широкий тип вмещает весь беззнаковый диапазон, а проверка не пропускает отрицательные значения, как в MySQL.
Строка отчета: unsigned_widened
ZEROFILL and display widths
удаляется
Дополнение нулями - это способ показа числа, а не его часть. Числа сохраняются; если дополнение важно, форматируйте их при чтении.
Строка отчета: zerofill_dropped
FLOAT(M,D)
real или double precision, без масштаба
У чисел с плавающей точкой в PostgreSQL нет фиксированного масштаба. Записанные значения копируются как есть.
Строка отчета: float_scale_dropped
BIT(1), BIT(n)
boolean, bit(n)
Один бит - это да или нет; более широкие битовые поля сохраняют каждый бит.
zero, partial or impossible dates (0000-00-00, 2024-02-30)
NULL
PostgreSQL хранит только настоящие календарные даты. MySQL сохраняет нулевые и невозможные даты, если это позволяет режим SQL; они становятся NULL, а отчет их подсчитывает.
Строка отчета: invalid_date_null
NOT NULL columns with values that become NULL
NOT NULL снимается
Столбец, значения которого пришлось заменить на NULL, не может сохранить NOT NULL, иначе загрузка остановится.
Строка отчета: not_null_relaxed
DEFAULT '0000-00-00'
без значения по умолчанию
Значение по умолчанию, которое PostgreSQL не может хранить, опускается, а не заменяется выдуманной датой.
Строка отчета: zero_date_default
TIME
time, если каждое значение укладывается в сутки, иначе interval
TIME в MySQL - это длительность от -838:59:59 до 838:59:59. Столбец, значения которого укладываются в сутки, остается временем суток; столбец с более длинными или отрицательными значениями становится interval.
Строка отчета: time_as_interval
YEAR
smallint с CHECK диапазона
В PostgreSQL нет типа для года; проверка допускает те же значения, что и MySQL.
CHAR(n)
varchar(n)
MySQL убирает пробелы в конце значений CHAR при чтении, а char(n) в PostgreSQL добавил бы их обратно, поэтому varchar(n) хранит значения такими, какими их возвращает MySQL.
DATETIME
timestamp
DATETIME - это показание часов без часового пояса, как timestamp в PostgreSQL.
TIMESTAMP
timestamptz в часовом поясе, указанном в дампе, или UTC, если он не указан
TIMESTAMP - это момент времени. Дампы указывают пояс, в котором он записан (mysqldump пишет UTC); без этого значения читаются как UTC, и отчет это отмечает.
Строка отчета: timestamp_zone_assumed
ENUM
text с CHECK значений
Текст с проверкой хранит те же значения, и его проще изменить, чем тип enum в PostgreSQL.
ENUM holding '' (MySQL's error value)
пустое значение, которое допускает проверка
MySQL записывает пустую строку, когда недопустимое значение попадает в ENUM в нестрогом режиме SQL. Такие строки сохраняются, и проверка их допускает.
Строка отчета: enum_invalid_value
ENUM or SET holding values outside its list
без CHECK
Значения, которых нет в списке, не прошли бы проверку, поэтому она не создается.
Строка отчета: enum_check_skipped
SET
text[] с CHECK, что каждый элемент есть в списке
Массив хранит каждый элемент отдельно, поэтому запросы могут проверять вхождение.
JSON
jsonb; json, если значение содержит \u0000
jsonb - это тип, который PostgreSQL хорошо индексирует и запрашивает, но он не принимает экранирование \u0000, поэтому столбец с ним остается json.
Строка отчета: json_not_jsonb
NUL bytes in text
удаляется
Текст в PostgreSQL не может содержать байт NUL. Байт удаляется, а отчет подсчитывает такие значения.
Строка отчета: nul_removed
text that isn't valid in its character set
неверные байты заменяются символом замены
Текст в PostgreSQL должен быть корректным UTF-8.
Строка отчета: invalid_utf8_replaced
text decoded twice (ñ)
копируется как есть
Дамп содержит именно эти символы, поэтому они копируются; отчет на них указывает.
Строка отчета: mojibake_suspected
AUTO_INCREMENT
GENERATED BY DEFAULT AS IDENTITY, счетчик выставлен после скопированных id
Столбцы identity принимают скопированные id, а счетчик продолжается после наибольшего из них или после AUTO_INCREMENT дампа, смотря что больше.
MyISAM AUTO_INCREMENT on the second column of a key
один счетчик на таблицу
MyISAM считает отдельно для каждой группы первого столбца ключа; в PostgreSQL один счетчик на столбец.
Строка отчета: myisam_group_autoincrement
ON UPDATE CURRENT_TIMESTAMP
триггер BEFORE UPDATE
Триггер записывает в столбец текущее время, когда строка меняется, а обновление не задало его, как это делает MySQL.
Строка отчета: on_update_trigger
generated columns
STORED
PostgreSQL с 13 по 17 поддерживает только хранимые генерируемые столбцы; формула та же.
Строка отчета: virtual_as_stored
generated columns with functions PostgreSQL lacks
обычный столбец
Формула, которую нельзя записать для PostgreSQL, оставляет обычный столбец и строку в отчете.
Строка отчета: generated_not_translated
INVISIBLE columns
обычные столбцы
В PostgreSQL нет невидимых столбцов.
Строка отчета: invisible_column
CHECK constraints
переводится, внутри CREATE TABLE
Встроенные проверки выполняются при загрузке каждой строки, а не отдельным проходом для каждой проверки после загрузки.
CHECK constraints with functions PostgreSQL lacks
не создается
Проверка, которую нельзя записать для PostgreSQL, опускается и попадает в отчет.
Строка отчета: check_skipped
CHECK ... NOT ENFORCED
не создается
MySQL хранит эти проверки, но не применяет их, поэтому строки могут их нарушать.
Строка отчета: check_not_enforced
defaults with functions PostgreSQL lacks
без значения по умолчанию
Значение по умолчанию, которое нельзя записать для PostgreSQL, опускается и попадает в отчет.
Строка отчета: default_not_translated
keys and indexes on a prefix, c(20)
индекс по left(c, 20)
PostgreSQL индексирует выражения, поэтому индексируется тот же префикс.
Строка отчета: prefix_unique
FULLTEXT indexes
GIN по to_tsvector('simple', ...)
Полнотекстовый поиск работает через @@ вместо MATCH ... AGAINST.
Строка отчета: fulltext_as_gin
indexes on values over 2,000 bytes
hash
Запись btree не вмещает такие длинные значения; индекс hash все равно находит равные значения.
Строка отчета: index_on_wide_values
SPATIAL indexes
не создается без PostGIS
Пространственным индексам нужен PostGIS.
Строка отчета: spatial_index_skipped
functional indexes PostgreSQL can't write
не создается
Индекс по выражению, которое нельзя записать для PostgreSQL, опускается и попадает в отчет.
Строка отчета: index_skipped
foreign keys
после данных, NOT VALID, затем проверка с предупреждением о строках без родителя
Дампы загружаются с FOREIGN_KEY_CHECKS=0, а MyISAM ничего не проверяет, поэтому строки без родительской записи встречаются часто. Ключ создается первым и проверяется последним; при строках без родителя он остается NOT VALID с предупреждением, и загрузка не останавливается.
foreign keys to columns without a unique key
не создается
PostgreSQL требует уникальный ключ на столбцах, на которые ссылается внешний ключ; InnoDB - нет.
Строка отчета: foreign_key_skipped
table and column names
нижний регистр, если имена остаются уникальными
Имена в нижнем регистре можно писать в PostgreSQL без кавычек.
Строка отчета: identifier_renamed
names over 63 bytes
сокращается с хешем
Имена в PostgreSQL вмещают 63 байта; хеш не дает сокращенным именам совпасть.
Строка отчета: name_shortened
GEOMETRY, POINT, POLYGON...
WKB в bytea
Без PostGIS стандартный формат well-known binary точно сохраняет каждую геометрию.
Строка отчета: geometry_as_wkb
INET4, INET6, UUID (MariaDB)
inet, uuid
В PostgreSQL есть такие же типы.
BINARY, VARBINARY, BLOB
bytea
Байты остаются байтами.
PARTITION BY
одна таблица
Строки те же; при необходимости секционируйте таблицу заново в PostgreSQL.
Строка отчета: partitions_dropped
WITH SYSTEM VERSIONING (MariaDB)
таблица с текущими строками
Дамп содержит только текущие строки.
Строка отчета: system_versioning_dropped
MyISAM, MEMORY and other engines
обычная таблица
В PostgreSQL один движок таблиц.
Строка отчета: engine_dropped
views, triggers, procedures, functions and events
перечисляются в отчете
Их код написан на языке MySQL, поэтому они перечислены, чтобы вы создали их заново.
Строка отчета: not_converted_objects
dumps without CREATE TABLE
типы определяются по значениям
Дамп, созданный с --no-create-info, содержит только строки, поэтому типы берутся из значений.
Строка отчета: no_schema
geometries in a dump without CREATE TABLE
WKB в bytea
Формат геометрии MySQL распознается по значениям и сохраняется как стандартный WKB.
Строка отчета: geometry_inferred
_ci collations
точное сравнение
PostgreSQL сравнивает текст точно, если не указано иное.
Строка отчета: case_insensitive_collation
SET NAMES utf8 from utf8mb4 tables
конвертируется с предупреждением
mysqldump из MySQL 5.x по умолчанию пишет 3-байтовую utf8 и заменяет каждый эмодзи вопросительным знаком еще до того, как файл увидит какой-либо конвертер.
Строка отчета: dump_lost_four_byte_chars
dumps without SET NAMES whose text isn't UTF-8
читается как Latin-1 (Windows-1252)
latin1 в MySQL - это Windows-1252, обычная кодировка таких дампов.
Строка отчета: charset_guessed
sjis, cp932, gbk, big5 dumps
читается как UTF-8, с предупреждением
Эти кодировки пока не декодируются.
Строка отчета: unsupported_charset
UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows
вычисляются
Некоторые программы дампа записывают значения как такие вызовы; они читаются как значения, которые возвращают, а в географическом WKT долгота ставится первой.
other expressions in rows
NULL
Конвертер, который не запускает базу данных, не может их вычислить.
Строка отчета: expression_value
DEFAULT in VALUES
значение столбца по умолчанию
Литеральное значение по умолчанию копируется в строку.
Строка отчета: default_keyword_filled
DEFAULT in VALUES without a literal default
NULL
Копировать нечего.
Строка отчета: default_keyword_null
REPLACE and INSERT IGNORE
обычные строки
В дампе одна строка на ключ, поэтому строки те же.
Строка отчета: replace_as_insert
types IntoSQL doesn't know
text
Тип text вмещает любое значение; измените тип после загрузки.
Строка отчета: unknown_type
values that don't fit their column
NULL
Поврежденный или написанный вручную дамп может содержать значения, которые столбец не вмещает; они становятся NULL, чтобы остальное загрузилось.
Строка отчета: invalid_value_null
unreadable geometries
NULL
Байты, которые не являются геометрией, нельзя сохранить как геометрию.
Строка отчета: bad_geometry
CREATE SEQUENCE (MariaDB)
CREATE SEQUENCE с текущим значением
Последовательности PostgreSQL принимают те же параметры.
| В MySQL | В PostgreSQL | Строка отчета |
|---|---|---|
| TINYINT(1), BOOL | boolean, если каждое значение 0 или 1, иначе smallintBOOL в MySQL - это TINYINT(1), который может хранить любое число от -128 до 127. Столбец только с 0 и 1 становится boolean; столбец с 2 или -1 остается числом, чтобы ни одно значение не изменило смысл. | bool_kept_numeric |
| UNSIGNED integers | следующий более широкий целый тип с CHECK на неотрицательность; BIGINT UNSIGNED становится bigint, если все значения помещаются, иначе numeric(20,0)В PostgreSQL нет беззнаковых целых. Более широкий тип вмещает весь беззнаковый диапазон, а проверка не пропускает отрицательные значения, как в MySQL. | unsigned_widened |
| ZEROFILL and display widths | удаляетсяДополнение нулями - это способ показа числа, а не его часть. Числа сохраняются; если дополнение важно, форматируйте их при чтении. | zerofill_dropped |
| FLOAT(M,D) | real или double precision, без масштабаУ чисел с плавающей точкой в PostgreSQL нет фиксированного масштаба. Записанные значения копируются как есть. | float_scale_dropped |
| BIT(1), BIT(n) | boolean, bit(n)Один бит - это да или нет; более широкие битовые поля сохраняют каждый бит. | |
| zero, partial or impossible dates (0000-00-00, 2024-02-30) | NULLPostgreSQL хранит только настоящие календарные даты. MySQL сохраняет нулевые и невозможные даты, если это позволяет режим SQL; они становятся NULL, а отчет их подсчитывает. | invalid_date_null |
| NOT NULL columns with values that become NULL | NOT NULL снимаетсяСтолбец, значения которого пришлось заменить на NULL, не может сохранить NOT NULL, иначе загрузка остановится. | not_null_relaxed |
| DEFAULT '0000-00-00' | без значения по умолчаниюЗначение по умолчанию, которое PostgreSQL не может хранить, опускается, а не заменяется выдуманной датой. | zero_date_default |
| TIME | time, если каждое значение укладывается в сутки, иначе intervalTIME в MySQL - это длительность от -838:59:59 до 838:59:59. Столбец, значения которого укладываются в сутки, остается временем суток; столбец с более длинными или отрицательными значениями становится interval. | time_as_interval |
| YEAR | smallint с CHECK диапазонаВ PostgreSQL нет типа для года; проверка допускает те же значения, что и MySQL. | |
| CHAR(n) | varchar(n)MySQL убирает пробелы в конце значений CHAR при чтении, а char(n) в PostgreSQL добавил бы их обратно, поэтому varchar(n) хранит значения такими, какими их возвращает MySQL. | |
| DATETIME | timestampDATETIME - это показание часов без часового пояса, как timestamp в PostgreSQL. | |
| TIMESTAMP | timestamptz в часовом поясе, указанном в дампе, или UTC, если он не указанTIMESTAMP - это момент времени. Дампы указывают пояс, в котором он записан (mysqldump пишет UTC); без этого значения читаются как UTC, и отчет это отмечает. | timestamp_zone_assumed |
| ENUM | text с CHECK значенийТекст с проверкой хранит те же значения, и его проще изменить, чем тип enum в PostgreSQL. | |
| ENUM holding '' (MySQL's error value) | пустое значение, которое допускает проверкаMySQL записывает пустую строку, когда недопустимое значение попадает в ENUM в нестрогом режиме SQL. Такие строки сохраняются, и проверка их допускает. | enum_invalid_value |
| ENUM or SET holding values outside its list | без CHECKЗначения, которых нет в списке, не прошли бы проверку, поэтому она не создается. | enum_check_skipped |
| SET | text[] с CHECK, что каждый элемент есть в спискеМассив хранит каждый элемент отдельно, поэтому запросы могут проверять вхождение. | |
| JSON | jsonb; json, если значение содержит \u0000jsonb - это тип, который PostgreSQL хорошо индексирует и запрашивает, но он не принимает экранирование \u0000, поэтому столбец с ним остается json. | json_not_jsonb |
| NUL bytes in text | удаляетсяТекст в PostgreSQL не может содержать байт NUL. Байт удаляется, а отчет подсчитывает такие значения. | nul_removed |
| text that isn't valid in its character set | неверные байты заменяются символом заменыТекст в PostgreSQL должен быть корректным UTF-8. | invalid_utf8_replaced |
| text decoded twice (ñ) | копируется как естьДамп содержит именно эти символы, поэтому они копируются; отчет на них указывает. | mojibake_suspected |
| AUTO_INCREMENT | GENERATED BY DEFAULT AS IDENTITY, счетчик выставлен после скопированных idСтолбцы identity принимают скопированные id, а счетчик продолжается после наибольшего из них или после AUTO_INCREMENT дампа, смотря что больше. | |
| MyISAM AUTO_INCREMENT on the second column of a key | один счетчик на таблицуMyISAM считает отдельно для каждой группы первого столбца ключа; в PostgreSQL один счетчик на столбец. | myisam_group_autoincrement |
| ON UPDATE CURRENT_TIMESTAMP | триггер BEFORE UPDATEТриггер записывает в столбец текущее время, когда строка меняется, а обновление не задало его, как это делает MySQL. | on_update_trigger |
| generated columns | STOREDPostgreSQL с 13 по 17 поддерживает только хранимые генерируемые столбцы; формула та же. | virtual_as_stored |
| generated columns with functions PostgreSQL lacks | обычный столбецФормула, которую нельзя записать для PostgreSQL, оставляет обычный столбец и строку в отчете. | generated_not_translated |
| INVISIBLE columns | обычные столбцыВ PostgreSQL нет невидимых столбцов. | invisible_column |
| CHECK constraints | переводится, внутри CREATE TABLEВстроенные проверки выполняются при загрузке каждой строки, а не отдельным проходом для каждой проверки после загрузки. | |
| CHECK constraints with functions PostgreSQL lacks | не создаетсяПроверка, которую нельзя записать для PostgreSQL, опускается и попадает в отчет. | check_skipped |
| CHECK ... NOT ENFORCED | не создаетсяMySQL хранит эти проверки, но не применяет их, поэтому строки могут их нарушать. | check_not_enforced |
| defaults with functions PostgreSQL lacks | без значения по умолчаниюЗначение по умолчанию, которое нельзя записать для PostgreSQL, опускается и попадает в отчет. | default_not_translated |
| keys and indexes on a prefix, c(20) | индекс по left(c, 20)PostgreSQL индексирует выражения, поэтому индексируется тот же префикс. | prefix_unique |
| FULLTEXT indexes | GIN по to_tsvector('simple', ...)Полнотекстовый поиск работает через @@ вместо MATCH ... AGAINST. | fulltext_as_gin |
| indexes on values over 2,000 bytes | hashЗапись btree не вмещает такие длинные значения; индекс hash все равно находит равные значения. | index_on_wide_values |
| SPATIAL indexes | не создается без PostGISПространственным индексам нужен PostGIS. | spatial_index_skipped |
| functional indexes PostgreSQL can't write | не создаетсяИндекс по выражению, которое нельзя записать для PostgreSQL, опускается и попадает в отчет. | index_skipped |
| foreign keys | после данных, NOT VALID, затем проверка с предупреждением о строках без родителяДампы загружаются с FOREIGN_KEY_CHECKS=0, а MyISAM ничего не проверяет, поэтому строки без родительской записи встречаются часто. Ключ создается первым и проверяется последним; при строках без родителя он остается NOT VALID с предупреждением, и загрузка не останавливается. | |
| foreign keys to columns without a unique key | не создаетсяPostgreSQL требует уникальный ключ на столбцах, на которые ссылается внешний ключ; InnoDB - нет. | foreign_key_skipped |
| table and column names | нижний регистр, если имена остаются уникальнымиИмена в нижнем регистре можно писать в PostgreSQL без кавычек. | identifier_renamed |
| names over 63 bytes | сокращается с хешемИмена в PostgreSQL вмещают 63 байта; хеш не дает сокращенным именам совпасть. | name_shortened |
| GEOMETRY, POINT, POLYGON... | WKB в byteaБез PostGIS стандартный формат well-known binary точно сохраняет каждую геометрию. | geometry_as_wkb |
| INET4, INET6, UUID (MariaDB) | inet, uuidВ PostgreSQL есть такие же типы. | |
| BINARY, VARBINARY, BLOB | byteaБайты остаются байтами. | |
| PARTITION BY | одна таблицаСтроки те же; при необходимости секционируйте таблицу заново в PostgreSQL. | partitions_dropped |
| WITH SYSTEM VERSIONING (MariaDB) | таблица с текущими строкамиДамп содержит только текущие строки. | system_versioning_dropped |
| MyISAM, MEMORY and other engines | обычная таблицаВ PostgreSQL один движок таблиц. | engine_dropped |
| views, triggers, procedures, functions and events | перечисляются в отчетеИх код написан на языке MySQL, поэтому они перечислены, чтобы вы создали их заново. | not_converted_objects |
| dumps without CREATE TABLE | типы определяются по значениямДамп, созданный с --no-create-info, содержит только строки, поэтому типы берутся из значений. | no_schema |
| geometries in a dump without CREATE TABLE | WKB в byteaФормат геометрии MySQL распознается по значениям и сохраняется как стандартный WKB. | geometry_inferred |
| _ci collations | точное сравнениеPostgreSQL сравнивает текст точно, если не указано иное. | case_insensitive_collation |
| SET NAMES utf8 from utf8mb4 tables | конвертируется с предупреждениемmysqldump из MySQL 5.x по умолчанию пишет 3-байтовую utf8 и заменяет каждый эмодзи вопросительным знаком еще до того, как файл увидит какой-либо конвертер. | dump_lost_four_byte_chars |
| dumps without SET NAMES whose text isn't UTF-8 | читается как Latin-1 (Windows-1252)latin1 в MySQL - это Windows-1252, обычная кодировка таких дампов. | charset_guessed |
| sjis, cp932, gbk, big5 dumps | читается как UTF-8, с предупреждениемЭти кодировки пока не декодируются. | unsupported_charset |
| UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows | вычисляютсяНекоторые программы дампа записывают значения как такие вызовы; они читаются как значения, которые возвращают, а в географическом WKT долгота ставится первой. | |
| other expressions in rows | NULLКонвертер, который не запускает базу данных, не может их вычислить. | expression_value |
| DEFAULT in VALUES | значение столбца по умолчаниюЛитеральное значение по умолчанию копируется в строку. | default_keyword_filled |
| DEFAULT in VALUES without a literal default | NULLКопировать нечего. | default_keyword_null |
| REPLACE and INSERT IGNORE | обычные строкиВ дампе одна строка на ключ, поэтому строки те же. | replace_as_insert |
| types IntoSQL doesn't know | textТип text вмещает любое значение; измените тип после загрузки. | unknown_type |
| values that don't fit their column | NULLПоврежденный или написанный вручную дамп может содержать значения, которые столбец не вмещает; они становятся NULL, чтобы остальное загрузилось. | invalid_value_null |
| unreadable geometries | NULLБайты, которые не являются геометрией, нельзя сохранить как геометрию. | bad_geometry |
| CREATE SEQUENCE (MariaDB) | CREATE SEQUENCE с текущим значениемПоследовательности PostgreSQL принимают те же параметры. |
Загрузите скрипт в PostgreSQL
Скрипт создает таблицы, копирует строки, затем добавляет ключи, индексы и внешние ключи, поэтому загрузка идет быстро и останавливается на первой ошибке.
psql -v ON_ERROR_STOP=1 -d mydb -f mysql.postgresql.sqlВнешние ключи добавляются как NOT VALID, а затем проверяются. Если строка ссылается на несуществующую строку, скрипт сообщает, какое ограничение нарушено, а не прерывается на полпути.
Для большого дампа сначала создайте базу данных и запустите скрипт через psql на той же машине, где работает сервер.
Как создать дамп MySQL для лучшего результата
Конвертер читает то, что есть в дампе. Эти параметры сохраняют в нем больше ваших данных:
- --default-character-set=utf8mb4 сохраняет эмодзи и другие 4-байтовые символы, которые дамп в utf8 теряет еще до конвертера.
- --hex-blob записывает двоичные столбцы в шестнадцатеричном виде, который не портится при копировании между системами.
- --routines --triggers --events добавляют эти объекты в дамп, чтобы отчет мог их перечислить.
Что не конвертируется
Представления, триггеры, хранимые процедуры, функции и события написаны на собственном языке каждой базы данных. Они перечислены в отчете по имени и никогда не удаляются молча, чтобы вы могли переписать их для PostgreSQL.
Сравнение с pgloader
Одни и те же дампы, конвертированные и загруженные IntoSQL и pgloader 3.6.7 на одной машине (10-ядерный i9-10900K с SATA SSD) в октябре 2026 года.
- Столбец TINYINT(1) со значениями 2, -1 и -5: pgloader загружает для каждого true, без предупреждения. IntoSQL сохраняет числа, а отчет объясняет почему.
- Дамп со значением по умолчанию 'it's', таблицей с обратной кавычкой в имени, ENUM с пустым значением и TIME больше 24 часов: pgloader останавливается. IntoSQL конвертирует и загружает его.
- В том, что загружает pgloader, теряются первичные, уникальные и внешние ключи и identity. IntoSQL сохраняет их все.
- Дамп 5.3 GB на 30 миллионов строк: pgloader требует сначала восстановить его в работающий MySQL, что занимает 18 минут, а затем с настройками по умолчанию ему не хватает памяти. IntoSQL конвертирует дамп за 10 секунд, а psql загружает скрипт примерно за 3 минуты.
- 476 дампов из MySQL с 5.5 по 9 и MariaDB с 10.3 по 12, загруженных в PostgreSQL 13 и 18: ни одной ошибки загрузки, и ни одно значение не изменилось без строки в отчете.
Вопросы о MySQL в PostgreSQL
Нет ответа на ваш вопрос? Напишите нам через страницу контактов, и вам ответит человек. Страница контактов
Мой дамп загружается на сервер?
Нет. Конвертер работает в вашем браузере и читает дамп с диска по частям, поэтому он никогда не покидает ваш компьютер. Приложение для компьютера и командная строка тоже конвертируют на вашем компьютере.
Насколько большим может быть дамп?
Конвертер никогда не держит весь дамп в памяти: он читает файл дважды, по диапазонам, и пишет скрипт по ходу чтения. Дампы в несколько гигабайт конвертируются в браузере, в приложении для компьютера и в командной строке.
Читает ли он дампы MariaDB?
Да. Дампы из mariadb-dump и из MariaDB с 10.3 по 12 конвертируются так же, включая столбцы INET6, INET4 и UUID в MariaDB.
Что происходит с датами 0000-00-00?
В PostgreSQL нет нулевой даты, поэтому они становятся NULL, а столбец допускает NULL. Отчет показывает, сколько их было в каждом столбце.
Сохраняются ли значения AUTO_INCREMENT?
Да. Каждый столбец AUTO_INCREMENT становится столбцом identity, каждая строка сохраняет свой id, а следующий id идет после наибольшего или после AUTO_INCREMENT таблицы, если он больше.
Становится ли TINYINT(1) типом boolean?
Только если каждое значение в столбце равно 0 или 1. Если в нем есть другие числа, например 2 или -1, он остается числом, а отчет объясняет почему, так что ни одно значение случайно не станет true.
А хранимые процедуры и триггеры?
Они перечислены в отчете по имени и не конвертируются, потому что каждая база данных пишет их на своем языке. Исключение - ON UPDATE CURRENT_TIMESTAMP: он становится триггером PostgreSQL.
На каких версиях PostgreSQL загружается скрипт?
PostgreSQL с 13 по 18. Один и тот же скрипт загружается на каждой из них и останавливается на первой ошибке, если что-то пойдет не так.