Szybki i łatwy sposób na migrację SQLite3 do MySQL? [Zamknięte]

224

Czy ktoś zna szybki i łatwy sposób migracji bazy danych SQLite3 do MySQL?

Stephen Cox
źródło

Odpowiedzi:

62

Oto lista konwerterów (nie aktualizowana od 2011 roku):


Alternatywną metodą, która działałaby ładnie, ale jest rzadko wymieniana, jest: użycie klasy ORM, która abstrahuje od ciebie określone różnice w bazach danych. np. dostajesz je w PHP ( RedBean ), Python (warstwa ORM Django, Storm , SqlAlchemy ), Ruby on Rails ( ActiveRecord ), Cocoa ( CoreData )

tzn. możesz to zrobić:

  1. Załaduj dane ze źródłowej bazy danych przy użyciu klasy ORM.
  2. Przechowuj dane w pamięci lub serializuj na dysk.
  3. Przechowuj dane w docelowej bazie danych za pomocą klasy ORM.
David d C e Freitas
źródło
107

Wygląda na to, że wszyscy zaczynają od kilku wyrażeń greps i perl, a ty dostajesz coś, co działa dla twojego konkretnego zestawu danych, ale nie masz pojęcia, czy dane zostały poprawnie zaimportowane, czy nie. Jestem naprawdę zaskoczony, że nikt nie zbudował solidnej biblioteki, która może konwertować między nimi.

Oto lista WSZYSTKICH różnic w składni SQL, o których wiem między dwoma formatami plików: Linie zaczynające się od:

  • ROZPOCZNIJ TRANSAKCJĘ
  • POPEŁNIĆ
  • ciąg_sqlite
  • UTWÓRZ UNIKALNY INDEKS

nie są używane w MySQL

  • Używa SQLlite CREATE TABLE/INSERT INTO "table_name"i MySQLCREATE TABLE/INSERT INTO table_name
  • MySQL nie używa cudzysłowów w definicji schematu
  • MySQL używa pojedynczych cudzysłowów dla ciągów znaków wewnątrz INSERT INTOklauzul
  • SQLlite i MySQL mają różne sposoby unikania ciągów znaków wewnątrz INSERT INTOklauzul
  • SQLlite używa, 't'a 'f'dla logicznych, MySQL używa 1i 0(prosty regex dla tego może się nie powieść, gdy masz ciąg taki jak: „Ja robię, ty nie” wewnątrz INSERT INTO)
  • Używa SQLLite AUTOINCREMENT, MySQLAUTO_INCREMENT

Oto bardzo prosty, zhakowany skrypt perla, który działa dla mojego zbioru danych i sprawdza, czy nie ma wielu innych warunków, które inne skrypty perla znalazłem w Internecie. Nu gwarantuje, że będzie działać na Twoje dane, ale możesz je zmodyfikować i opublikować tutaj.

#! /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
źródło
8
Alex Martelli spisał się świetnie, przepisując to jako python na stackoverflow.com/questions/1067060/perl-to-python
Jiaaro
Dodałem kompletny skrypt Pythona (sam skrypt perla nie działał dla mnie ... potrzebowałem dodatkowego przetwarzania do obsługi kluczy obcych i indeksów)
Jiaaro
Przepisałem tę odpowiedź na inne pytanie stackoverflow.com/questions/1067060/_/1070463#1070463
Brad Gilbert,
2
COMMIT i CREATE UNIQUE INDEX są poprawnymi poleceniami MySQL, proszę to naprawić.
niutech
5
Rozumiem, że twój skrypt jest „szybki i brudny”, ale także bardzo przydatny, więc oto kilka dodatków / poprawek: * po && ($line !~ /CREATE UNIQUE INDEX/)dodaniu && ($line !~ /PRAGMA foreign_keys=OFF/) * wyrażenie regularne pasujące do tabeli pomija cyfry, czyli zamiast tego $line =~ /INSERT INTO \"([a-z_]*)\"(.*)/musi być $line =~ /INSERT INTO \"([a-z_1-9]*)\"(.*)/ Nadzieja, że ​​to pomaga w przyszłości czytelnicy
Michał Leon
50

Oto skrypt w języku Python, zbudowany na podstawie odpowiedzi Shalmanese i pomocy Alexa Martelliego z Translating Perl to Python

Tworzę wiki społeczności, więc prosimy o edycję i refaktoryzację, pod warunkiem, że nie psuje funkcji (na szczęście możemy po prostu wycofać) - To jest brzydkie, ale działa

użyj tak (zakładając, że skrypt nazywa się dump_for_mysql.py:

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

Które możesz następnie zaimportować do mysql

Uwaga - należy ręcznie dodać ograniczenia klucza obcego, ponieważ narzędzie sqlite ich nie obsługuje

oto skrypt:

#!/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
źródło
2
Cześć Jim, w moim zbiorze danych każda pierwsza instrukcja INSERT jest zawijana w cudzysłów zamiast pojedynczego cudzysłowu: __ DROP TABLE JEŚLI ISTNIEJE schema_migrations; UTWÓRZ TABELĘ, JEŚLI NIE ISTNIEJE schema_migrations( versionvarchar (255) NOT NULL); INSERT INTO schema_migrations VALUES ( 20100714032840); INSERT INTO schema_migrations VALUES ('20100714033251'); __
David,
cóż ... nie pokazuje się powyżej, ale cudzysłowy pojawiają się wewnątrz WARTOŚCI ([TUTAJ] 20100714032840 [/ TUTAJ))
David
1
AUTOINCREMENT w Mysql to AUTO_INCREMENT. Skrypt tego nie uwzględnia.
giuseppe
To nie działa w przypadku medialnej bazy danych wiki. Wiele błędów: Blobvartyp danych, znaczniki wsteczne w instrukcji CREATE ...
Frank Hintsch
1
nie działa Może nie wszystkie warunki są brane pod uwagę ...
Himanshu Bansal
10

Prawdopodobnie najszybszym najłatwiejszym sposobem jest użycie polecenia sqlite .dump, w tym przypadku utwórz zrzut przykładowej bazy danych.

sqlite3 sample.db .dump > dump.sql

Następnie możesz (teoretycznie) zaimportować to do bazy danych mysql, w tym przypadku testowej bazy danych na serwerze bazy danych 127.0.0.1, używając root użytkownika.

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

Mówię teoretycznie, ponieważ istnieje kilka różnic między gramatykami.

W sqlite rozpoczynają się transakcje

BEGIN TRANSACTION;
...
COMMIT;

MySQL używa tylko

BEGIN;
...
COMMIT;

Istnieją inne podobne problemy (wracają do mnie varchary i podwójne cudzysłowy), ale nic nie można znaleźć i zastąpić.

Być może powinieneś zapytać, dlaczego przeprowadzasz migrację, jeśli problemem jest wydajność / rozmiar bazy danych, być może przyjrzyj się ponownemu uruchomieniu schematu, jeśli system przechodzi na bardziej wydajny produkt, może to być idealny czas na planowanie przyszłości twoich danych.

Richard Gourlay
źródło
2
ale najtrudniejszym zadaniem jest różnica między gramatykami między tygodniami
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
źródło
7

Właśnie przeszedłem ten proces i w tym pytaniu jest dużo bardzo dobrej pomocy i informacji, ale odkryłem, że musiałem zebrać różne elementy (plus niektóre z innych pytań / odpowiedzi), aby uzyskać działające rozwiązanie aby pomyślnie migrować.

Jednak nawet po połączeniu istniejących odpowiedzi stwierdziłem, że skrypt Pythona nie działał dla mnie w pełni, ponieważ nie działał tam, gdzie wystąpiło wiele wystąpień boolowskich w INSERT. Zobacz tutaj, dlaczego tak było.

Pomyślałem więc, że opublikuję tutaj scaloną odpowiedź. Uznanie należy się oczywiście tym, którzy przyczynili się gdzie indziej. Ale chciałem coś oddać i zaoszczędzić innym czas.

Skrypt opublikuję poniżej. Ale po pierwsze, oto instrukcje dotyczące konwersji ...

Uruchomiłem skrypt na OS X 10.7.5 Lion. Python działał od razu po wyjęciu z pudełka.

Aby wygenerować plik wejściowy MySQL z istniejącej bazy danych SQLite3, uruchom skrypt na własnych plikach w następujący sposób:

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

Następnie skopiowałem wynikowy plik dumped_sql.sql do systemu Linux z systemem Ubuntu 10.04.4 LTS, w którym miała znajdować się moja baza danych MySQL.

Innym problemem, który miałem podczas importowania pliku MySQL, było to, że niektóre znaki UTF-8 w standardzie Unicode (konkretnie pojedyncze cudzysłowy) nie były importowane poprawnie, więc musiałem dodać przełącznik do polecenia, aby określić UTF-8.

Wynikowa komenda wprowadzania danych do nowej, pustej bazy danych MySQL jest następująca:

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

Niech gotuje i to powinno być to! Nie zapomnij przeanalizować swoich danych przed i po.

Tak, jak wymagał PO, jest to szybkie i łatwe, gdy wiesz jak! :-)

Nawiasem mówiąc, jedną rzeczą, o której nie byłem pewien, zanim przejrzałem tę migrację, było to, czy wartości pola create_at i updated_at zostaną zachowane - dobra wiadomość dla mnie jest taka, że ​​mogę migrować istniejące dane produkcyjne.

Powodzenia!

AKTUALIZACJA

Po dokonaniu tej zmiany zauważyłem problem, którego wcześniej nie zauważyłem. W mojej aplikacji Rails moje pola tekstowe są zdefiniowane jako „ciąg”, co prowadzi do schematu bazy danych. Proces opisany tutaj powoduje, że są one zdefiniowane jako VARCHAR (255) w bazie danych MySQL. Powoduje to ograniczenie liczby znaków do 255 znaków - a wszystko poza tym zostało po cichu obcięte podczas importu. Wydaje mi się, że do obsługi tekstu o długości większej niż 255 schemat MySQL musiałby używać „TEKST” zamiast VARCHAR (255). Zdefiniowany tutaj proces nie obejmuje tej konwersji.


Oto scalony i poprawiony skrypt Pythona, który zadziałał dla moich danych:

#!/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
źródło
1
Dzięki. Skrypt jak obecnie napisany powyżej zawiera błąd składniowy; „else:” w linii 41 nie jest na odpowiednim poziomie wcięcia. Nie jest dla mnie jasne, czy linie powyżej powinny być wcięte, czy też dzieje się coś innego. Chcesz zaktualizować?
Dan Tenenbaum,
5

Niedawno musiałem przeprowadzić migrację z MySQL do JavaDB dla projektu, nad którym pracuje nasz zespół. Znalazłem bibliotekę Java napisaną przez Apache'a o nazwie DdlUtils, która ułatwiła to. Zapewnia interfejs API, który pozwala wykonać następujące czynności:

  1. Odkryj schemat bazy danych i wyeksportuj go jako plik XML.
  2. Zmodyfikuj bazę danych na podstawie tego schematu.
  3. Importuj rekordy z jednego DB do drugiego, zakładając, że mają ten sam schemat.

Narzędzia, z którymi wylądowaliśmy, nie były całkowicie zautomatyzowane, ale działały całkiem dobrze. Nawet jeśli twoja aplikacja nie jest w Javie, nie powinno być zbyt trudne uruchomienie kilku małych narzędzi do jednorazowej migracji. Myślę, że udało mi się przeprowadzić migrację z mniej niż 150 liniami kodu.

Outlaw Programmer
źródło
4

Nie ma potrzeby żadnego skryptu, polecenia itp.

musisz tylko wyeksportować bazę danych sqlite jako .csvplik, a następnie zaimportować ją do Mysql przy użyciu phpmyadmin.

Użyłem go i zadziałało niesamowicie ...

NavidIvanian
źródło
W połączeniu z tym jest to jedyna odpowiedź, która zadziałała dla mnie.
cdauth
3

Oparty na rozwiązaniu Jimsa: Szybki i łatwy sposób migracji SQLite3 na 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  

To działa dla mnie. Używam sed tylko do rzucenia pierwszej linii, która nie jest podobna do mysql, ale równie dobrze możesz zmodyfikować skrypt dump.py, aby wyrzucić tę linię.

alekwisnia
źródło
1
Miałem pewne problemy z kodowaniem UTF-8 z importowanymi danymi, ale wydaje się, że dodanie --default-character-set = utf8 do polecenia importu. Zaczerpnięte z tego pytania / odpowiedzi: stackoverflow.com/questions/346092/…
Snips,
Ok, dodałem to - czy jest w porządku?
alekwisnia
Właśnie tam używam dodatkowego przełącznika, tak.
Snips,
3

Uzyskaj zrzut SQL

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

Zaimportuj zrzut do MySQL

W przypadku małego importu:

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

lub

mysql -u root -p somedb < myTemporarySQLFile.sql

Spowoduje to monit o podanie hasła. Uwaga: jeśli chcesz wprowadzić hasło bezpośrednio, musisz to zrobić BEZ spacji, bezpośrednio po -p:

mysql -u root -pYOURPASS somedb < myTemporarySQLFile.sql

W przypadku większych zrzutów:

mysqlimport lub inne narzędzia do importowania, takie jak BigDump .

BigDump daje pasek postępu:

wprowadź opis zdjęcia tutaj

Martin Thoma
źródło
12
Nie działa to z powodu niewielkich różnic składniowych i flag w sqlite vs mysql. Nadal musisz go ręcznie przekonwertować.
dlite922,
1

Ha ... Szkoda, że ​​nie znalazłem tego pierwszego! Moja odpowiedź była na ten post ... skrypt do konwersji pliku zrzutu mysql sql do formatu, który można zaimportować do pliku sqlite3 db

Połączenie tych dwóch byłoby dokładnie tym, czego potrzebowałem:


Kiedy baza danych sqlite3 będzie używana z ruby, możesz chcieć to zmienić:

tinyint([0-9]*) 

do:

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

niestety, ta tylko połowa działa, ponieważ chociaż wstawiasz 1 i 0 do pola oznaczonego logicznie, sqlite3 przechowuje je jako 1 i 0, więc musisz przejść przez i zrobić coś takiego:

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)

ale warto było sprawdzić plik sql, aby znaleźć wszystkie wartości logiczne.

daicoden
źródło
1

Napisałem ten prosty skrypt w Python3. Może być używany jako dołączona klasa lub samodzielny skrypt wywoływany przez powłokę terminala. Domyślnie importuje wszystkie liczby całkowite jako int(11)i ciągi jako varchar(300), ale wszystko to można dostosować odpowiednio w argumentach konstruktora lub skryptu.

UWAGA: Wymaga MySQL Connector / Python 2.0.4 lub nowszego

Oto link do źródła na GitHub, jeśli poniższy kod jest trudny do odczytania: 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
źródło
0

Ten skrypt jest w porządku, z wyjątkiem tego przypadku, który oczywiście spotkałem:

INSERT INTO „requestcomparison_stopword” VALUES (149, „f”);
WSTAWIĆ WARTOŚCI „requestcomparison_stopword” (420, „t”);

Skrypt powinien dać następujące dane wyjściowe:

INSERT INTO requestcomparison_stopword VALUES (149, „f”);
WSTAW DO WARTOŚCI requestcomparison_stopword WARTOŚCI (420, „t”);

Ale zamiast tego daje ten wynik:

WSTAW DO WARTOŚCI requestcomparison_stopword WARTOŚCI (1490;
WSTAW DO WARTOŚCI requestcomparison_stopword WARTOŚCI (4201;

z niektórymi dziwnymi nie-asciowskimi postaciami w ostatnich 0 i 1.

Nie pojawiło się to już, gdy skomentowałem następujące wiersze kodu (43-46), ale pojawiły się inne problemy:


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

To tylko szczególny przypadek, gdy chcemy dodać wartość „f” lub „t”, ale nie bardzo mi się podoba wyrażenia regularne, chciałem tylko zauważyć, że ktoś może to poprawić.

W każdym razie wielkie dzięki za ten przydatny skrypt !!!


źródło
0

To proste rozwiązanie zadziałało dla mnie:

<?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
źródło
-5
echo ".dump" | sqlite3 /tmp/db.sqlite > db.sql

uważaj na instrukcje CREATE

mgribov
źródło