Кто-нибудь знает быстрый простой способ переноса базы данных SQLite3 в MySQL?
Вот список конвертеров (не обновляется с 2011 года):
<ч/" > Альтернативный метод, который будет работать хорошо, но редко упоминается: используйте класс ORM, который абстрагирует специфические различия в базе данных для вас. например вы получаете их в PHP (RedBean), Python (слой Django ORM, Storm, SqlAlchemy), Ruby on Rails (ActiveRecord), Cocoa (CoreData)
то есть. вы можете сделать это:
Кажется, что все начинаются с нескольких выражений grep и perl, и вы просто получаете что-то, что работает для вашего конкретного набора данных, но вы не знаете, правильно ли оно импортировало данные или нет. Я серьезно удивлен, что никто не создал сплошную библиотеку, которая может конвертировать между ними.
Вот список ВСЕХ различий в синтаксисе SQL, который я знаю о двух форматах файлов: Строки, начинающиеся с:
не используются в MySQL
Вот очень простой взломанный perl script, который работает для моего набора данных и проверяет еще многие из этих условий, что другие Perl-скрипты, которые я нашел в Интернете. Nu guarentees, что он будет работать для ваших данных, но не стесняйтесь изменять и отправлять сообщения здесь.
#! /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 = $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_]*)\"(.*)/){
$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;
}
}
Вот питон script, построенный из ответа Шалмана и некоторая помощь от Alex martelli в Перевод Perl на Python
Я делаю это сообщество wiki, поэтому, пожалуйста, не стесняйтесь редактировать и рефакторировать, пока он не нарушает функциональность (к счастью, мы можем просто откинуться назад) - Это довольно уродливо, но работает
используйте так (при условии, что script называется dump_for_mysql.py
:
sqlite3 sample.db .dump | python dump_for_mysql.py > dump.sql
Что вы можете импортировать в mysql
note - вам нужно добавить ограничения внешнего ключа вручную, так как sqlite фактически не поддерживает их
здесь находится 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,
schema_migrations
( version
varchar (255) NOT NULL); INSERT INTO schema_migrations VALUES ( 20100714032840
); INSERT INTO schema_migrations VALUES ('20100714033251'); __
Это беспорядочно, потому что файлы дампа являются специфичными для поставщика баз данных.
Если вы используете Rails, для этого существует отличный плагин. Читайте: http://blog.heroku.com/archives/2007/11/23/yamldb_for_databaseindependent_data_dumps/
В настоящее время поддерживается fork: https://github.com/ludicast/yaml_db
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
Удивленный никто не упоминал об этом, но на самом деле для этого явно используется инструмент. Это в perl, SQL: Переводчик: http://sqlfairy.sourceforge.net/
Преобразует любую форму табличных данных (различные форматы SQL, электронную таблицу Excel) и даже делает диаграммы вашей схемы SQL.
moose@pc08$ sqlite3 mySqliteDatabase.db .dump > myTemporarySQLFile.sql
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 дает вам индикатор выполнения:
Я только что прошел этот процесс, и в этом Q/A очень много полезной справки и информации, но я обнаружил, что мне нужно объединить различные элементы (плюс некоторые из других Q/As), чтобы получить рабочего решения для успешной миграции.
Однако даже после объединения существующих ответов я обнаружил, что Python script не полностью работает для меня, так как он не работает, когда в INSERT было несколько логических вхождений. См. здесь, почему это было так.
Итак, я думал, что опубликую свой объединенный ответ здесь. Конечно, кредит принадлежит тем, кто внес свой вклад в другие места. Но я хотел отдать что-то и спасти другие последующие моменты.
Я отправлю script ниже. Но, во-первых, здесь инструкции для преобразования...
Я запустил script на OS X 10.7.5 Lion. Python работал из коробки.
Чтобы сгенерировать входной файл MySQL из существующей базы данных SQLite3, запустите script в своих собственных файлах следующим образом:
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 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 ''); 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 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,
Вероятно, самый простой способ - использовать команду 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 и двойные кавычки spring), но ничего найти и заменить не удалось.
Возможно, вы должны спросить, почему вы мигрируете, если проблема с производительностью/базой данных является проблемой, возможно, стоит взглянуть на повторное включение схемы, если система переходит к более мощному продукту, это может быть идеальным временем для планирования будущего вашего данных.
Мне недавно пришлось перейти от MySQL к JavaDB для проекта, над которым работает наша команда. Я нашел Java-библиотеку, написанную Apache под названием DdlUtils, которая сделала это довольно легко. Он предоставляет API, который позволяет выполнять следующие действия:
Инструменты, которые мы закончили, не были полностью автоматизированы, но они работали очень хорошо. Даже если ваше приложение не находится на Java, не должно быть слишком сложно взломать несколько небольших инструментов для одноразовой миграции. Я думаю, что смог преодолеть миграцию с менее чем 150 строк кода.
Если вы используете 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')
python script работал после нескольких изменений следующим образом:
# 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('"', "'")
MySQL Workbench (лицензия GPL) очень легко переносится из SQLite с помощью Мастер миграции базы данных. Устанавливается на Windows, Ubuntu, RHEL, Fedora и OS X.
Основываясь на решении Jims: Быстрый простой способ миграции 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 script, чтобы выбросить эту строку.
Я использую загрузчик данных для переноса практически любых данных, это помогает мне преобразовать MSSQL в MYSQL, доступ к MS для MSSQL, mysql, csv loader, foxpro и MSSQL для доступа к MS, MYSQl, CSV, foxpro и т.д. На мой взгляд, это лучший инструмент переноса данных
Скачать бесплатно: http://www.dbload.com
Нет необходимости в каких-либо script, командах и т.д.
вам нужно экспортировать только базу данных sqlite в качестве файла .csv
, а затем импортировать ее в Mysql с помощью phpmyadmin.
Я использовал его, и он работал потрясающе...
это программное обеспечение из коробки - работает для меня. попробуйте и сообщите другим.
https://dbconvert.com/sqlite/mysql/
Кроме того:
Мне пришлось сделать одно небольшое изменение: каким-то образом auto_increment одного поля (поле, найденное из сообщения об ошибке) не было включено. Итак, в phpmyadmin я проверяю свойство A_I этого поля, и он работает полностью. Надеюсь, что это поможет.
Dunn.
fallino правильно идентифицировал местоположение ошибки в script. У меня есть решение. Проблема заключается в следующих строках:
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')
Образец замены (2-й параметр) в вызовах re.sub является "обычной" строкой, поэтому вместо \1, расширяющегося до первого соответствия регулярному выражению, он расширяется до литерала 0x01. Аналогично, \2 расширяется до 0x02. Например, строка, содержащая: , 'Т', 'F',
будет заменено на: < 0x01 > 10 < 0x02 >
(Первая замена изменяется, 't', до < 0x1 > 1 < 0x2 > Вторая замена изменяется < 0x02 > 'f', до < 0x1 > 0 < 0x1 > )
Исправление состоит в том, чтобы либо изменить заменяющие строки, добавив префикс 'r', либо экранируя \1 и\2 в существующей строке. Поскольку легкая манипуляция строками регулярных выражений - это то, что для сырых строк, здесь исправить, используя те:
line = re.sub(r"([^'])'t'(.)", r"\1THIS_IS_TRUE\2", line)
line = line.replace('THIS_IS_TRUE', '1')
line = re.sub(r"([^'])'f'(.)", r"\1THIS_IS_FALSE\2", line)
line = line.replace('THIS_IS_FALSE', '0')
Ха... Мне жаль, что я не нашел это первым! Мой ответ был на этот пост... script для преобразования sql файла mysql dump в формат, который можно импортировать в sqlite3 db
Объединение двух будет именно тем, что мне нужно:
Когда база данных sqlite3 будет использоваться с ruby, вы можете изменить:
tinyint([0-9]*)
в
sed 's/ tinyint(1*) / boolean/g ' |
sed 's/ tinyint([0|2-9]*) / integer /g' |
Увы, эта только половина работает, потому что, хотя вы вставляете 1 и 0 в поле, отмеченное булевым, 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 файл выглядел так, чтобы найти все логические значения.
Я взял Python script из https://stackoverflow.com/questions/18671/quick-easy-way-to-migrate-sqlite3-to-mysql (см. выше) и исправил его, чтобы справиться с нашими собственными схемами sqlite. Было несколько проблем, с которыми нужно иметь дело.
Здесь вы можете найти его здесь: https://bitbucket.org/mjogltd/sqlite3mysql
Также доступно то же самое, что и изображение Docker, здесь: https://hub.docker.com/r/mjog/sqlite3mysql/ - он полностью доступен даже под рабочим столом Windows.
Это простое решение для меня:
<?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 ) );
}
}
Я написал этот простой script в Python3. Он может использоваться в качестве включенного класса или автономного script, вызываемого через терминальную оболочку. По умолчанию он импортирует все целые числа как int(11)
и строки как varchar(300)
, но все они могут быть скорректированы соответственно в конструкторе или script.
ПРИМЕЧАНИЕ. Для этого требуется MySQL Connector/Python 2.0.4 или выше
Здесь ссылка на источник на GitHub, если вы находите код ниже, трудно прочитать: https://gist.github.com/techouse/4deb94eee58a02d104c6
#!/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()
Это script нормально, за исключением этого случая, что, конечно, я встречал:
INSERT INTO "requestcomparison_stopword" VALUES(149,'f'); INSERT INTO "requestcomparison_stopword" VALUES(420,'t');
script должен дать этот результат:
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'(.)", "\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')
Это просто особый случай, когда мы хотим добавить значение "f" или "t", но мне не очень нравится регулярные выражения, я просто хотел, чтобы этот случай был исправлен кем-то.
В любом случае, спасибо за этот удобный script!!!
Я тщательно проверил все ответы в этом сообщении, а также ответы в другом связанном сообщении Перевод Perl на Python. Тем не менее никто не смог полностью решить мою проблему.
Мой сценарий - мне нужно перенести базу данных Trac из sqlite в MySQL, а база данных содержит много вики-контента на основе технологий. Поэтому внутри значений 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
. Один для обновленной схемы БД, а другой для обновленных данных БД.
Можно было бы сделать дальнейшие ручные изменения в файле схемы DB с помощью любого текстового редактора, не беспокоясь об изменении содержимого.
Надеюсь, что это может помочь другим в будущем.
echo ".dump" | sqlite3 /tmp/db.sqlite > db.sql
следить за утверждениями CREATE