Pour convertir des tables SQLite en MySQL, un dump brut ne suffit jamais : la conversion de la base demande d'adapter les types, l'auto-incrément, les guillemets et les dates. Un script PHP ou Python qui copie table par table reste la voie la plus sûre, le dump corrigé à la main convient aux petites bases.
Quatre méthodes de conversion, du plus manuel au plus automatique : le dump SQL corrigé, l'export CSV rechargé par LOAD DATA, le script PDO en PHP, l'outil Python sqlite3-to-mysql. Toutes butent sur les mêmes différences de syntaxe, et l'application garde ensuite quelques requêtes propres à SQLite à réécrire.
Pourquoi un dump SQLite ne passe pas tel quel dans MySQL
Un dump SQLite échoue à l'import parce que les deux moteurs ne lisent pas les types de la même façon. SQLite range toute la base dans un seul database file et applique un typage par affinité : une colonne déclarée INTEGER accepte un texte sans protester. MySQL et MariaDB contrôlent chaque valeur selon le type déclaré et le mode SQL du serveur, strict par défaut depuis MySQL 5.7 (STRICT_TRANS_TABLES).
Les correspondances de types se résument ainsi :
| Déclaration SQLite | Équivalent côté MySQL | Point d'attention |
|---|---|---|
| INTEGER PRIMARY KEY AUTOINCREMENT | INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY | le mot-clé change de forme (AUTOINCREMENT vs AUTO_INCREMENT) |
| TEXT indexé | VARCHAR(n) | un index sur TEXT exige une longueur de préfixe |
| TEXT contenant des dates | DATETIME | format AAAA-MM-JJ HH:MM:SS obligatoire |
| INTEGER contenant un horodatage Unix | DATETIME via FROM_UNIXTIME() | ou garder un INT et convertir à la lecture |
| booléen stocké en 0 / 1 | TINYINT(1) | aucune transformation des valeurs |
| BLOB | BLOB, MEDIUMBLOB ou LONGBLOB | un BLOB MySQL plafonne à 64 Ko |
Les identifiants changent aussi d'habit : les noms entre guillemets doubles passent entre accents graves. Les lignes PRAGMA, BEGIN TRANSACTION et la table interne sqlite_sequence n'ont pas d'équivalent et disparaissent. Pour le moteur des nouvelles tables, InnoDB s'impose dès qu'il y a des clés étrangères ou des transactions, comme le détaille la page pour choisir entre MyISAM, InnoDB et MEMORY.
Corriger un dump SQL à la main, pour une poignée de tables
La commande .dump de l'outil sqlite3 produit un fichier texte en SQL pur, que la documentation de SQLite présente comme réutilisable par d'autres moteurs :
sqlite3 ancienne.db .dump > export.sql
Ce dump reste un brouillon. La méthode la plus propre consiste à créer d'abord les tables dans MySQL, en InnoDB et en utf8mb4, puis à ne garder du fichier que les instructions INSERT INTO. Les CREATE TABLE statements fail presque toujours quand on les importe sans retouche : un AUTOINCREMENT, un TEXT indexé ou un identifiant entre guillemets doubles suffit à arrêter l'import à la première ligne fautive. Sur plus de cinq ou six tables, le temps passé à corriger ce texte dépasse celui d'un script.
Passer par des fichiers CSV et LOAD DATA
L'export CSV contourne les erreurs de syntaxe, puisque seules les données voyagent. Chaque table sort de sqlite3 avec la ligne des noms de colonnes :
.headers on
.mode csv
.output clients.csv
SELECT * FROM clients;
.output stdout
Le fichier CSV se charge dans la table préparée à l'avance, par l'import de phpMyAdmin ou par LOAD DATA LOCAL INFILE. Depuis MySQL 8.0, l'option local_infile est désactivée par défaut côté serveur, et beaucoup d'hébergements mutualisés la laissent fermée : phpMyAdmin reste alors la seule porte. Les valeurs NULL demandent une attention particulière, car un champ vide du CSV arrive en chaîne vide et non en NULL.
Copier table par table avec un script PHP et PDO
Un script PHP avec PDO lit la base SQLite et écrit dans MySQL sans passer par aucun fichier intermédiaire. Les requêtes préparées gèrent les apostrophes, les accents et les NULL sans réécriture, pour la raison qui les rend indispensables contre les injections de code dans les formulaires :
$src = new PDO('sqlite:/chemin/ancienne.db');
$dst = new PDO('mysql:host=localhost;dbname=site;charset=utf8mb4', $user, $pass,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$ins = $dst->prepare('INSERT INTO clients (id, nom, email, cree_le) VALUES (?, ?, ?, ?)');
$dst->beginTransaction();
foreach ($src->query('SELECT id, nom, email, cree_le FROM clients') as $l) {
$ins->execute([$l['id'], $l['nom'], $l['email'], $l['cree_le']]);
}
$dst->commit();
La transaction accélère fortement l'écriture et annule tout en cas d'erreur. Le script se lance en ligne de commande, hors de la durée d'exécution limitée des pages web. En insérant les identifiants d'origine, InnoDB recale son compteur AUTO_INCREMENT sur la plus grande valeur reçue : les clés étrangères des autres tables restent justes. Pendant la copie, un SET FOREIGN_KEY_CHECKS = 0 évite qu'une table fille arrive avant sa table mère.
Automatiser la conversion en une seule commande
Le paquet Python sqlite3-to-mysql, open source et installé par pip, convertit une base complète en une commande : structure, données et index. Les tutoriels anglophones titrés « migrate SQLite database to MySQL with Python » ou « convert SQLite to MySQL with a Python script » décrivent presque tous cet outil :
pip install sqlite3-to-mysql
sqlite3mysql --sqlite-file ancienne.db --mysql-database site --mysql-user utilisateur -p
L'option -p fait demander le mot de passe au lancement, ce qui évite de le laisser dans l'historique du shell. L'outil exige un accès direct au serveur MySQL, donc une machine locale, un VPS ou un tunnel SSH vers l'hébergement. À côté, des convertisseurs graphiques comme Full Convert ou Stellar Converter for Database ciblent les bases lourdes ; leur licence et leurs limites se lisent chez l'éditeur, et un free online tool qui réclame l'envoi du fichier .db expose toutes les données qu'il contient.
Les requêtes de l'application à réécrire pour MySQL
La migration des tables ne termine pas le travail : le code qui interrogeait SQLite contient des tournures que MySQL refuse ou comprend autrement.
- INSERT OR REPLACE devient REPLACE INTO, ou mieux INSERT … ON DUPLICATE KEY UPDATE ; INSERT OR IGNORE devient INSERT IGNORE.
- L'opérateur || concatène sous SQLite, alors que MySQL le lit comme un OU logique, sauf en mode PIPES_AS_CONCAT : CONCAT() le remplace.
- datetime('now') devient NOW(), strftime() devient DATE_FORMAT() avec d'autres codes de format.
- Les requêtes WITH (expressions de table communes), y compris WITH RECURSIVE, fonctionnent à partir de MySQL 8.0 et MariaDB 10.2, pas sur MySQL 5.7.
- Les comparaisons de texte deviennent insensibles à la casse avec les collations utf8mb4 par défaut, là où SQLite distinguait majuscules et minuscules.
Un projet en JavaScript ou en PHP qui passait par un ORM n'a souvent qu'à changer sa chaîne de connexion, mais les requêtes écrites à la main se relisent une par une.
Contrôler la copie avant de supprimer le fichier .db
La vérification d'une conversion SQLite vers MySQL commence par compter les lignes de chaque table des deux côtés, puis par relire quelques enregistrements porteurs d'accents, de dates et de NULL. Le fichier .db d'origine reste intact tant que l'application n'a pas tourné quelques jours sur le nouveau serveur. Une table qui stockait des documents dans une colonne texte gagne souvent à passer au type JSON natif plutôt qu'à stocker du JSON dans un BLOB. Les autres sujets d'administration, de l'export d'une base à la sauvegarde, se trouvent dans la rubrique des bases de données SQL.
Sources : documentation de SQLite (outil en ligne de commande sqlite3, .dump, .mode csv, typage par affinité) ; manuel de référence de MySQL 8.4 (LOAD DATA, local_infile, modes SQL, WITH) ; manuel de php.net (extension PDO) ; dépôt du projet sqlite3-to-mysql.














