Schnelle und einfache Möglichkeit, SQLite3 auf MySQL zu migrieren? [geschlossen]

224

Kennt jemand eine schnelle und einfache Möglichkeit, eine SQLite3-Datenbank auf MySQL zu migrieren?

Stephen Cox
quelle

Antworten:

62

Hier ist eine Liste der Konverter (seit 2011 nicht aktualisiert):


Eine alternative Methode, die gut funktionieren würde, aber selten erwähnt wird, ist: Verwenden Sie eine ORM-Klasse, die bestimmte Datenbankunterschiede für Sie abstrahiert. zB erhalten Sie diese in PHP ( RedBean ), Python (Djangos ORM-Ebene, Storm , SqlAlchemy ), Ruby on Rails ( ActiveRecord ), Cocoa ( CoreData )

dh Sie könnten dies tun:

  1. Laden Sie Daten aus der Quelldatenbank mit der ORM-Klasse.
  2. Speichern Sie Daten im Speicher oder serialisieren Sie sie auf der Festplatte.
  3. Speichern Sie Daten mithilfe der ORM-Klasse in der Zieldatenbank.
David d C e Freitas
quelle
107

Jeder scheint mit ein paar Greps- und Perl-Ausdrücken zu beginnen, und Sie erhalten irgendwie etwas, das für Ihren bestimmten Datensatz funktioniert, aber Sie haben keine Ahnung, ob die Daten korrekt importiert wurden oder nicht. Ich bin ernsthaft überrascht, dass niemand eine solide Bibliothek aufgebaut hat, die zwischen den beiden konvertieren kann.

Hier eine Liste ALLER Unterschiede in der SQL-Syntax, die ich zwischen den beiden Dateiformaten kenne: Die Zeilen beginnen mit:

  • TRANSAKTION BEGINNEN
  • VERPFLICHTEN
  • sqlite_sequence
  • EINZIGARTIGEN INDEX ERSTELLEN

werden in MySQL nicht verwendet

  • SQLlite verwendet CREATE TABLE/INSERT INTO "table_name"und MySQL verwendetCREATE TABLE/INSERT INTO table_name
  • MySQL verwendet keine Anführungszeichen innerhalb der Schemadefinition
  • MySQL verwendet einfache Anführungszeichen für Zeichenfolgen innerhalb der INSERT INTOKlauseln
  • SQLlite und MySQL haben verschiedene Möglichkeiten, Zeichenfolgen in INSERT INTOKlauseln zu umgehen
  • SQLlite verwendet 't'und 'f'für Boolesche Werte verwendet MySQL 1und 0(ein einfacher regulärer Ausdruck hierfür kann fehlschlagen, wenn Sie eine Zeichenfolge wie "Ich tue, Sie tun es nicht" in Ihrem INSERT INTO).
  • SQLLite verwendet AUTOINCREMENT, MySQL verwendetAUTO_INCREMENT

Hier ist ein sehr einfaches gehacktes Perl-Skript, das für meinen Datensatz funktioniert und auf viele weitere dieser Bedingungen überprüft, die andere Perl-Skripte im Web gefunden haben. Nu garantiert, dass es für Ihre Daten funktioniert, Sie können es jedoch gerne ändern und hier veröffentlichen.

#! /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;
    }
}
Shalmanese
quelle
8
Alex Martelli hat großartige Arbeit geleistet und dies als Python unter stackoverflow.com/questions/1067060/perl-to-python
Jiaaro
Ich habe das komplette Python-Skript hinzugefügt (das Perl-Skript allein hat bei mir nicht ganz funktioniert ... es musste extra verarbeitet werden, um mit
Fremdschlüsseln
Ich schrieb diese Antwort auf die andere Frage stackoverflow.com/questions/1067060/_/1070463#1070463
Brad Gilbert
2
COMMIT und CREATE UNIQUE INDEX sind gültige MySQL-Befehle. Bitte beheben Sie diese.
Niutech
5
Ich verstehe, dass Ihr Skript "schnell und schmutzig" ist, aber auch sehr nützlich. Hier sind einige Ergänzungen / Bugfixes: * Nach dem && ($line !~ /CREATE UNIQUE INDEX/)Hinzufügen && ($line !~ /PRAGMA foreign_keys=OFF/) * fehlen bei der mit dem Tabellennamen übereinstimmenden Regex Ziffern, das heißt, $line =~ /INSERT INTO \"([a-z_]*)\"(.*)/es muss $line =~ /INSERT INTO \"([a-z_1-9]*)\"(.*)/ Hoffnung geben, dass dies der Zukunft hilft Leser
Michał Leon
50

Hier ist ein Python-Skript, das aus Shalmaneses Antwort und etwas Hilfe von Alex Martelli bei Translating Perl to Python aufgebaut ist

Ich mache es zum Community-Wiki, also zögern Sie nicht, es zu bearbeiten und umzugestalten, solange es die Funktionalität nicht beeinträchtigt (zum Glück können wir einfach einen Rollback durchführen) - Es ist ziemlich hässlich, funktioniert aber

Verwenden Sie wie folgt (vorausgesetzt, das Skript heißt dump_for_mysql.py:

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

Was Sie dann in MySQL importieren können

Hinweis - Sie müssen Fremdschlüsseleinschränkungen manuell hinzufügen, da SQLite diese nicht unterstützt

Hier ist das Skript:

#!/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
quelle
2
Hallo Jim, in meinem Datensatz wird jede erste INSERT-Anweisung durch ein Backquote anstelle eines einfachen Anführungszeichens umschlossen: __ DROP TABLE IF EXISTS schema_migrations; TABELLE ERSTELLEN, WENN NICHT EXISTIERT schema_migrations( versionvarchar (255) NOT NULL); INSERT INTO schema_migrations VALUES ( 20100714032840); INSERT INTO schema_migrations VALUES ('20100714033251'); __
David
Nun ... es wird oben nicht angezeigt, aber die Rückzitate erscheinen in den WERTEN ([HIER] 20100714032840 [/ HIER])
David
1
Das AUTOINCREMENT in MySQL ist AUTO_INCREMENT. Das Skript berücksichtigt dies nicht.
Giuseppe
Dies funktioniert nicht für eine Medien-Wiki-Datenbank. Viele Fehler: BlobvarDatentyp, Back Ticks in CREATE-Anweisung ...
Frank Hintsch
1
funktioniert nicht Möglicherweise werden nicht alle Bedingungen berücksichtigt ...
Himanshu Bansal
10

Der schnellste und einfachste Weg ist wahrscheinlich die Verwendung des Befehls sqlite .dump. In diesem Fall erstellen Sie einen Speicherauszug der Beispieldatenbank.

sqlite3 sample.db .dump > dump.sql

Sie können dies dann (theoretisch) mit dem Benutzer root in die MySQL-Datenbank importieren, in diesem Fall in die Testdatenbank auf dem Datenbankserver 127.0.0.1.

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

Ich sage theoretisch, da es einige Unterschiede zwischen den Grammatiken gibt.

In SQLite beginnen Transaktionen

BEGIN TRANSACTION;
...
COMMIT;

MySQL verwendet nur

BEGIN;
...
COMMIT;

Es gibt andere ähnliche Probleme (Varchars und doppelte Anführungszeichen fallen mir ein), aber nichts, was gefunden und ersetzt werden konnte, konnte nicht behoben werden.

Vielleicht sollten Sie sich fragen, warum Sie migrieren. Wenn Leistung / Datenbankgröße das Problem sind, sollten Sie das Schema neu definieren. Wenn das System auf ein leistungsfähigeres Produkt umstellt, ist dies möglicherweise der ideale Zeitpunkt, um die Zukunft Ihrer Daten zu planen.

Richard Gourlay
quelle
2
aber die schwierigste Aufgabe ist die Differenz betweek Grammatiken
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
quelle
7

Ich habe gerade diesen Prozess durchlaufen und es gibt eine Menge sehr guter Hilfe und Informationen in dieser Frage / Antwort, aber ich stellte fest, dass ich verschiedene Elemente (plus einige aus anderen Fragen / Antworten) zusammenführen musste, um eine funktionierende Lösung zu finden um erfolgreich zu migrieren.

Selbst nachdem ich die vorhandenen Antworten kombiniert hatte, stellte ich fest, dass das Python-Skript für mich nicht vollständig funktionierte, da es nicht funktionierte, wenn in einem INSERT mehrere boolesche Vorkommen vorkamen. Sehen Sie hier, warum das so war.

Also dachte ich, ich würde meine zusammengeführte Antwort hier veröffentlichen. Der Kredit geht natürlich an diejenigen, die anderswo beigetragen haben. Aber ich wollte etwas zurückgeben und anderen Zeit sparen, die folgen.

Ich werde das Skript unten posten. Aber zuerst hier die Anweisungen für eine Konvertierung ...

Ich habe das Skript unter OS X 10.7.5 Lion ausgeführt. Python hat sofort funktioniert.

Führen Sie das Skript wie folgt aus, um die MySQL-Eingabedatei aus Ihrer vorhandenen SQLite3-Datenbank zu generieren:

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

Ich habe dann die resultierende Datei dumped_sql.sql auf eine Linux-Box mit Ubuntu 10.04.4 LTS kopiert, auf der sich meine MySQL-Datenbank befinden sollte.

Ein weiteres Problem beim Importieren der MySQL-Datei war, dass einige Unicode-UTF-8-Zeichen (insbesondere einfache Anführungszeichen) nicht korrekt importiert wurden. Daher musste ich dem Befehl einen Schalter hinzufügen, um UTF-8 anzugeben.

Der resultierende Befehl zum Eingeben der Daten in eine neue leere MySQL-Datenbank lautet wie folgt:

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

Lass es kochen, und das sollte es sein! Vergessen Sie nicht, Ihre Daten vorher und nachher zu überprüfen.

Wie vom OP gefordert, ist es also schnell und einfach, wenn Sie wissen wie! :-)

Abgesehen davon war ich mir vor der Untersuchung dieser Migration nicht sicher, ob die Feldwerte created_at und updated_at erhalten bleiben würden. Die gute Nachricht für mich ist, dass dies der Fall ist, sodass ich meine vorhandenen Produktionsdaten migrieren kann.

Viel Glück!

AKTUALISIEREN

Seit ich diesen Wechsel vorgenommen habe, ist mir ein Problem aufgefallen, das ich vorher nicht bemerkt hatte. In meiner Rails-Anwendung sind meine Textfelder als "Zeichenfolge" definiert, was sich auf das Datenbankschema auswirkt. Der hier beschriebene Prozess führt dazu, dass diese in der MySQL-Datenbank als VARCHAR (255) definiert werden. Dadurch werden diese Feldgrößen auf 255 Zeichen begrenzt - und alles darüber hinaus wurde während des Imports stillschweigend abgeschnitten. Um eine Textlänge von mehr als 255 zu unterstützen, müsste das MySQL-Schema meiner Meinung nach 'TEXT' anstelle von VARCHAR (255) verwenden. Der hier definierte Prozess beinhaltet diese Konvertierung nicht.


Hier ist das zusammengeführte und überarbeitete Python-Skript, das für meine Daten funktioniert hat:

#!/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
quelle
1
Vielen Dank. Das oben beschriebene Skript enthält einen Syntaxfehler. Das "else:" in Zeile 41 befindet sich nicht auf der richtigen Einrückungsstufe. Mir ist nicht klar, ob die Zeilen darüber eingerückt werden sollen oder ob etwas anderes vor sich geht. Möchten Sie aktualisieren?
Dan Tenenbaum
5

Ich musste kürzlich für ein Projekt, an dem unser Team arbeitet, von MySQL auf JavaDB migrieren. Ich habe eine von Apache geschriebene Java-Bibliothek namens DdlUtils gefunden , die dies ziemlich einfach gemacht hat. Es bietet eine API, mit der Sie Folgendes tun können:

  1. Ermitteln Sie das Schema einer Datenbank und exportieren Sie es als XML-Datei.
  2. Ändern Sie eine Datenbank basierend auf diesem Schema.
  3. Importieren Sie Datensätze von einer Datenbank in eine andere, vorausgesetzt, sie haben dasselbe Schema.

Die Tools, mit denen wir endeten, waren nicht vollständig automatisiert, aber sie funktionierten ziemlich gut. Selbst wenn sich Ihre Anwendung nicht in Java befindet, sollte es nicht allzu schwierig sein, ein paar kleine Tools für eine einmalige Migration zu erstellen. Ich glaube, ich konnte unsere Migration mit weniger als 150 Codezeilen abbrechen.

Outlaw Programmer
quelle
4

Es ist kein Skript, Befehl usw. erforderlich.

Sie müssen Ihre SQLite-Datenbank nur als .csvDatei exportieren und dann mit phpmyadmin in MySQL importieren.

Ich habe es benutzt und es hat unglaublich funktioniert ...

NavidIvanian
quelle
In Kombination damit ist dies die einzige Antwort, die für mich funktioniert hat.
cdauth
3

Basierend auf Jims 'Lösung: Schnelle und einfache Möglichkeit, SQLite3 auf MySQL zu migrieren?

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  

Das funktioniert bei mir. Ich benutze sed nur, um die erste Zeile zu werfen, die nicht MySQL-ähnlich ist, aber Sie können auch das dump.py-Skript ändern, um diese Zeile wegzuwerfen.

Alekwisnia
quelle
1
Ich hatte einige UTF-8-Codierungsprobleme mit den importierten Daten, aber das Hinzufügen von --default-Zeichensatz = utf8 zum Importbefehl scheint dies behoben zu haben. Entnommen aus dieser Frage
Snips
Ok, ich habe das hinzugefügt - ist es in Ordnung?
Alewwisnia
Dort benutze ich den zusätzlichen Schalter, ja.
Snips
3

Holen Sie sich einen SQL-Dump

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

Dump in MySQL importieren

Für kleine Importe:

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

oder

mysql -u root -p somedb < myTemporarySQLFile.sql

Dadurch werden Sie zur Eingabe eines Kennworts aufgefordert. Bitte beachten Sie: Wenn Sie Ihr Passwort direkt eingeben möchten, müssen Sie dies OHNE Leerzeichen direkt nach -p:

mysql -u root -pYOURPASS somedb < myTemporarySQLFile.sql

Für größere Deponien:

mysqlimport oder andere Importtools wie BigDump .

BigDump gibt Ihnen einen Fortschrittsbalken:

Geben Sie hier die Bildbeschreibung ein

Martin Thoma
quelle
12
Dies funktioniert aufgrund geringfügiger Syntaxunterschiede und Flags in SQLite gegenüber MySQL nicht. Sie müssen es noch manuell konvertieren.
dlite922
1

Ha ... ich wünschte ich hätte das zuerst gefunden! Meine Antwort war auf dieses Post ... -Skript, um die MySQL-Dump-SQL-Datei in ein Format zu konvertieren, das in die SQLite3-Datenbank importiert werden kann

Die beiden zu kombinieren wäre genau das, was ich brauchte:


Wenn die sqlite3-Datenbank mit Ruby verwendet werden soll, möchten Sie möglicherweise Folgendes ändern:

tinyint([0-9]*) 

zu:

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

Leider funktioniert dies nur zur Hälfte, denn obwohl Sie Einsen und Nullen in ein mit Booleschem Wert gekennzeichnetes Feld einfügen, speichert sqlite3 sie als Einsen und Nullen, sodass Sie Folgendes tun müssen:

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)

Es war jedoch hilfreich, die SQL-Datei zu haben, um alle Booleschen Werte zu finden.

daicoden
quelle
1

Ich habe dieses einfache Skript in Python3 geschrieben. Es kann als enthaltene Klasse oder als eigenständiges Skript verwendet werden, das über eine Terminal-Shell aufgerufen wird. Standardmäßig werden alle Ganzzahlen als int(11)und Zeichenfolgen als importiert varchar(300), aber alles, was in den Konstruktor- bzw. Skriptargumenten angepasst werden kann.

HINWEIS: Es erfordert MySQL Connector / Python 2.0.4 oder höher

Hier ist ein Link zur Quelle auf GitHub, wenn Sie den folgenden Code schwer lesen können: 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
quelle
0

Dieses Skript ist in Ordnung, außer in diesem Fall, den ich natürlich getroffen habe:

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

Das Skript sollte diese Ausgabe geben:

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

Aber gibt stattdessen diese Ausgabe:

INSERT INTO requestcomparison_stopword VALUES (1490;
INSERT INTO requestcomparison_stopword VALUES (4201;

mit einigen seltsamen Nicht-ASCII-Zeichen um die letzten 0 und 1.

Dies wurde nicht mehr angezeigt, als ich die folgenden Zeilen des Codes (43-46) kommentierte, aber andere Probleme traten auf:


    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')

Dies ist nur ein Sonderfall, wenn wir einen Wert hinzufügen möchten, der 'f' oder 't' ist, ich aber mit regulären Ausdrücken nicht wirklich vertraut bin. Ich wollte nur diesen Fall erkennen, um von jemandem korrigiert zu werden.

Trotzdem vielen Dank für das praktische Skript !!!


quelle
0

Diese einfache Lösung hat bei mir funktioniert:

<?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
quelle
-5
echo ".dump" | sqlite3 /tmp/db.sqlite > db.sql

Achten Sie auf CREATE-Anweisungen

Mgibov
quelle