有人知道一个快速简单的方法来迁移SQLite3数据库到MySQL吗?


当前回答

下面是一个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,

其他回答

每个人似乎都是从一些grep和perl表达式开始的,你可能会得到一些适用于特定数据集的东西,但你不知道它是否正确地导入了数据。我真的很惊讶没有人建立一个可靠的库,可以在两者之间转换。

这里列出了我所知道的两种文件格式之间SQL语法的所有差异: 以以下几行开头:

开始事务 提交 sqlite_sequence 创建唯一索引

在MySQL中不使用

SQLite uses CREATE TABLE/INSERT INTO "table_name" and MySQL uses CREATE TABLE/INSERT INTO table_name MySQL doesn't use quotes inside the schema definition MySQL uses single quotes for strings inside the INSERT INTO clauses SQLite and MySQL have different ways of escaping strings inside INSERT INTO clauses SQLite uses 't' and 'f' for booleans, MySQL uses 1 and 0 (a simple regex for this can fail when you have a string like: 'I do, you don't' inside your INSERT INTO) SQLLite uses AUTOINCREMENT, MySQL uses 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_]*)\"(.*)/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;
    }
}
echo ".dump" | sqlite3 /tmp/db.sqlite > db.sql

注意CREATE语句

最简单的方法可能是使用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和双引号会回到脑海中),但没有查找和替换不能解决的问题。

也许你应该问问为什么要迁移,如果性能/数据库大小是问题,也许应该考虑重新规划模式,如果系统正在迁移到更强大的产品,这可能是计划数据未来的理想时机。

获取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;

or

mysql -u root -p somedb < myTemporarySQLFile.sql

这将提示您输入密码。请注意:如果你想直接输入你的密码,你必须不带空格,直接在-p后输入:

mysql -u root -pYOURPASS somedb < myTemporarySQLFile.sql

对于较大的转储:

mysqlimport或其他导入工具,如BigDump。

BigDump给你一个进度条:

基于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脚本来丢弃这一行。