任何人都知道将SQLite3数据库迁移到MySQL的快捷方法吗?
答案 0 :(得分:104)
每个人似乎都从一些greps和perl表达式开始,你可以得到一些适用于你的特定数据集但你不知道它是否正确导入数据的东西。我很惊讶没有人建立一个可以在两者之间转换的可靠库。
这里列出了我在两种文件格式之间所知的SQL语法的所有差异: 行以:
开头
CREATE TABLE/INSERT INTO "table_name"
,MySQL使用CREATE TABLE/INSERT INTO table_name
INSERT INTO
子句INSERT INTO
子句't'
和'f'
来表示布尔值,MySQL使用1
和0
(当你有一个字符串时,一个简单的正则表达式可能会失败:'我做,你不在INSERT INTO
)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;
}
}
答案 1 :(得分:57)
以下是转换器列表(自2011年以来未更新):
<小时/> 另一种可以很好地工作但很少提及的方法是:使用ORM类来为您提取特定的数据库差异。例如你可以用PHP(RedBean),Python(Django的ORM层,Storm,SqlAlchemy),Ruby on Rails(ActiveRecord),Cocoa(CoreData )
即。你可以这样做:
答案 2 :(得分:47)
这是一个python脚本,基于Shalmanese的回答和Alex martelli在Translating Perl to 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,
答案 3 :(得分:22)
它很混乱,因为转储文件是特定于数据库供应商的。
如果你正在使用Rails,那么就有一个很棒的插件。阅读:http://blog.heroku.com/archives/2007/11/23/yamldb_for_databaseindependent_data_dumps/
答案 4 :(得分:9)
MySQL Workbench(GPL许可证)通过Database Migration wizard非常轻松地从SQLite迁移。安装在Windows, Ubuntu, RHEL, Fedora and OS X。
答案 5 :(得分: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
答案 6 :(得分:8)
目前还没有人提到这一点,但实际上有一个明确的工具。 它在perl,SQL:Translator: http://sqlfairy.sourceforge.net/
在大多数任何形式的表格数据(不同的SQL格式,Excel电子表格)之间进行转换,甚至可以制作SQL模式的图表。
答案 7 :(得分:7)
我刚刚完成了这个过程,在这个Q / A中有很多非常好的帮助和信息,但我发现我必须将各种元素(加上其他Q / As中的一些元素)组合在一起才能得到一个工作解决方案,以便成功迁移。
然而,即使在结合现有答案之后,我发现Python脚本并不能完全适用于我,因为它在INSERT中存在多个布尔值时不起作用。见here为什么会这样。
所以,我想我会在这里发布我的合并答案。当然,归功于那些在其他地方做出贡献的人。但我想回馈一些东西,并节省其他时间。
我将发布以下脚本。但首先,这是转换的说明......
我在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,
答案 8 :(得分:6)
可能最简单的方法是使用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和双引号会重新出现),但找不到任何替换都无法修复。
也许您应该问为什么要进行迁移,如果性能/数据库大小问题可能会考虑重新构建模式,如果系统转向更强大的产品,这可能是计划未来的理想时间数据
答案 9 :(得分:6)
如果你使用的是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')
答案 10 :(得分:5)
我最近不得不从MySQL迁移到JavaDB,用于我们团队正在开发的项目。我发现Java library written by Apache called DdlUtils使这很容易。它提供了一个API,允许您执行以下操作:
我们最终得到的工具并非完全自动化,但它们运作良好。即使您的应用程序不是Java,也不应该用一些小工具来进行一次性迁移。我想我能用不到150行代码完成迁移。
答案 11 :(得分:4)
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为您提供了一个进度条:
答案 12 :(得分:3)
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('"', "'")
答案 13 :(得分:3)
不需要任何脚本,命令等......
你必须只将sqlite数据库导出为.csv
文件,然后使用phpmyadmin将其导入Mysql。
我用它并且效果很好......
答案 14 :(得分:2)
基于Jims的解决方案: Quick easy way to migrate SQLite3 to 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脚本来抛弃这一行。
答案 15 :(得分:2)
我使用数据加载器来迁移几乎所有数据,它帮助我将MSSQL转换为MYSQL,MS访问MSSQL,mysql,csv加载器,foxpro和MSSQL到MS访问,MYSQl,CSV,foxpro等。在我看来这个是一个最好的数据迁移工具
答案 16 :(得分:1)
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。同样,\ 2扩展为0x02。例如,一行包含:
, 'T', 'F',
将被替换为:
&LT; 0×01→10&LT; 0×02&GT;
(第一次替换改变't'到&lt; 0x1&gt; 1&lt; 0x2&gt;
第二次替换将&lt; 0x02&gt;'f'更改为&lt; 0x1&gt; 0&lt; 0x1&gt;)
修复方法是通过添加'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')
答案 17 :(得分:1)
将两者结合起来正是我所需要的:
当sqlite3数据库将与ruby一起使用时,您可能想要更改:
tinyint([0-9]*)
为:
sed 's/ tinyint(1*) / boolean/g ' |
sed 's/ tinyint([0|2-9]*) / integer /g' |
唉,这只有一半有效,因为即使你在一个标记为boolean的字段中插入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文件查找以查找所有布尔值是有帮助的。
答案 18 :(得分:1)
这个开箱即用的软件 - 对我有用。尝试一下,让别人知道。
https://dbconvert.com/sqlite/mysql/
另外:
我不得不做一个小改动:不知何故,一个字段的auto_increment(从错误消息中找到的字段)未启用。所以在phpmyadmin我检查这个字段的属性A_I,它完全有效。希望它有所帮助。
唐恩。
答案 19 :(得分:1)
答案 20 :(得分:0)
这个脚本没问题,除了这种情况,当然,我遇到过:
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'的值但我对正则表达式不太满意时,我只是想发现这个案例需要由某人纠正。< / p>
无论如何,非常感谢那个方便的剧本!!!
答案 21 :(得分:0)
这个简单的解决方案对我有用:
<?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 ) );
}
}
答案 22 :(得分:0)
我从https://stackoverflow.com/a/32243979/746459(上面)获取了Python脚本并修复它以应对我们自己的sqlite模式。有几个问题要处理。
您可以在源代码管理中找到它:https://bitbucket.org/mjogltd/sqlite3mysql
同样可用的是与Docker镜像相同的东西,在这里:https://hub.docker.com/r/mjog/sqlite3mysql/ - 即使在Windows桌面下它也完全可用。
答案 23 :(得分:0)
只需去https://www.rebasedata.com,他们有很多转换器,并且完全免费。我尝试将它用于从SQLite到Mysql的方法,并且效果很好。
答案 24 :(得分:-1)
我仔细检查了这篇文章中的所有答案,以及另一篇相关帖子Translating Perl to Python中的答案。然而没有人能完全解决我的问题。
我的方案是我需要将Trac数据库从sqlite迁移到MySQL,并且数据库包含许多基于技术的wiki内容。因此,在INSERT INTO
值中,可能存在CREATE TABLE
和AUTOINCREMENT
等SQL语句。但是逐行替换可能会在那里进行错误的替换。
最终我为此目的编写了自己的工具:
https://github.com/motherapp/sqlite_sql_parser
用法相对简单:
python parse_sqlite_sql.py export.sql
将生成两个文件:export.sql.schema.sql
和export.sql.data.sql
。一个用于更新的DB模式,另一个用于更新的DB数据。
可以使用任何文本编辑器对数据库模式文件进行进一步的手动修改,而无需担心更改内容。
希望将来可以帮助其他人。
答案 25 :(得分:-5)
echo ".dump" | sqlite3 /tmp/db.sqlite > db.sql
注意CREATE语句