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


当前回答

不需要任何脚本、命令等…

你只需将sqlite数据库导出为.csv文件,然后使用phpmyadmin将其导入Mysql。

我用过,效果非常好……

其他回答

如果你使用的是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')

不需要任何脚本、命令等…

你只需将sqlite数据库导出为.csv文件,然后使用phpmyadmin将其导入Mysql。

我用过,效果非常好……

我刚刚经历了这个过程,在这个Q/ a中有很多非常好的帮助和信息,但我发现我必须将各种元素(加上来自其他Q/ a的一些元素)组合在一起,以获得一个有效的解决方案,以便成功迁移。

然而,即使在结合现有的答案后,我发现Python脚本并没有完全为我工作,因为它在INSERT中出现多个布尔值时不起作用。看看为什么会这样。

所以,我想把我合并后的答案贴在这里。当然,功劳归于那些在其他地方做出贡献的人。但我想回馈一些东西,并节省其他人随后的时间。

我将在下面发布脚本。但首先,这里是转换的说明…

我在OS X 10.7.5 Lion上运行该脚本。巨蟒开箱即用。

要从您现有的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字段值是否会被保留——对我来说,好消息是它们会被保留,所以我可以迁移现有的生产数据。

好运!

更新

Since making this switch, I've noticed a problem that I hadn't noticed before. In my Rails application, my text fields are defined as 'string', and this carries through to the database schema. The process outlined here results in these being defined as VARCHAR(255) in the MySQL database. This places a 255 character limit on these field sizes - and anything beyond this was silently truncated during the import. To support text length greater than 255, the MySQL schema would need to use 'TEXT' rather than VARCHAR(255), I believe. The process defined here does not include this conversion.


以下是对我的数据进行合并和修改后的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,

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