[mysql] SQLite3를 MySQL로 마이그레이션하는 가장 쉬운 방법은 무엇입니까? [닫은]

누구나 SQLite3 데이터베이스를 MySQL로 마이그레이션하는 빠르고 쉬운 방법을 알고 있습니까?



답변

다음은 변환기 목록입니다 (2011 년 이후 업데이트되지 않음).


잘 작동하지만 거의 언급되지 않는 대체 방법은 다음과 같습니다. 특정 데이터베이스 차이점을 추상화하는 ORM 클래스를 사용하십시오. 예를 들어 PHP ( RedBean ), Python (Django의 ORM 레이어, Storm , SqlAlchemy ), Ruby on Rails ( ActiveRecord ), Cocoa ( CoreData )에서 얻을 수 있습니다

즉, 당신은 이것을 할 수 있습니다 :

  1. ORM 클래스를 사용하여 소스 데이터베이스에서 데이터를로드하십시오.
  2. 데이터를 메모리에 저장하거나 디스크에 직렬화하십시오.
  3. ORM 클래스를 사용하여 대상 데이터베이스에 데이터를 저장하십시오.

답변

모두가 몇 가지 grep과 perl 표현으로 시작하는 것처럼 보이며 특정 데이터 세트에 적합한 것을 얻지 만 데이터를 올바르게 가져 왔는지 여부는 알 수 없습니다. 아무도이 둘 사이를 변환 할 수있는 견고한 라이브러리를 만들지 않은 것에 놀랐습니다.

다음은 두 파일 형식 사이에서 알고있는 SQL 구문의 모든 차이점 목록입니다.

  • 거래 시작
  • 범하다
  • sqlite_sequence
  • 고유 인덱스 생성

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의 용도 10(당신이 같은 문자열이있을 때 이것에 대한 간단한 정규식이 실패 할 수 있습니다 : t ‘당신의 내부에’당신은 \ 돈 내가 않는 ‘를 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_]*)\"(.*)/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;
    }
}


답변

다음은 Shalmanese의 답변으로 작성된 Python 스크립트와 Alex Pertelli의 번역을 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,


답변

아마도 가장 빠른 방법은 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 및 큰 따옴표가 떠 올랐습니다)가 있지만 찾아서 바꿀 수있는 것은 없습니다.

성능 / 데이터베이스 크기가 문제인 경우 스키마를 다시 살펴 보는 것이 좋습니다. 시스템이보다 강력한 제품으로 이동하는 경우 데이터의 미래를 계획하기에 이상적인 시간 일 수 있습니다.


답변

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')


답변

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


답변

방금이 과정을 거쳤으며이 Q / A에는 매우 유용한 도움말과 정보가 많이 있지만 작업 솔루션을 얻기 위해 다양한 요소 (다른 Q / A의 일부)를 함께 가져와야한다는 것을 알았습니다. 성공적으로 마이그레이션합니다.

그러나 기존 답변을 결합한 후에도 INSERT에서 여러 부울이 발생하는 곳에서는 작동하지 않으므로 Python 스크립트가 제대로 작동하지 않는 것으로 나타났습니다. 보다그 이유를 여기에서 .

그래서 여기에 병합 된 답변을 게시 할 것이라고 생각했습니다. 신용은 물론 다른 곳에 기여한 사람들에게 간다. 그러나 나는 무언가를 돌려주고 싶었고 다른 사람들이 따르는 시간을 절약하고 싶었습니다.

아래에 스크립트를 게시하겠습니다. 그러나 먼저 전환에 대한 지침은 다음과 같습니다.

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 파일을 MySQL 데이터베이스가 상주하는 Ubuntu 10.04.4 LTS를 실행하는 Linux 상자에 복사했습니다.

MySQL 파일을 가져올 때 발생하는 또 다른 문제는 일부 유니 코드 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 애플리케이션에서 텍스트 필드는 ‘string’으로 정의되며 데이터베이스 스키마로 전달됩니다. 여기에 요약 된 프로세스는 MySQL 데이터베이스에서 VARCHAR (255)로 정의됩니다. 이렇게하면 필드 크기가 255 자로 제한되며 가져 오는 동안이 범위를 넘어서는 것은 자동으로 잘립니다. 255보다 긴 텍스트 길이를 지원하려면 MySQL 스키마가 VARCHAR (255) 대신 ‘TEXT’를 사용해야합니다. 여기에 정의 된 프로세스에는이 변환이 포함되지 않습니다.


내 데이터에서 작동하는 병합 및 수정 된 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,