Быстрый простой способ переноса SQLite3 в MySQL?

кто-нибудь знает быстрый простой способ переноса базы данных SQLite3 в MySQL?

25 ответов


вот список конвертеров (не обновляется с 2011 года):


Альтернативный метод, который будет работать хорошо, но редко упоминается: используйте класс ORM, который абстрагирует определенные различия в базе данных для вас. например, вы получаете их в PHP (RedBean), Python (слой ORM Django, шторм, SqlAlchemy), Ruby on Rails (ActiveRecord), какао (CoreData)

т. е. вы могли бы сделать это:

  1. загрузка данных из исходной базы данных с помощью класса ORM.
  2. сохранение данных в памяти или сериализовать на диск.
  3. хранить данные в целевой базе данных с помощью класса ORM.

все, кажется, начинается с нескольких выражений greps и perl, и вы вроде как получаете что-то, что работает для вашего конкретного набора данных, но вы понятия не имеете, правильно ли он импортировал данные или нет. Я серьезно удивлен, что никто не построил прочную библиотеку, которая может конвертировать между ними.

вот список всех различий в синтаксисе SQL, о которых я знаю между двумя форматами файлов: Строки, начинающиеся с:

  • начать Сделки
  • фиксация
  • sqlite_sequence
  • СОЗДАТЬ УНИКАЛЬНЫЙ ИНДЕКС

не используются в MySQL

  • SQLlite использует CREATE TABLE / INSERT в "table_name", а MySQL использует CREATE TABLE/INSERT в table_name
  • MySQL не использует кавычки внутри определения схемы
  • MySQL использует одинарные кавычки для строк внутри INSERT INTO clauses
  • SQLlite и MySQL имеют разные способы побега строки внутри INSERT INTO clauses
  • SQLlite использует " t " и " f "для логических значений, MySQL использует 1 и 0 (простое регулярное выражение для этого может потерпеть неудачу, когда у вас есть строка:" я делаю, вы не\'t'внутри вашей вставки в)
  • SQLLite использует автоинкремент, MySQL использует AUTO_INCREMENT

вот очень простой взломанный скрипт perl, который работает для мой dataset и проверяет многие другие из этих условий, которые другие скрипты perl я нашел в интернете. Ню гарантируйте, что он будет работать для ваших данных, но не стесняйтесь изменять и публиковать здесь.

#! /usr/bin/perl

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

        if ($line =~ /CREATE TABLE \"([a-z_]*)\"(.*)/){
            $name = ;
            $sub = ;
            $sub =~ s/\"//g;
            $line = "DROP TABLE IF EXISTS $name;\nCREATE TABLE IF NOT EXISTS $name$sub\n";
        }
        elsif ($line =~ /INSERT INTO \"([a-z_]*)\"(.*)/){
            $line = "INSERT INTO \n";
            $line =~ s/\"/\\"/g;
            $line =~ s/\"/\'/g;
        }else{
            $line =~ s/\'\'/\\'/g;
        }
        $line =~ s/([^\'])\'t\'(.)/THIS_IS_TRUE/g;
        $line =~ s/THIS_IS_TRUE/1/g;
        $line =~ s/([^\'])\'f\'(.)/THIS_IS_FALSE/g;
        $line =~ s/THIS_IS_FALSE/0/g;
        $line =~ s/AUTOINCREMENT/AUTO_INCREMENT/g;
        print $line;
    }
}

вот скрипт python, построенный на ответе Шалманесе и некоторой помощи от Алекса Мартелли в перевод Perl на Python

Я делаю это сообщество wiki, поэтому, пожалуйста, не стесняйтесь редактировать и рефакторинг, пока он не нарушает функциональность (к счастью, мы можем просто откатиться) - это довольно уродливо, но работает

использовать так (предполагая, что скрипт называется dump_for_mysql.py:

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

который вы можете импортировать в в MySQL

Примечание-вам нужно добавить ограничения внешнего ключа вручную, так как sqlite фактически не поддерживает их

вот этот скрипт:

#!/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'(.)", "THIS_IS_TRUE", line)
    line = line.replace('THIS_IS_TRUE', '1')
    line = re.sub(r"([^'])'f'(.)", "THIS_IS_FALSE", 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,

это грязно, потому что файлы дампа зависят от поставщика базы данных.

Если вы используете Rails, для этого существует отличный плагин. Читайте: http://blog.heroku.com/archives/2007/11/23/yamldb_for_databaseindependent_data_dumps/

обновление

в настоящее время поддерживается вилка:https://github.com/ludicast/yaml_db


удивлен, что никто не упомянул об этом сейчас, но на самом деле есть инструмент явно для этого. Это в perl, SQL: переводчик: http://sqlfairy.sourceforge.net/

преобразует между большинством любой формы табличных данных (различные форматы SQL, таблицы Excel), и даже делает диаграммы вашей схемы SQL.


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

Я только что прошел этот процесс, и в этом Q/A есть много очень хорошей помощи и информации, но я обнаружил, что мне пришлось собрать различные элементы (плюс некоторые из других Q/As), чтобы получить рабочее решение для успешной миграции.

однако даже после объединения существующих ответов я обнаружил, что скрипт Python не полностью работает для меня, поскольку он не работает там, где было несколько булевых вхождений во вставке. См.здесь почему случай.

Итак, я думал, что опубликую свой объединенный ответ здесь. Разумеется, заслуга принадлежит тем, кто внес свой вклад в других странах. Но я хотел дать что-то взамен и сэкономить время другим.

я опубликую сценарий ниже. Но, во-первых, вот инструкции для преобразования...

я запустил скрипт на OS X 10.7.5 Lion. Python работал из коробки.

чтобы сгенерировать входной файл MySQL из существующей базы данных SQLite3, запустите скрипт на собственные файлы следующим образом,

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

затем я скопировал полученный dumped_sql.sql-файл в поле Linux под управлением Ubuntu 10.04.4 LTS, где должна была находиться моя база данных MySQL.

еще одна проблема, которую я имел при импорте файла MySQL, заключалась в том, что некоторые символы unicode UTF-8 (особенно одинарные кавычки) не импортировались правильно, поэтому мне пришлось добавить переключатель в команду, чтобы указать UTF-8.

результирующая команда для ввода данных в порку новая пустая база данных MySQL выглядит следующим образом:

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

пусть он готовит, и это должно быть так! Не забудьте тщательно изучить ваши данные до и после.

Итак, как просил OP, это быстро и легко, когда вы знаете, как! :-)

в стороне, одна вещь, в которой я не был уверен, прежде чем я посмотрел на эту миграцию, была ли created_at и updated_at значения полей будут сохранены - хорошая новость для меня в том, что они есть, поэтому я мог бы перенести свою существующую продукцию данные.

удачи!

обновление

С момента создания этого переключателя я заметил проблему, которую я не заметил раньше. В моем приложении Rails мои текстовые поля определяются как "строка", и это переносится в схему базы данных. Процесс, описанный здесь, приводит к тому, что они определяются как VARCHAR(255) в базе данных MySQL. Это помещает ограничение 255 символов на эти размеры полей - и все, что за этим, было молча усечено во время импорт. Чтобы поддерживать длину текста больше 255, схема MySQL должна была бы использовать "текст", а не VARCHAR(255), я считаю. Процесс, определенный здесь, не включает это преобразование.


вот Объединенный и пересмотренный скрипт Python, который работал для моих данных:

#!/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,

недавно мне пришлось перейти с MySQL на JavaDB для проекта, над которым работает наша команда. Я нашел библиотека Java, написанная Apache под названием DdlUtils это сделало это довольно легко. Он предоставляет API, который позволяет вам делать следующее:

  1. откройте схему базы данных и экспортируйте ее в XML-файл.
  2. изменить БД на основе этой схемы.
  3. импорт записей из одной БД в другую, предполагая, что они имеют одинаковую схема.

инструменты, которые мы получили, не были полностью автоматизированы, но они работали довольно хорошо. Даже если ваше приложение не находится на Java, не должно быть слишком сложно создать несколько небольших инструментов для одноразовой миграции. Я думаю, что я смог вытащить нашу миграцию с менее чем 150 строками кода.


вероятно, самый простой способ-использовать sqlite .команда Dump, в этом случае создать дамп базы данных.

sqlite3 sample.db .dump > dump.sql

затем вы можете (теоретически) импортировать это в базу данных mysql, в этом случае тестовую базу данных на сервере базы данных 127.0.0.1, используя пользовательский корень.

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

Я говорю в теории, так как есть несколько различий между грамматиками.

в SQLite транзакции начинаются

BEGIN TRANSACTION;
...
COMMIT;

MySQL использует только

BEGIN;
...
COMMIT;

есть и другие подобные проблемы (varchars и двойные кавычки возвращаются на ум), но ничего найти и заменить не удалось исправить.

возможно, вы должны спросить, почему вы переносите, если производительность / размер базы данных является проблемой, возможно, посмотрите на переосмысление схемы, если система переходит на более мощный продукт, это может быть идеальное время для планирования будущего ваших данных.


получить дамп SQL

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

импорт дампа в MySQL

для маленьких импорта:

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

или

mysql -u root -p somedb < myTemporarySQLFile.sql

это предложит вам ввести пароль. Обратите внимание: если вы хотите ввести свой пароль напрямую, вы должны сделать это без пробела, сразу после -p:

mysql -u root -pYOURPASS somedb < myTemporarySQLFile.sql

для больших свалок:

mysqlimport или другие инструменты импорта, такие как BigDump.

BigDump дает вам прогресс-бар:

enter image description here


Если вы используете Python/Django, это довольно просто:

создать две базы данных в settings.py (как здесь https://docs.djangoproject.com/en/1.11/topics/db/multi-db/)

тогда просто сделайте так:

objlist = ModelObject.objects.using('sqlite').all()

for obj in objlist:
    obj.save(using='mysql')

MySQL Workbench (лицензия GPL) мигрирует из SQLite очень легко через мастер миграции баз данных. Устанавливается на Windows, Ubuntu, RHEL, Fedora и OS X.


скрипт python работал после нескольких модификаций следующим образом:

# Remove "PRAGMA foreign_keys=OFF; from beginning of script
# Double quotes were not removed from INSERT INTO "BaselineInfo" table, check if removed from subsequent tables.  Regex needed A-Z added.
# Removed backticks from CREATE TABLE
# Added replace AUTOINCREMENT with AUTO_INCREMENT
# Removed replacement,
#line = line.replace('"', '`').replace("'", '`')

...

useless_es = [
    'BEGIN TRANSACTION',
    'COMMIT',
    'sqlite_sequence',
    'CREATE UNIQUE INDEX',
    'PRAGMA foreign_keys=OFF',
    ]

...

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('"', "'")

...


Я использую Data loader для миграции почти любых данных, это помогает мне конвертировать MSSQL в MYSQL, MS access в MSSQL, mysql, CSV loader, foxpro и MSSQL в MS access, MYSQl, CSV, foxpro и т. д. На мой взгляд, это лучший инструмент миграции данных

Скачать Бесплатно:http://www.dbload.com


на основе решения Джимса: быстрый простой способ переноса SQLite3 в 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  

это работает для меня. Я использую sed только для броска первой строки, которая не похожа на mysql, но вы также можете изменить dump.py сценарий, чтобы выбросить эту строку.


га... Жаль, что я не нашел это первым! Мой ответ был на этот пост... скрипт для преобразования SQL-файла дампа mysql в формат, который можно импортировать в sqlite3 db

объединение двух было бы именно тем, что мне нужно:


когда база данных sqlite3 будет использоваться с ruby, вы можете изменить:

tinyint([0-9]*) 

в:

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

увы, это работает только наполовину, потому что, хотя вы вставляете 1 и 0 в a поле с пометкой boolean, sqlite3 хранит их как 1 и 0, поэтому вам нужно пройти и сделать что-то вроде:

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)

но это было полезно иметь файл SQL, чтобы найти все логические.


fallino правильно определил местоположение ошибки в скрипте. У меня есть решение. Проблема заключается в следующих строках:

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

шаблон замены (2-й параметр) в re.sub-вызовы - это "обычная" строка, поэтому вместо \1, расширяющегося до первого соответствия регулярному выражению, он расширяется до литерала 0x01. Аналогично, \2 расширяется до 0x02. Например, строка, содержащая: , 't', 'f', будут заменены: 10
(Первые изменения замены , 't', to 1 Очередную изменения 'ф', к 0)

исправление состоит в том, чтобы либо изменить строки замены, добавив префикс 'r', либо экранировав \1 и \2 в существующей строке. Поскольку простая манипуляция строками regexp-это то, для чего предназначены необработанные строки, Вот исправление, использующее те:

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

Это программное обеспечение из коробки-работает для меня. попробуйте и дайте знать другим.

https://dbconvert.com/sqlite/mysql/

кроме того:

Мне пришлось внести одно небольшое изменение: как-то auto_increment одного поля (поля, найденного из сообщения об ошибке) не был включен. Поэтому в phpmyadmin я проверяю свойство A_I этого поля и оно работает полностью. Надеюсь, это поможет.

Данн.


нет необходимости в каком-либо скрипте,команде и т. д...

вам нужно экспортировать базу данных sqlite только как .csv файл, а затем импортировать его в Mysql с помощью phpmyadmin.

я использовал его и он работал удивительно...


этот скрипт в порядке, за исключением этого случая, который, конечно, я встретил:

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

скрипт должен дать этот вывод:

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

но дает вместо этого вывод:

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

С некоторыми странными символами без ascii вокруг последних 0 и 1.

Это больше не появлялось, когда я прокомментировал следующие строки кода (43-46), но появились другие проблемы:


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

Это просто особый случай, когда мы хотим добавить значение " f " или "t", но мне не очень удобно с регулярными выражениями, я просто хотел, чтобы этот случай был исправлен кем-то.

в любом случае большое спасибо за этот удобный скрипт !!!


Это простое решение сработало для меня:

<?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 ) );
    }
}

Я взял скрипт Python из https://stackoverflow.com/a/32243979/746459 (выше) и исправил его, чтобы справиться с нашими собственными схемами sqlite. Нужно было решить несколько вопросов.

вы можете найти его в системе управления версиями здесь:https://bitbucket.org/mjogltd/sqlite3mysql

также доступно то же самое, что и изображение Docker, здесь:https://hub.docker.com/r/mjog/sqlite3mysql/ - оно полно годен к употреблению даже под a Рабочего стола Windows.


Я написал этот простой скрипт в Python3. Его можно использовать как включенный класс или автономный скрипт, вызываемый через оболочку терминала. По умолчанию он импортирует все целые числа как int(11)и строки varchar(300), но все это можно настроить в аргументах конструктора или скрипта соответственно.

Примечание: для этого требуется MySQL Connector / Python 2.0.4 или выше

вот ссылка на источник на github, если вы найдете код трудно читать: https://github.com/techouse/sqlite3-to-mysql/blob/master/sqlite3mysql.py

#!/usr/bin/env python3

__author__ = "Klemen Tušar"
__email__ = "techouse@gmail.com"
__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()

я тщательно проверил все ответы в этом посте, а также ответы в другом соответствующем посте перевод Perl на Python. Но никто не мог полностью решить мою проблему.

мой сценарий-мне нужно перенести базу данных Trac из sqlite в MySQL, и база данных содержит много технического контента wiki. Поэтому внутри INSERT INTO значения, могут быть операторы SQL, такие как CREATE TABLE и AUTOINCREMENT. Но линейная замена может быть неправильной замены есть.

в конце концов я написал свой собственный инструмент для этой цели:

https://github.com/motherapp/sqlite_sql_parser

использование относительно просто:

python parse_sqlite_sql.py export.sql

будут созданы два файла:export.sql.schema.sql и export.sql.data.sql. Один для обновленной схемы БД, а другой для обновленных данных БД.

можно было бы сделать дополнительные изменения вручную в файле схемы БД с помощью любого текстового редактора, без беспокойство об изменении содержания.

надеюсь, что это может помочь другим в будущем.


echo ".dump" | sqlite3 /tmp/db.sqlite > db.sql

следите за create операторы