Конвертация PostgreSQL в MySQL
Перетащите обычный дамп из pg_dump или pg_dumpall в формате .sql или .sql.gz и скачайте скрипт MySQL с теми же таблицами, ключами, индексами и данными. Каждое значение, которое MySQL не может хранить как есть, и каждый объект, который он не переносит, указаны в отчете.
18 строк конвертировано, 3 исправления, ни одна не отклонена
Таблицы в shop_2024_postgres.sqlДамп PostgreSQL 18.6 · создан программой pg_dump · utf8mb4
customers8 строк2
8 столбцов id int not null name text not null email varchar(768) not null country char(2) not null is_active tinyint(1) not null tags json not null signed_up date created_at datetime(6) not null orders10 строк1
10 столбцов id int not null customer_id int not null status enum('pending','paid','shipped','cancelled') not null total decimal(10,2) not null items smallint not null ordered_at datetime(6) not null shipped_at datetime(6) note text details json delivery varchar(100)
Не конвертировано
- Представление paid_orders
Каждая база данных пишет их на своем языке. Перепишите их для MySQL.
-- Written by IntoSQL from a PostgreSQL dump. Load it with: mysql --default-character-set=utf8mb4 <database> < <file>
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET SQL_MODE = 'NO_AUTO_VALUE_ON_ZERO,ALLOW_INVALID_DATES';
CREATE TABLE `customers` (
`id` int NOT NULL AUTO_INCREMENT,
`name` text NOT NULL,
`email` varchar(768) NOT NULL,
`country` char(2) NOT NULL DEFAULT 'US',
`is_active` tinyint(1) NOT NULL DEFAULT 1,
`tags` json NOT NULL,
`signed_up` date,
`created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
UNIQUE KEY `customers_email_key` (`email`),
PRIMARY KEY (`id`)
) AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='People who bought at least once';
CREATE TABLE `orders` (
`id` int NOT NULL AUTO_INCREMENT,
`customer_id` int NOT NULL,
`status` enum('pending','paid','shipped','cancelled') NOT NULL DEFAULT 'pending',
`total` decimal(10,2) NOT NULL,
`items` smallint NOT NULL DEFAULT 1,
`ordered_at` datetime(6) NOT NULL,
`shipped_at` datetime(6),
`note` text,
`details` json,
`delivery` varchar(100),
PRIMARY KEY (`id`),
KEY `idx_orders_customer` (`customer_id`),
KEY `idx_orders_status_date` (`status`,`ordered_at`),
CONSTRAINT `orders_total_check` CHECK (`total` >= (0)),
CONSTRAINT `orders_customer_id_fkey` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`)
) AUTO_INCREMENT=5001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
INSERT INTO `customers` (`id`,`name`,`email`,`country`,`is_active`,`tags`,`signed_up`,`created_at`) VALUES (1,'Ana Martínez','ana@example.com','MX',1,'["vip"]','2023-01-15','2023-01-15 10:24:00'),(2,'Zoë Müller','zoe@example.de','DE',1,'[]','2023-02-03','2023-02-03 08:11:42'),(3,'José Peña','jose@example.es','ES',0,'["wholesale","late payer"]','2023-03-21','2023-03-21 19:05:17'),(4,'李雷','lilei@example.cn','CN',1,'[]','2023-05-09','2023-05-09 02:40:00'),(5,'Noah O\'Brien','noah@example.ie','IE',1,'[]',NULL,'2023-06-30 14:00:00'),(6,'Amélie Laurent','amelie@example.fr','FR',1,'["vip"]','2024-01-02','2024-01-02 09:30:00'),(7,'Ravi Patel','ravi@example.in','IN',0,'[]','2024-02-29','2024-02-29 23:59:59'),(8,'Sofia Rossi 🌻','sofia@example.it','IT',1,'[]','2024-04-12','2024-04-12 12:12:12');
INSERT INTO `orders` (`id`,`customer_id`,`status`,`total`,`items`,`ordered_at`,`shipped_at`,`note`,`details`,`delivery`) VALUES (1,1,'shipped',129.90,3,'2024-03-01 10:00:00','2024-03-02 16:30:00',NULL,'{"gift": false}','1 day 06:30:00'),(2,2,'paid',54.00,1,'2024-03-02 11:15:00',NULL,'Gift wrap 🎁','{"gift": true, "wrap": "red"}',NULL),(3,1,'cancelled',12.50,1,'2024-03-03 09:45:00',NULL,'Customer changed their mind',NULL,NULL),(4,4,'shipped',980.00,12,'2024-03-05 02:10:00','2024-03-07 08:00:00','请小心轻放','{"fragile": true}','2 days 05:50:00'),(5,5,'pending',45.75,2,'2024-03-08 17:20:00',NULL,'Leave at the door; ring twice',NULL,NULL),(6,6,'shipped',310.40,5,'2024-03-10 13:00:00','2024-03-11 09:00:00','Fragile: "glass"','{"fragile": true}','20:00:00'),(7,8,'paid',76.20,2,'2024-03-11 18:45:00',NULL,NULL,NULL,NULL),(8,2,'shipped',19.99,1,'2024-03-12 08:00:00',NULL,'Shipped date set to infinity by an old import',NULL,NULL),(9,3,'shipped',64.00,2,'2024-03-14 15:30:00','2024-03-15 10:00:00','Two parcels','{"parcels": [1, 2]}','18:30:00'),(10,7,'paid',1499.00,1,'2024-03-15 21:05:00',NULL,'C:\\Users\\ravi\\invoice.pdf',NULL,NULL);
SET UNIQUE_CHECKS = 1;
SET FOREIGN_KEY_CHECKS = 1;
Исправления
- "created_at" таблицы customers содержит моменты времени с часовым поясом; в MySQL нет типа, который его сохраняет, поэтому они хранятся в UTC как datetime(6).
- "tags" таблицы customers имеет тип text[], которого нет в MySQL, поэтому тип json с теми же значениями.
- "delivery" таблицы orders имеет тип interval, которого нет в MySQL, поэтому тип varchar(100) с теми же значениями.
Требует проверки
- Не конвертированы представления, триггеры, процедуры и события (1 объект); отчет перечисляет каждый. Создайте их заново вручную в MySQL.
- Значение по умолчанию столбца "tags" таблицы customers нельзя записать для MySQL (a value its type can't hold), поэтому у столбца его нет. Добавьте значение по умолчанию вручную, если оно нужно новым строкам.
- Индекс idx_orders_details таблицы orders использует выражение, которое нельзя записать для MySQL (neither MySQL 5.7 nor MariaDB index an expression), поэтому он не создан. Создайте индекс вручную, если он нужен запросам.
- В "shipped_at" таблицы orders есть даты, которые MySQL не может хранить (1 значение), например infinity; они заменены на NULL. MySQL хранит годы с 1 по 9999 без бесконечности; если эти даты важны, замените их в источнике.
Файл конвертируется на этом компьютере и никуда не загружается.
Другие примеры:
Что меняется при переходе с PostgreSQL на MySQL
Каждая строка - это правило, которое применяет конвертер, и строка отчета, которую он пишет, когда правило что-то меняет в ваших данных.
boolean
tinyint(1)
BOOLEAN в MySQL - это TINYINT(1): true равно 1, а false равно 0.
varchar(n), char(n), text
varchar(n) до 1024 символов, char(n) до 255, дальше text
Текст хранит каждый символ в utf8mb4. Столбец в ключе - не больше varchar(768), это максимум, который вмещает ключ; то же касается текста со значением по умолчанию, которое MySQL 5.7 допускает только для varchar.
numeric(p,s) past 65 digits or 30 decimals, and numeric values past what decimal(65,30) holds
varchar
decimal в MySQL вмещает 65 цифр, из них 30 после запятой. Более широкие числа хранятся как текст, чтобы каждая цифра осталась точной; numeric без точности становится decimal(65,30), если значения помещаются.
Строка отчета: decimal_as_text
money
decimal(19,2)
В MySQL нет типа money. Сумма хранится как число, без символа валюты, добавленного локалью сервера.
Строка отчета: type_replaced
interval
varchar(100) с текстом PostgreSQL
TIME в MySQL вмещает длительности до 838 часов, но в interval есть месяцы и дни; его текст сохраняет каждую часть.
Строка отчета: type_replaced
arrays (integer[], text[]...)
массивы json
В MySQL нет массивов. Массив JSON сохраняет каждый элемент и NULL, а функции JSON его читают.
Строка отчета: type_replaced
ranges, geometric types, tsvector, xml, hstore, composite and extension types
longtext с текстом PostgreSQL
В MySQL нет равнозначного типа, поэтому каждое значение сохраняет текст, записанный PostgreSQL.
Строка отчета: type_replaced
json, jsonb
json
Те же значения JSON; порядок ключей и пробелы в jsonb такие, как их задал сервер.
timestamp
datetime(p)
timestamp without time zone - это показание часов, как datetime.
timestamp with time zone
datetime(p) в UTC
TIMESTAMP в MySQL доходит только до 2038 года, поэтому моменты хранятся как datetime в UTC: тот же момент, какое бы смещение ни записал дамп.
Строка отчета: moments_as_utc
time
time(p)
TIME в MySQL вмещает любое время суток, включая 24:00:00.
infinity, -infinity, BC dates and years past 9999
NULL
MySQL хранит годы с 1000 по 9999 без бесконечности. Такие значения становятся NULL, а отчет их подсчитывает.
Строка отчета: date_out_of_range
NaN and Infinity in numeric, real and double precision
NULL
В числах MySQL нет NaN и Infinity, поэтому такие значения становятся NULL, а отчет их подсчитывает.
Строка отчета: invalid_value_null
NOT NULL columns with values that become NULL
NOT NULL снимается
Столбец, значения которого пришлось заменить на NULL, не может сохранить NOT NULL, иначе загрузка остановится.
Строка отчета: not_null_relaxed
enum types
enum(...) с теми же метками
ENUM в MySQL сохраняет метки в их порядке.
uuid, inet, cidr
uuid как char(36), inet и cidr как varchar(43)
В MySQL нет типов uuid и адресов; текст сохраняет каждое значение.
bytea
blob, mediumblob или longblob; varbinary(3072) в ключе
Байты остаются байтами.
bit(n)
bit(n) до 64, дальше text
Битовая строка до 64 бит - это число, которое составляют ее биты; более длинная сохраняет свои цифры.
citext and nondeterministic collations
точное сравнение
Таблицы используют utf8mb4_bin, поэтому текст сравнивается точно, как в PostgreSQL; столбцы, которые в источнике не учитывали регистр, это теряют.
Строка отчета: case_insensitive_collation
serial, identity
AUTO_INCREMENT, продолжается после наибольшего id
Нумеруется первый столбец первичного ключа; счетчик продолжается с того места, где была последовательность.
a second serial or identity column in a table
обычное целое
MySQL нумерует один столбец на таблицу; любой другой сохраняет свои значения без счетчика.
Строка отчета: counter_dropped
sequences not owned by a column
не создается
В MySQL нет последовательностей. Столбцы, которые нумеровались ими, вместо этого становятся AUTO_INCREMENT.
Строка отчета: sequence_not_converted
generated columns
генерируемые столбцы, STORED, как в источнике
MySQL вычисляет их так же.
generated columns with functions MySQL lacks
обычный столбец
Формула, которую нельзя записать для MySQL, оставляет обычный столбец и строку в отчете.
Строка отчета: generated_not_translated
defaults with functions MySQL lacks, and defaults from a sequence
без значения по умолчанию
Значение по умолчанию, которое нельзя записать для MySQL, опускается и попадает в отчет.
Строка отчета: default_not_translated
CHECK constraints
переводится, внутри CREATE TABLE
MySQL 8.0.16 и MariaDB их применяют; MySQL 5.7 хранит их без проверки.
CHECK constraints with functions MySQL lacks
не создается
Проверка, которую нельзя записать для MySQL, опускается и попадает в отчет.
Строка отчета: check_skipped
CHECK ... NOT VALID
не создается
PostgreSQL не проверял строки, которые были до нее, поэтому они могут ее нарушать.
Строка отчета: check_not_valid
gin, gist, brin and hash indexes, partial and expression indexes
не создается
В MySQL есть только индексы b-tree, полнотекстовые и пространственные, по целым столбцам, без WHERE.
Строка отчета: index_skipped
foreign keys
внешние ключи, загрузка с FOREIGN_KEY_CHECKS=0
Строки загружаются в любом порядке, а ключи затем проверяют новые строки.
foreign keys to tables the dump doesn't create
не создается
Ключ не может ссылаться на таблицу, которой нет.
Строка отчета: foreign_key_skipped
partitioned tables
одна таблица со строками всех секций
Строки те же; при необходимости секционируйте таблицу заново в MySQL.
Строка отчета: partitions_dropped
schemas
одна база данных, имя таблицы начинается с имени ее схемы
Схемы в MySQL - это базы данных, а скрипт, загружаемый в одну базу, не может их создать; таблицы из public сохраняют свои имена.
Строка отчета: schemas_merged
views, functions, procedures, triggers, rules, policies, extensions and types
перечисляются в отчете
Их код написан на языке PostgreSQL, поэтому они перечислены, чтобы вы создали их заново.
Строка отчета: not_converted_objects
dumps without CREATE TABLE (--data-only)
типы определяются по значениям
Дамп только с данными содержит лишь строки, поэтому типы берутся из значений.
Строка отчета: no_schema
expressions in INSERT values
NULL
Конвертер, который не запускает базу данных, не может их вычислить.
Строка отчета: expression_value
types IntoSQL doesn't know
longtext
Тип text вмещает любое значение; измените тип после загрузки.
Строка отчета: unknown_type
text that isn't valid in its encoding
неверные байты заменяются символом замены
Текст должен быть корректным utf8mb4.
Строка отчета: invalid_utf8_replaced
text decoded twice (ñ)
копируется как есть
Дамп содержит именно эти символы, поэтому они копируются; отчет на них указывает.
Строка отчета: mojibake_suspected
| В PostgreSQL | В MySQL | Строка отчета |
|---|---|---|
| boolean | tinyint(1)BOOLEAN в MySQL - это TINYINT(1): true равно 1, а false равно 0. | |
| varchar(n), char(n), text | varchar(n) до 1024 символов, char(n) до 255, дальше textТекст хранит каждый символ в utf8mb4. Столбец в ключе - не больше varchar(768), это максимум, который вмещает ключ; то же касается текста со значением по умолчанию, которое MySQL 5.7 допускает только для varchar. | |
| numeric(p,s) past 65 digits or 30 decimals, and numeric values past what decimal(65,30) holds | varchardecimal в MySQL вмещает 65 цифр, из них 30 после запятой. Более широкие числа хранятся как текст, чтобы каждая цифра осталась точной; numeric без точности становится decimal(65,30), если значения помещаются. | decimal_as_text |
| money | decimal(19,2)В MySQL нет типа money. Сумма хранится как число, без символа валюты, добавленного локалью сервера. | type_replaced |
| interval | varchar(100) с текстом PostgreSQLTIME в MySQL вмещает длительности до 838 часов, но в interval есть месяцы и дни; его текст сохраняет каждую часть. | type_replaced |
| arrays (integer[], text[]...) | массивы jsonВ MySQL нет массивов. Массив JSON сохраняет каждый элемент и NULL, а функции JSON его читают. | type_replaced |
| ranges, geometric types, tsvector, xml, hstore, composite and extension types | longtext с текстом PostgreSQLВ MySQL нет равнозначного типа, поэтому каждое значение сохраняет текст, записанный PostgreSQL. | type_replaced |
| json, jsonb | jsonТе же значения JSON; порядок ключей и пробелы в jsonb такие, как их задал сервер. | |
| timestamp | datetime(p)timestamp without time zone - это показание часов, как datetime. | |
| timestamp with time zone | datetime(p) в UTCTIMESTAMP в MySQL доходит только до 2038 года, поэтому моменты хранятся как datetime в UTC: тот же момент, какое бы смещение ни записал дамп. | moments_as_utc |
| time | time(p)TIME в MySQL вмещает любое время суток, включая 24:00:00. | |
| infinity, -infinity, BC dates and years past 9999 | NULLMySQL хранит годы с 1000 по 9999 без бесконечности. Такие значения становятся NULL, а отчет их подсчитывает. | date_out_of_range |
| NaN and Infinity in numeric, real and double precision | NULLВ числах MySQL нет NaN и Infinity, поэтому такие значения становятся NULL, а отчет их подсчитывает. | invalid_value_null |
| NOT NULL columns with values that become NULL | NOT NULL снимаетсяСтолбец, значения которого пришлось заменить на NULL, не может сохранить NOT NULL, иначе загрузка остановится. | not_null_relaxed |
| enum types | enum(...) с теми же меткамиENUM в MySQL сохраняет метки в их порядке. | |
| uuid, inet, cidr | uuid как char(36), inet и cidr как varchar(43)В MySQL нет типов uuid и адресов; текст сохраняет каждое значение. | |
| bytea | blob, mediumblob или longblob; varbinary(3072) в ключеБайты остаются байтами. | |
| bit(n) | bit(n) до 64, дальше textБитовая строка до 64 бит - это число, которое составляют ее биты; более длинная сохраняет свои цифры. | |
| citext and nondeterministic collations | точное сравнениеТаблицы используют utf8mb4_bin, поэтому текст сравнивается точно, как в PostgreSQL; столбцы, которые в источнике не учитывали регистр, это теряют. | case_insensitive_collation |
| serial, identity | AUTO_INCREMENT, продолжается после наибольшего idНумеруется первый столбец первичного ключа; счетчик продолжается с того места, где была последовательность. | |
| a second serial or identity column in a table | обычное целоеMySQL нумерует один столбец на таблицу; любой другой сохраняет свои значения без счетчика. | counter_dropped |
| sequences not owned by a column | не создаетсяВ MySQL нет последовательностей. Столбцы, которые нумеровались ими, вместо этого становятся AUTO_INCREMENT. | sequence_not_converted |
| generated columns | генерируемые столбцы, STORED, как в источникеMySQL вычисляет их так же. | |
| generated columns with functions MySQL lacks | обычный столбецФормула, которую нельзя записать для MySQL, оставляет обычный столбец и строку в отчете. | generated_not_translated |
| defaults with functions MySQL lacks, and defaults from a sequence | без значения по умолчаниюЗначение по умолчанию, которое нельзя записать для MySQL, опускается и попадает в отчет. | default_not_translated |
| CHECK constraints | переводится, внутри CREATE TABLEMySQL 8.0.16 и MariaDB их применяют; MySQL 5.7 хранит их без проверки. | |
| CHECK constraints with functions MySQL lacks | не создаетсяПроверка, которую нельзя записать для MySQL, опускается и попадает в отчет. | check_skipped |
| CHECK ... NOT VALID | не создаетсяPostgreSQL не проверял строки, которые были до нее, поэтому они могут ее нарушать. | check_not_valid |
| gin, gist, brin and hash indexes, partial and expression indexes | не создаетсяВ MySQL есть только индексы b-tree, полнотекстовые и пространственные, по целым столбцам, без WHERE. | index_skipped |
| foreign keys | внешние ключи, загрузка с FOREIGN_KEY_CHECKS=0Строки загружаются в любом порядке, а ключи затем проверяют новые строки. | |
| foreign keys to tables the dump doesn't create | не создаетсяКлюч не может ссылаться на таблицу, которой нет. | foreign_key_skipped |
| partitioned tables | одна таблица со строками всех секцийСтроки те же; при необходимости секционируйте таблицу заново в MySQL. | partitions_dropped |
| schemas | одна база данных, имя таблицы начинается с имени ее схемыСхемы в MySQL - это базы данных, а скрипт, загружаемый в одну базу, не может их создать; таблицы из public сохраняют свои имена. | schemas_merged |
| views, functions, procedures, triggers, rules, policies, extensions and types | перечисляются в отчетеИх код написан на языке PostgreSQL, поэтому они перечислены, чтобы вы создали их заново. | not_converted_objects |
| dumps without CREATE TABLE (--data-only) | типы определяются по значениямДамп только с данными содержит лишь строки, поэтому типы берутся из значений. | no_schema |
| expressions in INSERT values | NULLКонвертер, который не запускает базу данных, не может их вычислить. | expression_value |
| types IntoSQL doesn't know | longtextТип text вмещает любое значение; измените тип после загрузки. | unknown_type |
| text that isn't valid in its encoding | неверные байты заменяются символом заменыТекст должен быть корректным utf8mb4. | invalid_utf8_replaced |
| text decoded twice (ñ) | копируется как естьДамп содержит именно эти символы, поэтому они копируются; отчет на них указывает. | mojibake_suspected |
Загрузите скрипт в MySQL
Скрипт создает таблицы, копирует строки, затем добавляет ключи, индексы и внешние ключи, поэтому загрузка идет быстро и останавливается на первой ошибке.
mysql --default-character-set=utf8mb4 mydb < postgresql.mysql.sqlВнешние ключи создаются так, как их объявляет дамп, с FOREIGN_KEY_CHECKS=0 на время загрузки строк, поэтому строка, которая ссылается на отсутствующую строку, не останавливает загрузку.
Для большого дампа запускайте клиент на той же машине, где работает сервер, и увеличьте max_allowed_packet, если отдельная строка очень большая.
IntoSQL Pro загружает файл прямо в MySQL из приложения для компьютера или командной строки, без написания скрипта.
Как создать дамп PostgreSQL для лучшего результата
Конвертер читает то, что есть в дампе. Эти параметры сохраняют в нем больше ваших данных:
- --format=plain, формат pg_dump по умолчанию, пишет SQL. Архив custom или directory (-Fc, -Fd) превращается в SQL командой pg_restore -f dump.sql, без сервера баз данных.
- --no-owner --no-privileges исключают роли и права, которые конвертер все равно пропускает, поэтому дамп получается меньше.
- pg_dumpall сохраняет все базы данных в один файл; имена таблиц каждой базы начинаются с ее имени, поэтому ничего не конфликтует.
Что не конвертируется
Представления, материализованные представления, функции, процедуры, триггеры, правила, политики защиты строк, расширения и типы написаны на собственном языке PostgreSQL. Они перечислены в отчете по имени и никогда не удаляются молча, чтобы вы могли переписать их для MySQL.
Вопросы о PostgreSQL в MySQL
Нет ответа на ваш вопрос? Напишите нам через страницу контактов, и вам ответит человек. Страница контактов
Мой дамп загружается на сервер?
Нет. Конвертер работает в вашем браузере и читает дамп с диска по частям, поэтому он никогда не покидает ваш компьютер. Приложение для компьютера и командная строка тоже конвертируют на вашем компьютере.
Насколько большим может быть дамп?
Конвертер никогда не держит весь дамп в памяти: он читает файл дважды, по диапазонам, и пишет скрипт по ходу чтения. Дампы в несколько гигабайт конвертируются в браузере, в приложении для компьютера и в командной строке.
Читает ли он архивы pg_dump -Fc?
Не напрямую: архив custom - это двоичный формат. pg_restore -f dump.sql dump.backup превращает его в обычный SQL, который читает конвертер, без подключения к серверу. Получив архив, конвертер так и сообщает.
Из каких версий PostgreSQL может быть дамп?
Дампы, записанные pg_dump и pg_dumpall из PostgreSQL 10 и новее, со строками COPY или INSERT. Каждый тестовый дамп создан в PostgreSQL 13 и 18 и сверен с источником ячейка за ячейкой.
Сохраняются ли значения serial и identity?
Да, для одного столбца на таблицу: столбец serial или identity, с которого начинается первичный ключ, становится AUTO_INCREMENT и продолжается после наибольшего id. MySQL нумерует один столбец на таблицу, поэтому любой другой сохраняет значения без счетчика, и отчет это отмечает.
Что происходит с массивами, enum и другими типами PostgreSQL?
Массивы становятся массивами JSON, логические значения - tinyint(1), enum - ENUM с теми же метками, money - decimal. Диапазоны, геометрические типы, hstore и составные типы сохраняют текст PostgreSQL, а отчет указывает, какие столбцы.
А функции, триггеры и представления?
Они перечислены в отчете по имени и не конвертируются, потому что каждая база данных пишет их на своем языке.
На каких версиях MySQL загружается скрипт?
MySQL 5.7 и новее. Каждый тестовый дамп загружается на 5.7 и 9 и останавливается на первой ошибке, если что-то пойдет не так.