Un moyen simple et rapide de migrer SQLite3 vers MySQL? [fermé]

224

Quelqu'un connaît un moyen rapide et facile de migrer une base de données SQLite3 vers MySQL?

Stephen Cox
la source

Réponses:

62

Voici une liste de convertisseurs (non mis à jour depuis 2011):


Une autre méthode qui fonctionnerait bien mais est rarement mentionnée est la suivante: utilisez une classe ORM qui résume les différences de base de données spécifiques pour vous. par exemple, vous les obtenez en PHP ( RedBean ), Python (couche ORM de Django, Storm , SqlAlchemy ), Ruby on Rails ( ActiveRecord ), Cocoa ( CoreData )

c'est-à-dire que vous pourriez faire ceci:

  1. Chargez les données de la base de données source à l'aide de la classe ORM.
  2. Stockez les données en mémoire ou sérialisez sur le disque.
  3. Stockez les données dans la base de données de destination à l'aide de la classe ORM.
David d C e Freitas
la source
107

Tout le monde semble commencer avec quelques expressions greps et perl et vous obtenez en quelque sorte quelque chose qui fonctionne pour votre ensemble de données particulier, mais vous ne savez pas s'il a importé les données correctement ou non. Je suis sérieusement surpris que personne n'ait construit une bibliothèque solide qui puisse convertir entre les deux.

Voici une liste de TOUTES les différences de syntaxe SQL que je connais entre les deux formats de fichier: Les lignes commençant par:

  • COMMENCER LA TRANSACTION
  • COMMETTRE
  • sqlite_sequence
  • CRÉER UN INDICE UNIQUE

ne sont pas utilisés dans MySQL

  • Utilisations SQLlite CREATE TABLE/INSERT INTO "table_name"et MySQLCREATE TABLE/INSERT INTO table_name
  • MySQL n'utilise pas de guillemets dans la définition de schéma
  • MySQL utilise des guillemets simples pour les chaînes à l'intérieur des INSERT INTOclauses
  • SQLlite et MySQL ont différentes manières d'échapper les chaînes à l'intérieur des INSERT INTOclauses
  • SQLlite utilise 't'et 'f'pour les booléens, MySQL utilise 1et 0(une simple expression régulière pour cela peut échouer lorsque vous avez une chaîne comme: `` Je le fais, vous ne le faites pas '' à l'intérieur de votre INSERT INTO)
  • SQLLite utilise AUTOINCREMENT, MySQL utiliseAUTO_INCREMENT

Voici un script perl piraté très basique qui fonctionne pour mon jeu de données et vérifie beaucoup plus de ces conditions que les autres scripts perl que j'ai trouvés sur le web. Nu garantit que cela fonctionnera pour vos données, mais n'hésitez pas à modifier et à publier ici.

#! /usr/bin/perl

while ($line = <>){
    if (($line !~  /BEGIN TRANSACTION/) && ($line !~ /COMMIT/) && ($line !~ /sqlite_sequence/) && ($line !~ /CREATE UNIQUE INDEX/)){

        if ($line =~ /CREATE TABLE \"([a-z_]*)\"(.*)/i){
            $name = $1;
            $sub = $2;
            $sub =~ s/\"//g;
            $line = "DROP TABLE IF EXISTS $name;\nCREATE TABLE IF NOT EXISTS $name$sub\n";
        }
        elsif ($line =~ /INSERT INTO \"([a-z_]*)\"(.*)/i){
            $line = "INSERT INTO $1$2\n";
            $line =~ s/\"/\\\"/g;
            $line =~ s/\"/\'/g;
        }else{
            $line =~ s/\'\'/\\\'/g;
        }
        $line =~ s/([^\\'])\'t\'(.)/$1THIS_IS_TRUE$2/g;
        $line =~ s/THIS_IS_TRUE/1/g;
        $line =~ s/([^\\'])\'f\'(.)/$1THIS_IS_FALSE$2/g;
        $line =~ s/THIS_IS_FALSE/0/g;
        $line =~ s/AUTOINCREMENT/AUTO_INCREMENT/g;
        print $line;
    }
}
Shalmanais
la source
8
Alex Martelli a fait un excellent travail en réécrivant cela en python sur stackoverflow.com/questions/1067060/perl-to-python
Jiaaro
J'ai ajouté le script python complet (le script perl seul ne fonctionnait pas tout à fait pour moi ... avait besoin d'un traitement supplémentaire pour gérer les clés étrangères et les index)
Jiaaro
J'ai réécrit cette réponse sur l'autre question stackoverflow.com/questions/1067060/_/1070463#1070463
Brad Gilbert
2
COMMIT et CREATE UNIQUE INDEX sont des commandes MySQL valides, veuillez le corriger.
niutech
5
Je comprends que votre script est "rapide et sale", mais aussi très utile, alors voici quelques ajouts / corrections de bugs: * après && ($line !~ /CREATE UNIQUE INDEX/) ajout && ($line !~ /PRAGMA foreign_keys=OFF/) * le regex correspondant au nom de la table manque des chiffres, c'est-à-dire $line =~ /INSERT INTO \"([a-z_]*)\"(.*)/qu'il doit y avoir au lieu d' $line =~ /INSERT INTO \"([a-z_1-9]*)\"(.*)/ espérer que cela aide l'avenir lecteurs
Michał Leon
50

Voici un script python, construit à partir de la réponse de Shalmanese et de l'aide d'Alex martelli sur Translating Perl to Python

J'en fais un wiki communautaire, alors n'hésitez pas à le modifier et à le refactoriser tant qu'il ne casse pas la fonctionnalité (heureusement, nous pouvons simplement revenir en arrière) - C'est assez moche mais ça marche

utiliser comme tel (en supposant que le script s'appelle dump_for_mysql.py:

sqlite3 sample.db .dump | python dump_for_mysql.py > dump.sql

Que vous pouvez ensuite importer dans mysql

note - vous devez ajouter manuellement les contraintes de clé étrangère car sqlite ne les prend pas réellement en charge

voici le script:

#!/usr/bin/env python

import re
import fileinput

def this_line_is_useless(line):
    useless_es = [
        'BEGIN TRANSACTION',
        'COMMIT',
        'sqlite_sequence',
        'CREATE UNIQUE INDEX',
        'PRAGMA foreign_keys=OFF',
    ]
    for useless in useless_es:
        if re.search(useless, line):
            return True

def has_primary_key(line):
    return bool(re.search(r'PRIMARY KEY', line))

searching_for_end = False
for line in fileinput.input():
    if this_line_is_useless(line):
        continue

    # this line was necessary because '');
    # would be converted to \'); which isn't appropriate
    if re.match(r".*, ''\);", line):
        line = re.sub(r"''\);", r'``);', line)

    if re.match(r'^CREATE TABLE.*', line):
        searching_for_end = True

    m = re.search('CREATE TABLE "?(\w*)"?(.*)', line)
    if m:
        name, sub = m.groups()
        line = "DROP TABLE IF EXISTS %(name)s;\nCREATE TABLE IF NOT EXISTS `%(name)s`%(sub)s\n"
        line = line % dict(name=name, sub=sub)
    else:
        m = re.search('INSERT INTO "(\w*)"(.*)', line)
        if m:
            line = 'INSERT INTO %s%s\n' % m.groups()
            line = line.replace('"', r'\"')
            line = line.replace('"', "'")
    line = re.sub(r"([^'])'t'(.)", "\1THIS_IS_TRUE\2", line)
    line = line.replace('THIS_IS_TRUE', '1')
    line = re.sub(r"([^'])'f'(.)", "\1THIS_IS_FALSE\2", line)
    line = line.replace('THIS_IS_FALSE', '0')

    # Add auto_increment if it is not there since sqlite auto_increments ALL
    # primary keys
    if searching_for_end:
        if re.search(r"integer(?:\s+\w+)*\s*PRIMARY KEY(?:\s+\w+)*\s*,", line):
            line = line.replace("PRIMARY KEY", "PRIMARY KEY AUTO_INCREMENT")
        # replace " and ' with ` because mysql doesn't like quotes in CREATE commands 
        if line.find('DEFAULT') == -1:
            line = line.replace(r'"', r'`').replace(r"'", r'`')
        else:
            parts = line.split('DEFAULT')
            parts[0] = parts[0].replace(r'"', r'`').replace(r"'", r'`')
            line = 'DEFAULT'.join(parts)

    # And now we convert it back (see above)
    if re.match(r".*, ``\);", line):
        line = re.sub(r'``\);', r"'');", line)

    if searching_for_end and re.match(r'.*\);', line):
        searching_for_end = False

    if re.match(r"CREATE INDEX", line):
        line = re.sub('"', '`', line)

    if re.match(r"AUTOINCREMENT", line):
        line = re.sub("AUTOINCREMENT", "AUTO_INCREMENT", line)

    print line,
Jiaaro
la source
2
Salut Jim, sur mon jeu de données, chaque première instruction INSERT est entourée d'un guillemet arrière au lieu d'un guillemet simple: __ DROP TABLE IF EXISTS schema_migrations; CRÉER UN TABLEAU SI IL N'EXISTE PAS schema_migrations( versionvarchar (255) NOT NULL); INSÉRER DANS schema_migrations VALUES ( 20100714032840); INSERT INTO schema_migrations VALUES ('20100714033251'); __
David
eh bien ... cela n'apparaît pas ci-dessus, mais les guillemets apparaissent à l'intérieur des VALEURS ([ICI] 20100714032840 [/ ICI])
David
1
L'AUTOINCREMENT dans Mysql est AUTO_INCREMENT. Le script ne tient pas compte de cela.
giuseppe
Cela ne fonctionne pas pour une base de données wiki multimédia. Beaucoup d'erreurs: Blobvartype de données, back ticks dans l'instruction CREATE ...
Frank Hintsch
1
ne fonctionne pas. Peut-être que toutes les conditions ne sont pas prises en considération ...
Himanshu Bansal
10

La méthode la plus simple et rapide consiste probablement à utiliser la commande sqlite .dump, dans ce cas, créez un vidage de la base de données exemple.

sqlite3 sample.db .dump > dump.sql

Vous pouvez ensuite (en théorie) l'importer dans la base de données mysql, dans ce cas la base de données de test sur le serveur de base de données 127.0.0.1, en utilisant l'utilisateur root.

mysql -p -u root -h 127.0.0.1 test < dump.sql

Je dis en théorie car il y a quelques différences entre les grammaires.

Dans sqlite, les transactions commencent

BEGIN TRANSACTION;
...
COMMIT;

MySQL utilise juste

BEGIN;
...
COMMIT;

Il y a d'autres problèmes similaires (varchars et guillemets reviennent à l'esprit) mais rien à trouver et à remplacer n'a pu être résolu.

Vous devriez peut-être vous demander pourquoi vous migrez, si les performances / la taille de la base de données sont le problème, peut-être envisager de réorganiser le schéma, si le système passe à un produit plus puissant, cela pourrait être le moment idéal pour planifier l'avenir de vos données.

Richard Gourlay
la source
2
mais la tâche la plus difficile est la différence entre les grammaires de semaine
francois
8
aptitude install sqlfairy libdbd-sqlite3-perl

sqlt -f DBI --dsn dbi:SQLite:../.open-tran/ten-sq.db -t MySQL --add-drop-table > mysql-ten-sq.sql
sqlt -f DBI --dsn dbi:SQLite:../.open-tran/ten-sq.db -t Dumper --use-same-auth > sqlite2mysql-dumper.pl
chmod +x sqlite2mysql-dumper.pl
./sqlite2mysql-dumper.pl --help
./sqlite2mysql-dumper.pl --add-truncate --mysql-loadfile > mysql-dump.sql
sed -e 's/LOAD DATA INFILE/LOAD DATA LOCAL INFILE/' -i mysql-dump.sql

echo 'drop database `ten-sq`' | mysql -p -u root
echo 'create database `ten-sq` charset utf8' | mysql -p -u root
mysql -p -u root -D ten-sq < mysql-ten-sq.sql
mysql -p -u root -D ten-sq < mysql-dump.sql
Dashamir Hoxha
la source
7

Je viens de passer par ce processus, et il y a beaucoup de très bonnes informations et aide dans ce Q / R, mais j'ai trouvé que je devais rassembler divers éléments (plus certains d'autres Q / As) pour obtenir une solution de travail dans afin de réussir la migration.

Cependant, même après avoir combiné les réponses existantes, j'ai constaté que le script Python ne fonctionnait pas entièrement pour moi car il ne fonctionnait pas lorsqu'il y avait plusieurs occurrences booléennes dans un INSERT. Voir ici pourquoi c'était le cas.

Donc, je pensais publier ma réponse fusionnée ici. Le mérite revient à ceux qui ont contribué ailleurs, bien sûr. Mais je voulais donner quelque chose en retour et faire gagner du temps aux autres.

Je posterai le script ci-dessous. Mais d'abord, voici les instructions pour une conversion ...

J'ai exécuté le script sur OS X 10.7.5 Lion. Python a fonctionné hors de la boîte.

Pour générer le fichier d'entrée MySQL à partir de votre base de données SQLite3 existante, exécutez le script sur vos propres fichiers comme suit,

Snips$ sqlite3 original_database.sqlite3 .dump | python ~/scripts/dump_for_mysql.py > dumped_data.sql

J'ai ensuite copié le fichier dumped_sql.sql résultant sur une boîte Linux exécutant Ubuntu 10.04.4 LTS où ma base de données MySQL devait résider.

Un autre problème que j'ai eu lors de l'importation du fichier MySQL était que certains caractères Unicode UTF-8 (en particulier les guillemets simples) n'étaient pas importés correctement, j'ai donc dû ajouter un commutateur à la commande pour spécifier UTF-8.

La commande résultante pour entrer les données dans une nouvelle base de données MySQL vide fessée est la suivante:

Snips$ mysql -p -u root -h 127.0.0.1 test_import --default-character-set=utf8 < dumped_data.sql

Laissez-le cuire, et ça devrait être ça! N'oubliez pas d'examiner vos données avant et après.

Ainsi, comme l'OP l'a demandé, c'est rapide et facile, quand vous savez comment! :-)

Soit dit en passant, une chose dont je n'étais pas sûr avant d'examiner cette migration, était de savoir si les valeurs des champs created_at et updated_at seraient préservées - la bonne nouvelle pour moi est qu'elles le sont, afin que je puisse migrer mes données de production existantes.

Bonne chance!

METTRE À JOUR

Depuis que j'ai fait ce changement, j'ai remarqué un problème que je n'avais pas remarqué auparavant. Dans mon application Rails, mes champs de texte sont définis comme «chaîne», et cela se poursuit jusqu'au schéma de base de données. Le processus décrit ici entraîne leur définition comme VARCHAR (255) dans la base de données MySQL. Cela place une limite de 255 caractères sur ces tailles de champ - et tout ce qui était au-delà a été tronqué silencieusement lors de l'importation. Pour prendre en charge une longueur de texte supérieure à 255, le schéma MySQL devrait utiliser «TEXT» plutôt que VARCHAR (255), je crois. Le processus défini ici n'inclut pas cette conversion.


Voici le script Python fusionné et révisé qui a fonctionné pour mes données:

#!/usr/bin/env python

import re
import fileinput

def this_line_is_useless(line):
    useless_es = [
        'BEGIN TRANSACTION',
        'COMMIT',
        'sqlite_sequence',
        'CREATE UNIQUE INDEX',        
        'PRAGMA foreign_keys=OFF'
        ]
    for useless in useless_es:
        if re.search(useless, line):
            return True

def has_primary_key(line):
    return bool(re.search(r'PRIMARY KEY', line))

searching_for_end = False
for line in fileinput.input():
    if this_line_is_useless(line): continue

    # this line was necessary because ''); was getting
    # converted (inappropriately) to \');
    if re.match(r".*, ''\);", line):
        line = re.sub(r"''\);", r'``);', line)

    if re.match(r'^CREATE TABLE.*', line):
        searching_for_end = True

    m = re.search('CREATE TABLE "?([A-Za-z_]*)"?(.*)', line)
    if m:
        name, sub = m.groups()
        line = "DROP TABLE IF EXISTS %(name)s;\nCREATE TABLE IF NOT EXISTS `%(name)s`%(sub)s\n"
        line = line % dict(name=name, sub=sub)
        line = line.replace('AUTOINCREMENT','AUTO_INCREMENT')
        line = line.replace('UNIQUE','')
        line = line.replace('"','')
    else:
        m = re.search('INSERT INTO "([A-Za-z_]*)"(.*)', line)
        if m:
            line = 'INSERT INTO %s%s\n' % m.groups()
            line = line.replace('"', r'\"')
            line = line.replace('"', "'")
            line = re.sub(r"(?<!')'t'(?=.)", r"1", line)
            line = re.sub(r"(?<!')'f'(?=.)", r"0", line)

    # Add auto_increment if it's not there since sqlite auto_increments ALL
    # primary keys
    if searching_for_end:
        if re.search(r"integer(?:\s+\w+)*\s*PRIMARY KEY(?:\s+\w+)*\s*,", line):
            line = line.replace("PRIMARY KEY", "PRIMARY KEY AUTO_INCREMENT")
        # replace " and ' with ` because mysql doesn't like quotes in CREATE commands

    # And now we convert it back (see above)
    if re.match(r".*, ``\);", line):
        line = re.sub(r'``\);', r"'');", line)

    if searching_for_end and re.match(r'.*\);', line):
        searching_for_end = False

    if re.match(r"CREATE INDEX", line):
        line = re.sub('"', '`', line)

    print line,
Snips
la source
1
Merci. Le script tel qu'il est actuellement écrit ci-dessus contient une erreur de syntaxe; le "else:" à la ligne 41 n'est pas au niveau de retrait approprié. Il n'est pas clair pour moi si les lignes au-dessus devraient être en retrait ou si quelque chose d'autre se passe. Voulez-vous mettre à jour?
Dan Tenenbaum
5

J'ai récemment dû migrer de MySQL vers JavaDB pour un projet sur lequel notre équipe travaille. J'ai trouvé une bibliothèque Java écrite par Apache appelée DdlUtils qui a rendu cela assez facile. Il fournit une API qui vous permet d'effectuer les opérations suivantes:

  1. Découvrez le schéma d'une base de données et exportez-le sous forme de fichier XML.
  2. Modifiez une base de données en fonction de ce schéma.
  3. Importez des enregistrements d'une base de données vers une autre, en supposant qu'ils ont le même schéma.

Les outils avec lesquels nous nous sommes retrouvés n'étaient pas complètement automatisés, mais ils fonctionnaient plutôt bien. Même si votre application n'est pas en Java, il ne devrait pas être trop difficile de concocter quelques petits outils pour effectuer une migration unique. Je pense que j'ai pu tirer de notre migration avec moins de 150 lignes de code.

Programmeur hors-la-loi
la source
4

Il n'y a aucun besoin de script, commande, etc ...

vous devez uniquement exporter votre base de données sqlite en tant que .csv fichier, puis l'importer dans Mysql en utilisant phpmyadmin.

Je l'ai utilisé et cela a fonctionné de façon incroyable ...

NavidIvanian
la source
En combinaison avec cela , c'est la seule réponse qui a fonctionné pour moi.
cdauth
3

Basé sur la solution de Jims: un moyen simple et rapide de migrer SQLite3 vers MySQL?

sqlite3 your_sql3_database.db .dump | python ./dump.py > your_dump_name.sql
cat your_dump_name.sql | sed '1d' | mysql --user=your_mysql_user --default-character-set=utf8 your_mysql_db -p  

Cela fonctionne pour moi. J'utilise sed juste pour jeter la première ligne, qui n'est pas semblable à mysql, mais vous pourriez aussi bien modifier le script dump.py pour jeter cette ligne.

alekwisnia
la source
1
J'ai eu quelques problèmes de codage UTF-8 avec les données importées, mais l'ajout de --default-character-set = utf8 à la commande d'importation semble avoir résolu ce problème. Tiré de cette Q / A: stackoverflow.com/questions/346092/…
Snips
Ok, j'ai ajouté ceci - est-ce que ça va?
alekwisnia
C'est là que j'utilise l'interrupteur supplémentaire, oui.
Snips
3

Obtenez un vidage SQL

moose@pc08$ sqlite3 mySqliteDatabase.db .dump > myTemporarySQLFile.sql

Importer le vidage dans MySQL

Pour les petites importations:

moose@pc08$ mysql -u <username> -p
Enter password:
....
mysql> use somedb;
Database changed
mysql> source myTemporarySQLFile.sql;

ou

mysql -u root -p somedb < myTemporarySQLFile.sql

Cela vous demandera un mot de passe. Attention: si vous souhaitez saisir directement votre mot de passe, vous devez le faire SANS espace, directement après -p:

mysql -u root -pYOURPASS somedb < myTemporarySQLFile.sql

Pour les décharges plus importantes:

mysqlimport ou d'autres outils d'importation comme BigDump .

BigDump vous donne une barre de progression:

entrez la description de l'image ici

Martin Thoma
la source
12
Cela ne fonctionne pas en raison de légères différences de syntaxe et de drapeaux dans sqlite vs mysql. Vous devez toujours le convertir manuellement.
dlite922
1

Ha ... j'aurais aimé l'avoir trouvé en premier! Ma réponse a été à ce poste ... script pour convertir le fichier sql dump mysql dans un format qui peut être importé dans sqlite3 db

La combinaison des deux serait exactement ce dont j'avais besoin:


Lorsque la base de données sqlite3 va être utilisée avec ruby, vous pouvez changer:

tinyint([0-9]*) 

à:

sed 's/ tinyint(1*) / boolean/g ' |
sed 's/ tinyint([0|2-9]*) / integer /g' |

hélas, cela ne fonctionne qu'à moitié parce que même si vous insérez des 1 et des 0 dans un champ marqué booléen, sqlite3 les stocke en tant que 1 et 0, vous devez donc passer par et faire quelque chose comme:

Table.find(:all, :conditions => {:column => 1 }).each { |t| t.column = true }.each(&:save)
Table.find(:all, :conditions => {:column => 0 }).each { |t| t.column = false}.each(&:save)

mais il était utile d'avoir le fichier sql à regarder pour trouver tous les booléens.

daicoden
la source
1

J'ai écrit ce script simple en Python3. Il peut être utilisé comme classe incluse ou script autonome appelé via un shell de terminal. Par défaut, il importe tous les entiers en tant que int(11)et les chaînes en tant quevarchar(300) , mais tout ce qui peut être ajusté dans les arguments du constructeur ou du script respectivement.

REMARQUE: il nécessite MySQL Connector / Python 2.0.4 ou supérieur

Voici un lien vers la source sur GitHub si vous trouvez le code ci-dessous difficile à lire: https://github.com/techouse/sqlite3-to-mysql

#!/usr/bin/env python3

__author__ = "Klemen Tušar"
__email__ = "[email protected]"
__copyright__ = "GPL"
__version__ = "1.0.1"
__date__ = "2015-09-12"
__status__ = "Production"

import os.path, sqlite3, mysql.connector
from mysql.connector import errorcode


class SQLite3toMySQL:
    """
    Use this class to transfer an SQLite 3 database to MySQL.

    NOTE: Requires MySQL Connector/Python 2.0.4 or higher (https://dev.mysql.com/downloads/connector/python/)
    """
    def __init__(self, **kwargs):
        self._properties = kwargs
        self._sqlite_file = self._properties.get('sqlite_file', None)
        if not os.path.isfile(self._sqlite_file):
            print('SQLite file does not exist!')
            exit(1)
        self._mysql_user = self._properties.get('mysql_user', None)
        if self._mysql_user is None:
            print('Please provide a MySQL user!')
            exit(1)
        self._mysql_password = self._properties.get('mysql_password', None)
        if self._mysql_password is None:
            print('Please provide a MySQL password')
            exit(1)
        self._mysql_database = self._properties.get('mysql_database', 'transfer')
        self._mysql_host = self._properties.get('mysql_host', 'localhost')

        self._mysql_integer_type = self._properties.get('mysql_integer_type', 'int(11)')
        self._mysql_string_type = self._properties.get('mysql_string_type', 'varchar(300)')

        self._sqlite = sqlite3.connect(self._sqlite_file)
        self._sqlite.row_factory = sqlite3.Row
        self._sqlite_cur = self._sqlite.cursor()

        self._mysql = mysql.connector.connect(
            user=self._mysql_user,
            password=self._mysql_password,
            host=self._mysql_host
        )
        self._mysql_cur = self._mysql.cursor(prepared=True)
        try:
            self._mysql.database = self._mysql_database
        except mysql.connector.Error as err:
            if err.errno == errorcode.ER_BAD_DB_ERROR:
                self._create_database()
            else:
                print(err)
                exit(1)

    def _create_database(self):
        try:
            self._mysql_cur.execute("CREATE DATABASE IF NOT EXISTS `{}` DEFAULT CHARACTER SET 'utf8'".format(self._mysql_database))
            self._mysql_cur.close()
            self._mysql.commit()
            self._mysql.database = self._mysql_database
            self._mysql_cur = self._mysql.cursor(prepared=True)
        except mysql.connector.Error as err:
            print('_create_database failed creating databse {}: {}'.format(self._mysql_database, err))
            exit(1)

    def _create_table(self, table_name):
        primary_key = ''
        sql = 'CREATE TABLE IF NOT EXISTS `{}` ( '.format(table_name)
        self._sqlite_cur.execute('PRAGMA table_info("{}")'.format(table_name))
        for row in self._sqlite_cur.fetchall():
            column = dict(row)
            sql += ' `{name}` {type} {notnull} {auto_increment}, '.format(
                name=column['name'],
                type=self._mysql_string_type if column['type'].upper() == 'TEXT' else self._mysql_integer_type,
                notnull='NOT NULL' if column['notnull'] else 'NULL',
                auto_increment='AUTO_INCREMENT' if column['pk'] else ''
            )
            if column['pk']:
                primary_key = column['name']
        sql += ' PRIMARY KEY (`{}`) ) ENGINE = InnoDB CHARACTER SET utf8'.format(primary_key)
        try:
            self._mysql_cur.execute(sql)
            self._mysql.commit()
        except mysql.connector.Error as err:
            print('_create_table failed creating table {}: {}'.format(table_name, err))
            exit(1)

    def transfer(self):
        self._sqlite_cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")
        for row in self._sqlite_cur.fetchall():
            table = dict(row)
            # create the table
            self._create_table(table['name'])
            # populate it
            print('Transferring table {}'.format(table['name']))
            self._sqlite_cur.execute('SELECT * FROM "{}"'.format(table['name']))
            columns = [column[0] for column in self._sqlite_cur.description]
            try:
                self._mysql_cur.executemany("INSERT IGNORE INTO `{table}` ({fields}) VALUES ({placeholders})".format(
                    table=table['name'],
                    fields=('`{}`, ' * len(columns)).rstrip(' ,').format(*columns),
                    placeholders=('%s, ' * len(columns)).rstrip(' ,')
                ), (tuple(data) for data in self._sqlite_cur.fetchall()))
                self._mysql.commit()
            except mysql.connector.Error as err:
                print('_insert_table_data failed inserting data into table {}: {}'.format(table['name'], err))
                exit(1)
        print('Done!')


def main():
    """ For use in standalone terminal form """
    import sys, argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('--sqlite-file', dest='sqlite_file', default=None, help='SQLite3 db file')
    parser.add_argument('--mysql-user', dest='mysql_user', default=None, help='MySQL user')
    parser.add_argument('--mysql-password', dest='mysql_password', default=None, help='MySQL password')
    parser.add_argument('--mysql-database', dest='mysql_database', default=None, help='MySQL host')
    parser.add_argument('--mysql-host', dest='mysql_host', default='localhost', help='MySQL host')
    parser.add_argument('--mysql-integer-type', dest='mysql_integer_type', default='int(11)', help='MySQL default integer field type')
    parser.add_argument('--mysql-string-type', dest='mysql_string_type', default='varchar(300)', help='MySQL default string field type')
    args = parser.parse_args()

    if len(sys.argv) == 1:
        parser.print_help()
        exit(1)

    converter = SQLite3toMySQL(
        sqlite_file=args.sqlite_file,
        mysql_user=args.mysql_user,
        mysql_password=args.mysql_password,
        mysql_database=args.mysql_database,
        mysql_host=args.mysql_host,
        mysql_integer_type=args.mysql_integer_type,
        mysql_string_type=args.mysql_string_type
    )
    converter.transfer()

if __name__ == '__main__':
    main()
Klemen Tušar
la source
0

Ce script est ok, sauf dans ce cas, bien sûr, j'ai rencontré:

INSERT INTO "requestcomparison_stopword" VALUES (149, 'f');
INSERT INTO "requestcomparison_stopword" VALUES (420, 't');

Le script devrait donner cette sortie:

INSERT INTO requestcomparison_stopword VALUES (149, 'f');
INSERT INTO requestcomparison_stopword VALUES (420, 't');

Mais donne à la place cette sortie:

INSÉRER DANS requestcomparison_stopword VALEURS (1490;
INSÉRER DANS requestcomparison_stopword VALEURS (4201;

avec d'étranges caractères non ascii autour des 0 et 1 derniers.

Cela ne s'est plus affiché lorsque j'ai commenté les lignes suivantes du code (43-46) mais d'autres problèmes sont apparus:


    line = re.sub(r"([^'])'t'(.)", "\1THIS_IS_TRUE\2", line)
    line = line.replace('THIS_IS_TRUE', '1')
    line = re.sub(r"([^'])'f'(.)", "\1THIS_IS_FALSE\2", line)
    line = line.replace('THIS_IS_FALSE', '0')

Ceci est juste un cas spécial, lorsque nous voulons ajouter une valeur étant 'f' ou 't' mais je ne suis pas vraiment à l'aise avec les expressions régulières, je voulais juste repérer ce cas pour être corrigé par quelqu'un.

Quoi qu'il en soit, merci beaucoup pour ce script pratique !!!


la source
0

Cette solution simple a fonctionné pour moi:

<?php
$sq = new SQLite3( 'sqlite3.db' );

$tables = $sq->query( 'SELECT name FROM sqlite_master WHERE type="table"' );

while ( $table = $tables->fetchArray() ) {
    $table = current( $table );
    $result = $sq->query( sprintf( 'SELECT * FROM %s', $table ) );

    if ( strpos( $table, 'sqlite' ) !== false )
        continue;

    printf( "-- %s\n", $table );
    while ( $row = $result->fetchArray( SQLITE3_ASSOC ) ) {
        $values = array_map( function( $value ) {
            return sprintf( "'%s'", mysql_real_escape_string( $value ) );
        }, array_values( $row ) );
        printf( "INSERT INTO `%s` VALUES( %s );\n", $table, implode( ', ', $values ) );
    }
}
soulseekah
la source
-5
echo ".dump" | sqlite3 /tmp/db.sqlite > db.sql

attention aux instructions CREATE

mgribov
la source