将 SQLite3 迁移到 MySQL 的快速简单方法?
题
有人知道将 SQLite3 数据库迁移到 MySQL 的快速简单方法吗?
解决方案
以下是转换器列表(自 2011 年以来未更新):
另一种效果很好但很少被提及的替代方法是:使用 ORM 类为您抽象出特定的数据库差异。例如你可以用 PHP 得到这些(红豆), Python (Django 的 ORM 层, 风暴, SqlAlchemy), Ruby on Rails (活动记录), 可可 (核心数据)
IE。你可以这样做:
- 使用 ORM 类从源数据库加载数据。
- 将数据存储在内存中或序列化到磁盘。
- 使用 ORM 类将数据存储到目标数据库中。
其他提示
每个人似乎都是从一些 grep 和 perl 表达式开始,你会得到一些适合你的特定数据集的东西,但你不知道它是否正确导入了数据。我非常惊讶没有人建立一个可以在两者之间进行转换的可靠库。
以下是我所知道的两种文件格式之间 SQL 语法的所有差异的列表:这些行开头为:
- 开始交易
- 犯罪
- sqlite_序列
- 创建唯一索引
没有在 MySQL 中使用
- SQLlite 使用
CREATE TABLE/INSERT INTO "table_name"
和 MySQL 使用CREATE TABLE/INSERT INTO table_name
- MySQL 在模式定义中不使用引号
- MySQL 对内的字符串使用单引号
INSERT INTO
条款 - SQLlite 和 MySQL 有不同的内部字符串转义方式
INSERT INTO
条款 - SQLlite 使用
't'
和'f'
对于布尔值,MySQL 使用1
和0
(当您有如下字符串时,简单的正则表达式可能会失败:'我愿意,你不'在你的内心INSERT INTO
) - SQLLite 使用
AUTOINCREMENT
,MySQL使用AUTO_INCREMENT
这是一个非常基本的破解 perl 脚本,适用于 我的 数据集并检查我在网络上找到的其他 Perl 脚本的更多这些条件。Nu 保证它适用于您的数据,但请随意修改并发布回此处。
#! /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;
}
}
这是一个 python 脚本,是根据 Shalmanese 的答案和 Alex martelli 的一些帮助构建的 将 Perl 翻译为 Python
我正在将其制作为社区维基,所以请随意编辑和重构,只要它不破坏功能(幸运的是我们可以回滚)-它非常丑陋但有效
像这样使用(假设脚本被称为 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'(.)", "\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,
这很混乱,因为转储文件是特定于数据库供应商的。
如果您使用 Rails,那么有一个很棒的插件可以实现这一点。读: http://blog.heroku.com/archives/2007/11/23/yamldb_for_databaseindependent_data_dumps/
更新
目前维护的fork: https://github.com/ludicast/yaml_db
MySQL Workbench(GPL 许可证)可以通过以下方式轻松从 SQLite 迁移: 数据库迁移向导. 。安装于 Windows、Ubuntu、RHEL、Fedora 和 OS X.
令人惊讶的是,现在还没人提到这一点,但实际上有一个专门用于此目的的工具。它是用 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
我刚刚经历了这个过程,这个问答中有很多非常好的帮助和信息,但我发现我必须将各种元素(加上其他问答中的一些元素)放在一起才能得到一个可行的解决方案才能成功迁移。
然而,即使在组合现有答案之后,我发现 Python 脚本并不完全适合我,因为它在 INSERT 中出现多个布尔值的情况下不起作用。看 这里 为什么会这样。
所以,我想我应该在这里发布我的合并答案。当然,功劳归于那些在其他地方做出贡献的人。但我想回馈一些东西,并节省其他人随后的时间。
我将在下面发布脚本。但首先,这是转换说明......
我在 OS X 10.7.5 Lion 上运行了该脚本。Python 开箱即用。
要从现有 SQLite3 数据库生成 MySQL 输入文件,请在您自己的文件上运行脚本,如下所示:
Snips$ sqlite3 original_database.sqlite3 .dump | python ~/scripts/dump_for_mysql.py > dumped_data.sql
然后,我将生成的 dumped_sql.sql 文件复制到运行 Ubuntu 10.04.4 LTS 的 Linux 机器上,我的 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 应用程序中,我的文本字段被定义为“字符串”,这会传递到数据库模式。此处概述的过程导致这些在 MySQL 数据库中被定义为 VARCHAR(255)。这对这些字段大小设置了 255 个字符的限制 - 超出此限制的任何内容都会在导入过程中被悄悄截断。我相信,为了支持大于 255 的文本长度,MySQL 模式需要使用“TEXT”而不是 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,
可能最快速、最简单的方法是使用 sqlite .dump 命令,在本例中创建示例数据库的转储。
sqlite3 sample.db .dump > dump.sql
然后(理论上)您可以使用 root 用户将其导入 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 和双引号),但没有找到和替换无法解决的问题。
也许您应该问为什么要迁移,如果性能/数据库大小是问题,也许可以考虑重新组织架构,如果系统正在转向更强大的产品,这可能是规划数据未来的理想时机。
如果您使用 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 迁移到 JavaDB。我找到了一个 由 Apache 编写的名为 DdlUtils 的 Java 库 这让这变得很容易。它提供了一个 API,可让您执行以下操作:
- 发现数据库的架构并将其导出为 XML 文件。
- 根据此模式修改数据库。
- 将记录从一个数据库导入到另一个数据库,假设它们具有相同的架构。
我们最终使用的工具并不是完全自动化的,但它们运行得很好。即使您的应用程序不是使用 Java 编写的,创建一些小工具来进行一次性迁移也应该不会太困难。我认为我能够用不到 150 行代码来完成迁移。
获取 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 给你一个进度条:
经过一些修改后,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('"', "'")
...
不需要任何脚本、命令等...
您只需将 sqlite 数据库导出为 .csv
文件,然后使用 phpmyadmin 将其导入到 Mysql 中。
我用过它,效果惊人......
我使用数据加载器来迁移几乎所有数据,它帮助我将 MSSQL 转换为 MYSQL,将 MS Access 转换为 MSSQL、mysql、csv 加载器、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 脚本来扔掉这一行。
哈...我希望我先找到这个!我的回复是对这篇文章的... 将 mysql dump sql 文件转换为可以导入 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 文件以查找所有布尔值很有帮助。
Fallino 正确识别了脚本中错误的位置。我有解决办法。问题是以下几行:
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')
re.sub 调用中的替换模式(第二个参数)是一个“常规”字符串,因此它不是扩展为第一个正则表达式匹配,而是扩展为文字 0x01,而不是 \1。同样,\2 扩展为 0x02。例如,一行包含:,'t','f'将被替换为:10
(第一个替代更改为't',为<0x1> 1 <0x2>第二个替代更改<0x02>'f',to <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')
这个软件开箱即用 - 对我有用。尝试一下并让其他人知道。
https://dbconvert.com/sqlite/mysql/
此外:
我必须做一个小改变:不知何故,一个字段(从错误消息中找到的字段)的 auto_increment 未启用。因此,在 phpmyadmin 中,我检查了该字段的属性 A_I,它完全可以工作。希望能帮助到你。
邓恩.
我用 Python3 编写了这个简单的脚本。它可以用作包含的类或通过终端 shell 调用的独立脚本。默认情况下,它将所有整数导入为 int(11)
和字符串作为 varchar(300)
, ,但所有这些都可以分别在构造函数或脚本参数中进行调整。
笔记: 它需要 MySQL Connector/Python 2.0.4 或更高版本
如果您发现下面的代码难以阅读,可以点击 GitHub 上的源代码链接: https://github.com/techhouse/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()
这个脚本没问题,除了我遇到过的这种情况:
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;
最后 0 和 1 周围有一些奇怪的非 ASCII 字符。
当我评论以下代码行(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”但我对正则表达式不太满意时,我只是想发现这种情况由某人纠正。
不管怎样,非常感谢这个方便的脚本!
这个简单的解决方案对我有用:
<?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/ - 即使在 Windows 桌面下也完全可用。
我仔细检查了这篇文章中的所有答案,以及另一篇相关文章中的答案 将 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 语句