Aller au contenu

Convertir MySQL en PostgreSQL

Déposez un dump de mysqldump, mariadb-dump, mysqlpump, phpMyAdmin ou Adminer, en .sql ou .sql.gz, et téléchargez un script PostgreSQL avec les mêmes tables, clés, index et données. Chaque valeur que PostgreSQL ne peut pas contenir telle quelle, et chaque objet qu'il ne reprend pas, est listé dans le rapport.

Votre fichier
sqlshop_2024.sqlFichier d'exemple, 5,1 ko
Votre base de données

18 lignes converties, 8 corrections, aucune rejetée

Afficher

Tables de shop_2024.sqlDump MySQL 8.0.46 · écrit par mysqldump · utf8mb4

  • customers8 lignes3
    7 colonnes
    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 lignes5
    9 colonnes
    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();

Corrections

  1. "id" de customers est non signée ; elle est donc devenue bigint avec un CHECK qui interdit les valeurs négatives.
  2. "signed_up" de customers accepte maintenant NULL : certaines de ses valeurs ne pouvaient pas être stockées et sont NULL.
  3. La valeur par défaut de "signed_up" dans customers, 0000‑00‑00, n'est pas une date acceptée par PostgreSQL ; la colonne n'a donc pas de valeur par défaut.
  4. "customer_id" de orders est non signée ; elle est donc devenue bigint avec un CHECK qui interdit les valeurs négatives.
  5. "id" de orders est non signée ; elle est donc devenue bigint avec un CHECK qui interdit les valeurs négatives.
  6. "items" de orders est non signée ; elle est donc devenue integer avec un CHECK qui interdit les valeurs négatives.
  7. 1 ligne de "status" dans orders contient la valeur vide que MySQL stocke pour les entrées invalides ; la contrainte CHECK de ses valeurs l'autorise.
  8. "updated_at" de orders passe à l'heure actuelle à chaque mise à jour ; dans PostgreSQL, un déclencheur s'en charge.

À vérifier

  • 1 valeur dans "signed_up" de customers est une date zéro ou impossible, comme 0000‑00‑00, que PostgreSQL ne peut pas stocker ; elle est donc NULL. Si ces dates ont un sens, remplacez-les dans la source par de vraies dates avant le dump.
  • 1 valeur dans "shipped_at" de orders est une date zéro ou impossible, comme 0000‑00‑00 00:00:00, que PostgreSQL ne peut pas stocker ; elle est donc NULL. Si ces dates ont un sens, remplacez-les dans la source par de vraies dates avant le dump.

Votre fichier est converti sur cet ordinateur et n'est jamais envoyé.

Autres exemples :

Ce qui change de MySQL à PostgreSQL

Chaque ligne est une règle appliquée par le convertisseur, avec la ligne de rapport qu'il écrit quand la règle change quelque chose dans vos données.

  • TINYINT(1), BOOL

    boolean si chaque valeur vaut 0 ou 1, sinon smallint

    Le BOOL de MySQL est un TINYINT(1) qui peut contenir tout nombre de -128 à 127. Une colonne qui ne contient que 0 et 1 devient boolean ; une colonne qui contient 2 ou -1 reste un nombre, pour qu'aucune valeur ne change de sens.

    Ligne du rapport: bool_kept_numeric

  • UNSIGNED integers

    l'entier immédiatement plus large, avec un CHECK qui interdit les négatifs ; BIGINT UNSIGNED devient bigint si chaque valeur y tient, sinon numeric(20,0)

    PostgreSQL n'a pas d'entiers non signés. Un type plus large contient toute la plage non signée, et la contrainte CHECK écarte les valeurs négatives comme le faisait MySQL.

    Ligne du rapport: unsigned_widened

  • ZEROFILL and display widths

    supprimé

    Le remplissage par des zéros est une façon d'afficher un nombre, pas une partie du nombre. Les nombres sont conservés ; formatez-les à la lecture si le remplissage compte.

    Ligne du rapport: zerofill_dropped

  • FLOAT(M,D)

    real ou double precision, sans l'échelle

    Les flottants PostgreSQL n'ont pas d'échelle fixe. Les valeurs stockées sont copiées telles quelles.

    Ligne du rapport: float_scale_dropped

  • BIT(1), BIT(n)

    boolean, bit(n)

    Un seul bit est un oui ou un non ; les champs de bits plus larges gardent chaque bit.

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

    NULL

    PostgreSQL ne stocke que de vraies dates du calendrier. MySQL garde les dates zéro et impossibles quand son mode SQL les autorise ; elles deviennent NULL et le rapport les compte.

    Ligne du rapport: invalid_date_null

  • NOT NULL columns with values that become NULL

    NOT NULL retiré

    Une colonne dont des valeurs ont dû devenir NULL ne peut pas garder NOT NULL, sinon le chargement s'arrêterait.

    Ligne du rapport: not_null_relaxed

  • DEFAULT '0000-00-00'

    aucune valeur par défaut

    Une valeur par défaut que PostgreSQL ne peut pas stocker est omise plutôt que remplacée par une date inventée.

    Ligne du rapport: zero_date_default

  • TIME

    time si chaque valeur tient dans une journée, sinon interval

    Le TIME de MySQL est une durée de -838:59:59 à 838:59:59. Une colonne dont les valeurs tiennent dans une journée reste une heure du jour ; une colonne avec des valeurs plus longues ou négatives devient interval.

    Ligne du rapport: time_as_interval

  • YEAR

    smallint avec un CHECK de sa plage

    PostgreSQL n'a pas de type année ; la contrainte CHECK garde les mêmes valeurs que celles autorisées par MySQL.

  • CHAR(n)

    varchar(n)

    MySQL retire les espaces de fin des valeurs CHAR à la lecture, et le char(n) de PostgreSQL les rajouterait ; varchar(n) garde donc les valeurs telles que MySQL les renvoie.

  • DATETIME

    timestamp

    Un DATETIME est une date et une heure locales, sans fuseau horaire, comme le timestamp de PostgreSQL.

  • TIMESTAMP

    timestamptz dans le fuseau horaire indiqué par le dump, UTC s'il n'en indique aucun

    Un TIMESTAMP est un instant. Les dumps indiquent le fuseau dans lequel ils l'ont écrit (mysqldump écrit en UTC) ; sinon, les valeurs sont lues en UTC et le rapport le signale.

    Ligne du rapport: timestamp_zone_assumed

  • ENUM

    text avec un CHECK de ses valeurs

    Un texte avec une contrainte CHECK garde les mêmes valeurs et se modifie plus facilement qu'un enum type de PostgreSQL.

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

    la valeur vide autorisée par le CHECK

    MySQL stocke une chaîne vide quand une valeur invalide entre dans un ENUM avec un mode SQL permissif. Ces lignes sont conservées et la contrainte CHECK les autorise.

    Ligne du rapport: enum_invalid_value

  • ENUM or SET holding values outside its list

    pas de CHECK

    Les valeurs absentes de la liste feraient échouer la contrainte CHECK, qui n'est donc pas créée.

    Ligne du rapport: enum_check_skipped

  • SET

    text[] avec un CHECK qui vérifie que chaque élément est dans la liste

    Un tableau garde chaque élément séparé, donc les requêtes peuvent tester l'appartenance.

  • JSON

    jsonb ; json quand une valeur contient \u0000

    jsonb est le type que PostgreSQL indexe et interroge bien, mais il rejette l'échappement \u0000 ; une colonne qui en contient reste donc en json.

    Ligne du rapport: json_not_jsonb

  • NUL bytes in text

    retiré

    Le texte PostgreSQL ne peut pas contenir d'octet NUL. L'octet est retiré et le rapport compte les valeurs.

    Ligne du rapport: nul_removed

  • text that isn't valid in its character set

    les octets invalides deviennent le caractère de remplacement

    Le texte PostgreSQL doit être de l'UTF-8 valide.

    Ligne du rapport: invalid_utf8_replaced

  • text decoded twice (ñ)

    copié tel quel

    Le dump contient exactement ces caractères, ils sont donc copiés ; le rapport les signale.

    Ligne du rapport: mojibake_suspected

  • AUTO_INCREMENT

    GENERATED BY DEFAULT AS IDENTITY, avec le compteur placé après les identifiants copiés

    Les colonnes d'identité acceptent les identifiants copiés, et le compteur reprend après le plus élevé ou après l'AUTO_INCREMENT du dump, selon le plus grand.

  • MyISAM AUTO_INCREMENT on the second column of a key

    un seul compteur pour la table

    MyISAM compte par groupe de la première colonne de la clé ; PostgreSQL a un compteur par colonne.

    Ligne du rapport: myisam_group_autoincrement

  • ON UPDATE CURRENT_TIMESTAMP

    un déclencheur BEFORE UPDATE

    Le déclencheur met la colonne à l'heure actuelle quand une ligne change et que la mise à jour ne l'a pas définie, comme le fait MySQL.

    Ligne du rapport: on_update_trigger

  • generated columns

    STORED

    PostgreSQL 13 à 17 ne fait que stocker les colonnes générées ; la formule est la même.

    Ligne du rapport: virtual_as_stored

  • generated columns with functions PostgreSQL lacks

    une colonne simple

    Une formule qui ne peut pas être écrite pour PostgreSQL laisse une colonne simple et une ligne dans le rapport.

    Ligne du rapport: generated_not_translated

  • INVISIBLE columns

    colonnes ordinaires

    PostgreSQL n'a pas de colonnes invisibles.

    Ligne du rapport: invisible_column

  • CHECK constraints

    traduites, en ligne dans CREATE TABLE

    Les contraintes CHECK en ligne sont vérifiées au chargement de chaque ligne, au lieu d'un parcours par contrainte ensuite.

  • CHECK constraints with functions PostgreSQL lacks

    non créée

    Une contrainte CHECK qui ne peut pas être écrite pour PostgreSQL est omise et signalée.

    Ligne du rapport: check_skipped

  • CHECK ... NOT ENFORCED

    non créée

    MySQL garde ces contraintes CHECK sans les appliquer, donc les lignes peuvent les enfreindre.

    Ligne du rapport: check_not_enforced

  • defaults with functions PostgreSQL lacks

    aucune valeur par défaut

    Une valeur par défaut qui ne peut pas être écrite pour PostgreSQL est omise et signalée.

    Ligne du rapport: default_not_translated

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

    un index sur left(c, 20)

    PostgreSQL indexe des expressions, donc le même préfixe est indexé.

    Ligne du rapport: prefix_unique

  • FULLTEXT indexes

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

    La recherche plein texte utilise @@ au lieu de MATCH ... AGAINST.

    Ligne du rapport: fulltext_as_gin

  • indexes on values over 2,000 bytes

    hash

    Une entrée btree ne peut pas contenir des valeurs aussi longues ; un index hash trouve quand même les valeurs égales.

    Ligne du rapport: index_on_wide_values

  • SPATIAL indexes

    non créé sans PostGIS

    Les index spatiaux ont besoin de PostGIS.

    Ligne du rapport: spatial_index_skipped

  • functional indexes PostgreSQL can't write

    non créé

    Un index sur une expression qui ne peut pas être écrite pour PostgreSQL est omis et signalé.

    Ligne du rapport: index_skipped

  • foreign keys

    après les données, NOT VALID, puis validée avec un avertissement sur les orphelins

    Les dumps se chargent avec FOREIGN_KEY_CHECKS=0 et MyISAM ne vérifie jamais, donc les lignes sans parent sont fréquentes. La clé est créée d'abord et validée en dernier ; des orphelins la laissent NOT VALID avec un avertissement au lieu d'arrêter le chargement.

  • foreign keys to columns without a unique key

    non créée

    PostgreSQL exige une clé unique sur les colonnes référencées ; InnoDB non.

    Ligne du rapport: foreign_key_skipped

  • table and column names

    en minuscules si le nom reste unique

    Les noms en minuscules s'écrivent sans guillemets dans PostgreSQL.

    Ligne du rapport: identifier_renamed

  • names over 63 bytes

    raccourci avec un hachage

    Les noms PostgreSQL font 63 octets au plus ; le hachage distingue les noms raccourcis.

    Ligne du rapport: name_shortened

  • GEOMETRY, POINT, POLYGON...

    WKB dans bytea

    Sans PostGIS, le format binaire standard (well-known binary) garde chaque géométrie à l'identique.

    Ligne du rapport: geometry_as_wkb

  • INET4, INET6, UUID (MariaDB)

    inet, uuid

    PostgreSQL a les mêmes types.

  • BINARY, VARBINARY, BLOB

    bytea

    Les octets restent des octets.

  • PARTITION BY

    une seule table

    Les lignes sont les mêmes ; partitionnez à nouveau la table dans PostgreSQL si besoin.

    Ligne du rapport: partitions_dropped

  • WITH SYSTEM VERSIONING (MariaDB)

    une table avec les lignes actuelles

    Un dump ne contient que les lignes actuelles.

    Ligne du rapport: system_versioning_dropped

  • MyISAM, MEMORY and other engines

    une table ordinaire

    PostgreSQL a un seul moteur de tables.

    Ligne du rapport: engine_dropped

  • views, triggers, procedures, functions and events

    listés dans le rapport

    Leur code est écrit dans le langage de MySQL ; ils sont donc listés pour que vous les recréiez.

    Ligne du rapport: not_converted_objects

  • dumps without CREATE TABLE

    types déduits des valeurs

    Un dump fait avec --no-create-info ne contient que des lignes, donc les types viennent des valeurs.

    Ligne du rapport: no_schema

  • geometries in a dump without CREATE TABLE

    WKB dans bytea

    Le format de géométrie de MySQL est reconnu à partir des valeurs et stocké en WKB standard.

    Ligne du rapport: geometry_inferred

  • _ci collations

    comparaison exacte

    PostgreSQL compare le texte exactement, sauf indication contraire.

    Ligne du rapport: case_insensitive_collation

  • SET NAMES utf8 from utf8mb4 tables

    converti, avec un avertissement

    Le mysqldump de MySQL 5.x écrit par défaut en utf8 sur 3 octets et remplace chaque emoji par un point d'interrogation avant qu'un convertisseur ne voie le fichier.

    Ligne du rapport: dump_lost_four_byte_chars

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

    lu comme Latin-1 (Windows-1252)

    Le latin1 de MySQL est le Windows-1252, l'encodage habituel de ces dumps.

    Ligne du rapport: charset_guessed

  • sjis, cp932, gbk, big5 dumps

    lu comme UTF-8, avec un avertissement

    Ces encodages ne sont pas encore décodés.

    Ligne du rapport: unsupported_charset

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

    évaluées

    Certains outils de dump écrivent les valeurs sous forme de ces appels ; ils sont lus comme les valeurs qu'ils produisent, le WKT géographique étant remis avec la longitude en premier.

  • other expressions in rows

    NULL

    Un convertisseur qui n'exécute pas de base de données ne peut pas les évaluer.

    Ligne du rapport: expression_value

  • DEFAULT in VALUES

    la valeur par défaut de la colonne

    Une valeur par défaut littérale est copiée dans la ligne.

    Ligne du rapport: default_keyword_filled

  • DEFAULT in VALUES without a literal default

    NULL

    Il n'y a aucune valeur à copier.

    Ligne du rapport: default_keyword_null

  • REPLACE and INSERT IGNORE

    lignes simples

    Un dump a une ligne par clé, donc les lignes sont les mêmes.

    Ligne du rapport: replace_as_insert

  • types IntoSQL doesn't know

    text

    Le texte contient n'importe quelle valeur ; changez le type après le chargement.

    Ligne du rapport: unknown_type

  • values that don't fit their column

    NULL

    Un dump endommagé ou écrit à la main peut contenir des valeurs que sa colonne n'accepte pas ; elles deviennent NULL pour que le reste se charge.

    Ligne du rapport: invalid_value_null

  • unreadable geometries

    NULL

    Des octets qui ne forment pas une géométrie ne peuvent pas être stockés comme telle.

    Ligne du rapport: bad_geometry

  • CREATE SEQUENCE (MariaDB)

    CREATE SEQUENCE avec sa valeur actuelle

    Les séquences PostgreSQL acceptent les mêmes options.

Dans MySQLDans PostgreSQLLigne du rapport
TINYINT(1), BOOLboolean si chaque valeur vaut 0 ou 1, sinon smallintLe BOOL de MySQL est un TINYINT(1) qui peut contenir tout nombre de -128 à 127. Une colonne qui ne contient que 0 et 1 devient boolean ; une colonne qui contient 2 ou -1 reste un nombre, pour qu'aucune valeur ne change de sens.bool_kept_numeric
UNSIGNED integersl'entier immédiatement plus large, avec un CHECK qui interdit les négatifs ; BIGINT UNSIGNED devient bigint si chaque valeur y tient, sinon numeric(20,0)PostgreSQL n'a pas d'entiers non signés. Un type plus large contient toute la plage non signée, et la contrainte CHECK écarte les valeurs négatives comme le faisait MySQL.unsigned_widened
ZEROFILL and display widthssuppriméLe remplissage par des zéros est une façon d'afficher un nombre, pas une partie du nombre. Les nombres sont conservés ; formatez-les à la lecture si le remplissage compte.zerofill_dropped
FLOAT(M,D)real ou double precision, sans l'échelleLes flottants PostgreSQL n'ont pas d'échelle fixe. Les valeurs stockées sont copiées telles quelles.float_scale_dropped
BIT(1), BIT(n)boolean, bit(n)Un seul bit est un oui ou un non ; les champs de bits plus larges gardent chaque bit.
zero, partial or impossible dates (0000-00-00, 2024-02-30)NULLPostgreSQL ne stocke que de vraies dates du calendrier. MySQL garde les dates zéro et impossibles quand son mode SQL les autorise ; elles deviennent NULL et le rapport les compte.invalid_date_null
NOT NULL columns with values that become NULLNOT NULL retiréUne colonne dont des valeurs ont dû devenir NULL ne peut pas garder NOT NULL, sinon le chargement s'arrêterait.not_null_relaxed
DEFAULT '0000-00-00'aucune valeur par défautUne valeur par défaut que PostgreSQL ne peut pas stocker est omise plutôt que remplacée par une date inventée.zero_date_default
TIMEtime si chaque valeur tient dans une journée, sinon intervalLe TIME de MySQL est une durée de -838:59:59 à 838:59:59. Une colonne dont les valeurs tiennent dans une journée reste une heure du jour ; une colonne avec des valeurs plus longues ou négatives devient interval.time_as_interval
YEARsmallint avec un CHECK de sa plagePostgreSQL n'a pas de type année ; la contrainte CHECK garde les mêmes valeurs que celles autorisées par MySQL.
CHAR(n)varchar(n)MySQL retire les espaces de fin des valeurs CHAR à la lecture, et le char(n) de PostgreSQL les rajouterait ; varchar(n) garde donc les valeurs telles que MySQL les renvoie.
DATETIMEtimestampUn DATETIME est une date et une heure locales, sans fuseau horaire, comme le timestamp de PostgreSQL.
TIMESTAMPtimestamptz dans le fuseau horaire indiqué par le dump, UTC s'il n'en indique aucunUn TIMESTAMP est un instant. Les dumps indiquent le fuseau dans lequel ils l'ont écrit (mysqldump écrit en UTC) ; sinon, les valeurs sont lues en UTC et le rapport le signale.timestamp_zone_assumed
ENUMtext avec un CHECK de ses valeursUn texte avec une contrainte CHECK garde les mêmes valeurs et se modifie plus facilement qu'un enum type de PostgreSQL.
ENUM holding '' (MySQL's error value)la valeur vide autorisée par le CHECKMySQL stocke une chaîne vide quand une valeur invalide entre dans un ENUM avec un mode SQL permissif. Ces lignes sont conservées et la contrainte CHECK les autorise.enum_invalid_value
ENUM or SET holding values outside its listpas de CHECKLes valeurs absentes de la liste feraient échouer la contrainte CHECK, qui n'est donc pas créée.enum_check_skipped
SETtext[] avec un CHECK qui vérifie que chaque élément est dans la listeUn tableau garde chaque élément séparé, donc les requêtes peuvent tester l'appartenance.
JSONjsonb ; json quand une valeur contient \u0000jsonb est le type que PostgreSQL indexe et interroge bien, mais il rejette l'échappement \u0000 ; une colonne qui en contient reste donc en json.json_not_jsonb
NUL bytes in textretiréLe texte PostgreSQL ne peut pas contenir d'octet NUL. L'octet est retiré et le rapport compte les valeurs.nul_removed
text that isn't valid in its character setles octets invalides deviennent le caractère de remplacementLe texte PostgreSQL doit être de l'UTF-8 valide.invalid_utf8_replaced
text decoded twice (ñ)copié tel quelLe dump contient exactement ces caractères, ils sont donc copiés ; le rapport les signale.mojibake_suspected
AUTO_INCREMENTGENERATED BY DEFAULT AS IDENTITY, avec le compteur placé après les identifiants copiésLes colonnes d'identité acceptent les identifiants copiés, et le compteur reprend après le plus élevé ou après l'AUTO_INCREMENT du dump, selon le plus grand.
MyISAM AUTO_INCREMENT on the second column of a keyun seul compteur pour la tableMyISAM compte par groupe de la première colonne de la clé ; PostgreSQL a un compteur par colonne.myisam_group_autoincrement
ON UPDATE CURRENT_TIMESTAMPun déclencheur BEFORE UPDATELe déclencheur met la colonne à l'heure actuelle quand une ligne change et que la mise à jour ne l'a pas définie, comme le fait MySQL.on_update_trigger
generated columnsSTOREDPostgreSQL 13 à 17 ne fait que stocker les colonnes générées ; la formule est la même.virtual_as_stored
generated columns with functions PostgreSQL lacksune colonne simpleUne formule qui ne peut pas être écrite pour PostgreSQL laisse une colonne simple et une ligne dans le rapport.generated_not_translated
INVISIBLE columnscolonnes ordinairesPostgreSQL n'a pas de colonnes invisibles.invisible_column
CHECK constraintstraduites, en ligne dans CREATE TABLELes contraintes CHECK en ligne sont vérifiées au chargement de chaque ligne, au lieu d'un parcours par contrainte ensuite.
CHECK constraints with functions PostgreSQL lacksnon crééeUne contrainte CHECK qui ne peut pas être écrite pour PostgreSQL est omise et signalée.check_skipped
CHECK ... NOT ENFORCEDnon crééeMySQL garde ces contraintes CHECK sans les appliquer, donc les lignes peuvent les enfreindre.check_not_enforced
defaults with functions PostgreSQL lacksaucune valeur par défautUne valeur par défaut qui ne peut pas être écrite pour PostgreSQL est omise et signalée.default_not_translated
keys and indexes on a prefix, c(20)un index sur left(c, 20)PostgreSQL indexe des expressions, donc le même préfixe est indexé.prefix_unique
FULLTEXT indexesGIN sur to_tsvector('simple', ...)La recherche plein texte utilise @@ au lieu de MATCH ... AGAINST.fulltext_as_gin
indexes on values over 2,000 byteshashUne entrée btree ne peut pas contenir des valeurs aussi longues ; un index hash trouve quand même les valeurs égales.index_on_wide_values
SPATIAL indexesnon créé sans PostGISLes index spatiaux ont besoin de PostGIS.spatial_index_skipped
functional indexes PostgreSQL can't writenon crééUn index sur une expression qui ne peut pas être écrite pour PostgreSQL est omis et signalé.index_skipped
foreign keysaprès les données, NOT VALID, puis validée avec un avertissement sur les orphelinsLes dumps se chargent avec FOREIGN_KEY_CHECKS=0 et MyISAM ne vérifie jamais, donc les lignes sans parent sont fréquentes. La clé est créée d'abord et validée en dernier ; des orphelins la laissent NOT VALID avec un avertissement au lieu d'arrêter le chargement.
foreign keys to columns without a unique keynon crééePostgreSQL exige une clé unique sur les colonnes référencées ; InnoDB non.foreign_key_skipped
table and column namesen minuscules si le nom reste uniqueLes noms en minuscules s'écrivent sans guillemets dans PostgreSQL.identifier_renamed
names over 63 bytesraccourci avec un hachageLes noms PostgreSQL font 63 octets au plus ; le hachage distingue les noms raccourcis.name_shortened
GEOMETRY, POINT, POLYGON...WKB dans byteaSans PostGIS, le format binaire standard (well-known binary) garde chaque géométrie à l'identique.geometry_as_wkb
INET4, INET6, UUID (MariaDB)inet, uuidPostgreSQL a les mêmes types.
BINARY, VARBINARY, BLOBbyteaLes octets restent des octets.
PARTITION BYune seule tableLes lignes sont les mêmes ; partitionnez à nouveau la table dans PostgreSQL si besoin.partitions_dropped
WITH SYSTEM VERSIONING (MariaDB)une table avec les lignes actuellesUn dump ne contient que les lignes actuelles.system_versioning_dropped
MyISAM, MEMORY and other enginesune table ordinairePostgreSQL a un seul moteur de tables.engine_dropped
views, triggers, procedures, functions and eventslistés dans le rapportLeur code est écrit dans le langage de MySQL ; ils sont donc listés pour que vous les recréiez.not_converted_objects
dumps without CREATE TABLEtypes déduits des valeursUn dump fait avec --no-create-info ne contient que des lignes, donc les types viennent des valeurs.no_schema
geometries in a dump without CREATE TABLEWKB dans byteaLe format de géométrie de MySQL est reconnu à partir des valeurs et stocké en WKB standard.geometry_inferred
_ci collationscomparaison exactePostgreSQL compare le texte exactement, sauf indication contraire.case_insensitive_collation
SET NAMES utf8 from utf8mb4 tablesconverti, avec un avertissementLe mysqldump de MySQL 5.x écrit par défaut en utf8 sur 3 octets et remplace chaque emoji par un point d'interrogation avant qu'un convertisseur ne voie le fichier.dump_lost_four_byte_chars
dumps without SET NAMES whose text isn't UTF-8lu comme Latin-1 (Windows-1252)Le latin1 de MySQL est le Windows-1252, l'encodage habituel de ces dumps.charset_guessed
sjis, cp932, gbk, big5 dumpslu comme UTF-8, avec un avertissementCes encodages ne sont pas encore décodés.unsupported_charset
UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rowsévaluéesCertains outils de dump écrivent les valeurs sous forme de ces appels ; ils sont lus comme les valeurs qu'ils produisent, le WKT géographique étant remis avec la longitude en premier.
other expressions in rowsNULLUn convertisseur qui n'exécute pas de base de données ne peut pas les évaluer.expression_value
DEFAULT in VALUESla valeur par défaut de la colonneUne valeur par défaut littérale est copiée dans la ligne.default_keyword_filled
DEFAULT in VALUES without a literal defaultNULLIl n'y a aucune valeur à copier.default_keyword_null
REPLACE and INSERT IGNORElignes simplesUn dump a une ligne par clé, donc les lignes sont les mêmes.replace_as_insert
types IntoSQL doesn't knowtextLe texte contient n'importe quelle valeur ; changez le type après le chargement.unknown_type
values that don't fit their columnNULLUn dump endommagé ou écrit à la main peut contenir des valeurs que sa colonne n'accepte pas ; elles deviennent NULL pour que le reste se charge.invalid_value_null
unreadable geometriesNULLDes octets qui ne forment pas une géométrie ne peuvent pas être stockés comme telle.bad_geometry
CREATE SEQUENCE (MariaDB)CREATE SEQUENCE avec sa valeur actuelleLes séquences PostgreSQL acceptent les mêmes options.

Charger le script dans PostgreSQL

Le script crée les tables, copie les lignes, puis ajoute les clés, les index et les clés étrangères ; il se charge donc vite et s'arrête à la première erreur.

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

Les clés étrangères sont ajoutées en NOT VALID, puis validées. Si une ligne pointe vers une ligne qui n'existe pas, le script vous indique quelle contrainte, au lieu d'échouer à mi-chemin.

Pour un gros dump, créez d'abord la base de données et exécutez le script avec psql depuis la machine du serveur.

Faire le dump MySQL pour un meilleur résultat

Le convertisseur lit ce que contient le dump. Ces options y gardent davantage de vos données :

  • --default-character-set=utf8mb4 garde les emoji et autres caractères sur 4 octets, qu'un dump utf8 perd avant que le convertisseur ne le voie.
  • --hex-blob écrit les colonnes binaires en hexadécimal, ce qui résiste aux copies entre systèmes.
  • --routines --triggers --events ajoutent ces objets au dump, pour que le rapport puisse les lister.

Ce qui n'est pas converti

Les vues, déclencheurs, procédures stockées, fonctions et événements sont écrits dans le langage propre à chaque base de données. Ils sont listés par nom dans le rapport, jamais supprimés en silence, pour que vous puissiez les réécrire pour PostgreSQL.

Comparé à pgloader

Les mêmes dumps, convertis et chargés par IntoSQL et par pgloader 3.6.7 sur une même machine (un i9-10900K à 10 cœurs avec un SATA SSD), en octobre 2026.

  • Une colonne TINYINT(1) contenant 2, -1 et -5 : pgloader charge true pour chacune, sans avertissement. IntoSQL garde les nombres et le rapport explique pourquoi.
  • Un dump avec une valeur par défaut 'it's', une table dont le nom contient un accent grave, un ENUM contenant la valeur vide et un TIME de plus de 24 heures : pgloader s'arrête. IntoSQL le convertit et le charge.
  • Ce que pgloader charge a perdu ses clés primaires, uniques et étrangères, et ses identités. IntoSQL les garde toutes.
  • Un dump de 5.3 GB et 30 millions de lignes : pgloader exige qu'il soit d'abord restauré dans un MySQL en marche, ce qui prend 18 minutes, puis manque de mémoire avec ses réglages par défaut. IntoSQL convertit le dump en 10 secondes, et psql charge le script en 3 minutes environ.
  • Sur 476 dumps de MySQL 5.5 à 9 et de MariaDB 10.3 à 12, chargés dans PostgreSQL 13 et 18 : aucune erreur de chargement, et aucune valeur modifiée sans une ligne dans le rapport.

Questions sur MySQL vers PostgreSQL

Pas de réponse ici ? Écrivez-nous depuis la page de contact, une personne vous répond. Page de contact

Mon dump est-il envoyé ?

Non. Le convertisseur s'exécute dans votre navigateur et lit le dump depuis votre disque par morceaux ; il ne quitte donc jamais votre ordinateur. L'application de bureau et la ligne de commande convertissent aussi sur votre ordinateur.

Quelle taille peut avoir le dump ?

Le convertisseur ne garde jamais tout le dump en mémoire : il lit le fichier deux fois, par plages, et écrit le script au fur et à mesure. Les dumps de plusieurs gigaoctets se convertissent dans le navigateur, dans l'application de bureau et avec la ligne de commande.

Lit-il les dumps MariaDB ?

Oui. Les dumps de mariadb-dump et de MariaDB 10.3 à 12 se convertissent de la même façon, y compris les colonnes INET6, INET4 et UUID de MariaDB.

Que deviennent les dates 0000-00-00 ?

PostgreSQL n'a pas de date zéro ; elles deviennent donc NULL et la colonne accepte NULL. Le rapport indique combien il y en avait dans chaque colonne.

Les valeurs AUTO_INCREMENT sont-elles conservées ?

Oui. Chaque colonne AUTO_INCREMENT devient une colonne d'identité, chaque ligne garde son identifiant, et le prochain identifiant reprend après le plus élevé, ou après l'AUTO_INCREMENT de la table s'il est plus grand.

TINYINT(1) devient-il boolean ?

Seulement si chaque valeur de la colonne vaut 0 ou 1. Si elle contient d'autres nombres, comme 2 ou -1, elle reste un nombre et le rapport explique pourquoi ; aucune valeur ne devient true par accident.

Et les procédures stockées et les déclencheurs ?

Ils sont listés par nom dans le rapport et ne sont pas convertis, car chaque base de données les écrit dans son propre langage. ON UPDATE CURRENT_TIMESTAMP est l'exception : il devient un déclencheur PostgreSQL.

Sur quelles versions de PostgreSQL le script se charge-t-il ?

PostgreSQL 13 à 18. Le même script se charge sur chacune, et s'arrête à la première erreur en cas de problème.