Cách nhanh chóng dễ dàng để di chuyển SQLite3 sang MySQL? [đóng cửa]


224

Bất cứ ai cũng biết một cách dễ dàng nhanh chóng để di chuyển cơ sở dữ liệu SQLite3 sang MySQL?

Câu trả lời:


62

Dưới đây là danh sách các bộ chuyển đổi (không được cập nhật từ năm 2011):


Một phương pháp thay thế sẽ hoạt động tốt nhưng hiếm khi được đề cập là: sử dụng lớp ORM trừu tượng hóa các khác biệt cơ sở dữ liệu cụ thể cho bạn. ví dụ: bạn nhận được những thứ này trong PHP ( RedBean ), Python (lớp ORM của Django, Storm , SqlAlchemy ), Ruby on Rails ( ActiveRecord ), Ca cao ( CoreData )

tức là bạn có thể làm điều này:

  1. Tải dữ liệu từ cơ sở dữ liệu nguồn bằng lớp ORM.
  2. Lưu trữ dữ liệu trong bộ nhớ hoặc tuần tự hóa vào đĩa.
  3. Lưu trữ dữ liệu vào cơ sở dữ liệu đích bằng cách sử dụng lớp ORM.

107

Mọi người dường như bắt đầu với một vài biểu thức greps và perl và bạn sắp xếp một cái gì đó phù hợp với tập dữ liệu cụ thể của bạn nhưng bạn không biết liệu nó có nhập dữ liệu chính xác hay không. Tôi thực sự ngạc nhiên khi không ai xây dựng một thư viện vững chắc có thể chuyển đổi giữa hai.

Dưới đây là danh sách TẤT CẢ sự khác biệt trong cú pháp SQL mà tôi biết về giữa hai định dạng tệp: Các dòng bắt đầu bằng:

  • BEGIN GIAO DỊCH
  • CAM KẾT
  • sqlite_ resultence
  • TẠO INDEX ĐỘC ĐÁO

không được sử dụng trong MySQL

  • Sử dụng SQLlite CREATE TABLE/INSERT INTO "table_name"và sử dụng MySQLCREATE TABLE/INSERT INTO table_name
  • MySQL không sử dụng dấu ngoặc kép trong định nghĩa lược đồ
  • MySQL sử dụng dấu ngoặc đơn cho các chuỗi bên trong INSERT INTOmệnh đề
  • SQLlite và MySQL có các cách thoát chuỗi khác nhau trong INSERT INTOcác mệnh đề
  • SQLlite sử dụng 't''f'cho booleans, MySQL sử dụng 10(một regex đơn giản cho việc này có thể thất bại khi bạn có một chuỗi như: 'Tôi làm, bạn không \' t 'bên trong của bạn INSERT INTO)
  • Sử dụng SQLLite AUTOINCREMENT, sử dụng MySQLAUTO_INCREMENT

Dưới đây là một tập lệnh perl đã bị hack rất cơ bản, hoạt động cho tập dữ liệu của tôi và kiểm tra nhiều điều kiện khác mà các tập lệnh perl khác tôi tìm thấy trên web. Nu guarentees rằng nó sẽ hoạt động cho dữ liệu của bạn nhưng hãy thoải mái sửa đổi và đăng lại ở đây.

#! /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;
    }
}

8
Alex martelli đã làm rất tốt khi viết lại điều này với tư cách là con trăn tại stackoverflow.com/questions/1067060/perl-to-python
Jiaaro

Tôi đã thêm tập lệnh python hoàn chỉnh (riêng tập lệnh perl không hoạt động với tôi ... cần một số xử lý bổ sung để xử lý Khóa ngoài và Chỉ mục)
Jiaaro

Tôi viết lại câu trả lời này cho câu hỏi khác stackoverflow.com/questions/1067060/_/1070463#1070463
Brad Gilbert

2
CAM KẾTTẠO INDEX UNIITE là các lệnh MySQL hợp lệ, vui lòng sửa nó.
niutech

5
Tôi hiểu rằng tập lệnh của bạn là "nhanh và bẩn", nhưng cũng rất hữu ích, vì vậy đây là một vài bổ sung / sửa lỗi: * sau khi && ($line !~ /CREATE UNIQUE INDEX/)thêm && ($line !~ /PRAGMA foreign_keys=OFF/) * biểu thức khớp với tên bảng bỏ lỡ các chữ số, điều đó thay vì $line =~ /INSERT INTO \"([a-z_]*)\"(.*)/phải có $line =~ /INSERT INTO \"([a-z_1-9]*)\"(.*)/ Hy vọng điều này sẽ giúp tương lai độc giả
Michał Leon

50

Đây là một kịch bản python, được xây dựng từ câu trả lời của Shalmanese và một số trợ giúp từ Alex martelli tại Dịch thuật Perl sang Python

Tôi đang biến nó thành wiki cộng đồng, vì vậy xin vui lòng chỉnh sửa và tái cấu trúc miễn là nó không phá vỡ chức năng (rất may chúng tôi chỉ có thể quay lại) - Nó khá xấu nhưng hoạt động tốt

sử dụng như vậy (giả sử tập lệnh được gọi là dump_for_mysql.py:

sqlite3 sample.db .dump | python dump_for_mysql.py > dump.sql

Mà sau đó bạn có thể nhập vào mysql

lưu ý - bạn cần thêm các ràng buộc khóa ngoại theo cách thủ công vì sqlite không thực sự hỗ trợ chúng

đây là kịch bản:

#!/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,

2
Xin chào Jim, trên tập dữ liệu của tôi, mỗi câu lệnh INSERT đầu tiên được gói bởi một backquote thay vì một trích dẫn duy nhất: __ DROP TABLE IF EXISTS lược đồ_migations; TẠO BẢNG NẾU KHÔNG EXISTS schema_migrations( versionvarchar (255) KHÔNG NULL); XÁC NHẬN VÀO lược đồ_migations VALUES ( 20100714032840); XÁC NHẬN VÀO lược đồ_migations GIÁ TRỊ ('20100714033251'); __
David

tốt ... nó không hiển thị ở trên, nhưng các backquote xuất hiện bên trong GIÁ TRỊ ([TẠI ĐÂY] 20100714032840 [/ TẠI ĐÂY])
David

1
AUTOINCREMENT trong Mysql là AUTO_INCREMENT. Kịch bản không tính đến điều đó.
giuseppe

Điều này không làm việc cho một cơ sở dữ liệu wiki phương tiện truyền thông. Nhiều lỗi: Blobvarkiểu dữ liệu, dấu tích ngược trong câu lệnh CREATE ...
Frank Hintsch

1
không hoạt động. Có thể không phải tất cả các điều kiện đều được xem xét ...
Himanshu Bansal

10

Có lẽ cách nhanh nhất dễ nhất là sử dụng lệnh sqlite .dump, trong trường hợp này tạo ra một bãi chứa cơ sở dữ liệu mẫu.

sqlite3 sample.db .dump > dump.sql

Sau đó, bạn có thể (về lý thuyết) nhập dữ liệu này vào cơ sở dữ liệu mysql, trong trường hợp này là cơ sở dữ liệu thử nghiệm trên máy chủ cơ sở dữ liệu 127.0.0.1, sử dụng root người dùng.

mysql -p -u root -h 127.0.0.1 test < dump.sql

Tôi nói trong lý thuyết vì có một vài khác biệt giữa các ngữ pháp.

Trong giao dịch sqlite bắt đầu

BEGIN TRANSACTION;
...
COMMIT;

MySQL chỉ sử dụng

BEGIN;
...
COMMIT;

Có những vấn đề tương tự khác (varchars và double trích dẫn trở lại trong tâm trí) nhưng không có gì tìm và thay thế không thể khắc phục.

Có lẽ bạn nên hỏi tại sao bạn di chuyển, nếu kích thước hiệu suất / cơ sở dữ liệu là vấn đề có thể nhìn vào việc tái hiện lại lược đồ, nếu hệ thống chuyển sang một sản phẩm mạnh hơn thì đây có thể là thời điểm lý tưởng để lập kế hoạch cho tương lai của dữ liệu của bạn.


2
nhưng nhiệm vụ khó khăn nhất là sự khác biệt giữa các ngữ pháp
francois


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


7

Tôi vừa trải qua quá trình này, và có rất nhiều trợ giúp và thông tin rất tốt trong Q / A này, nhưng tôi thấy tôi phải tập hợp nhiều yếu tố khác nhau (cộng với một số từ Q / As khác) để có được giải pháp hoạt động để di chuyển thành công.

Tuy nhiên, ngay cả sau khi kết hợp các câu trả lời hiện có, tôi thấy rằng tập lệnh Python không hoạt động hoàn toàn đối với tôi vì nó không hoạt động khi có nhiều lần xuất hiện boolean trong một INSERT. Xem ở đây tại sao đó là trường hợp.

Vì vậy, tôi nghĩ rằng tôi sẽ đăng câu trả lời hợp nhất của mình ở đây. Tín dụng cho những người đã đóng góp ở nơi khác, tất nhiên. Nhưng tôi muốn trả lại một cái gì đó và tiết kiệm thời gian cho những người khác.

Tôi sẽ đăng kịch bản dưới đây. Nhưng trước tiên, đây là hướng dẫn chuyển đổi ...

Tôi đã chạy tập lệnh trên OS X 10.7.5 Lion. Python làm việc ra khỏi hộp.

Để tạo tệp đầu vào MySQL từ cơ sở dữ liệu SQLite3 hiện tại của bạn, hãy chạy tập lệnh trên các tệp của riêng bạn như sau,

Snips$ sqlite3 original_database.sqlite3 .dump | python ~/scripts/dump_for_mysql.py > dumped_data.sql

Sau đó, tôi đã sao chép tệp dumped_sql.sql sang hộp Linux chạy Ubuntu 10.04.4 LTS nơi cơ sở dữ liệu MySQL của tôi cư trú.

Một vấn đề khác tôi gặp phải khi nhập tệp MySQL là một số ký tự unicode UTF-8 (cụ thể là dấu ngoặc đơn) không được nhập chính xác, vì vậy tôi phải thêm một công tắc vào lệnh để chỉ định UTF-8.

Lệnh kết quả để nhập dữ liệu vào cơ sở dữ liệu MySQL trống rỗng mới như sau:

Snips$ mysql -p -u root -h 127.0.0.1 test_import --default-character-set=utf8 < dumped_data.sql

Hãy để nó nấu ăn, và đó nên là nó! Đừng quên xem xét kỹ lưỡng dữ liệu của bạn, trước và sau.

Vì vậy, như OP yêu cầu, thật nhanh chóng và dễ dàng, khi bạn biết cách! :-)

Bên cạnh đó, một điều tôi không chắc chắn trước khi tôi xem xét việc di chuyển này, là liệu các giá trị trường created_at và update_at có được giữ nguyên hay không - tin tốt cho tôi là chúng, vì vậy tôi có thể di chuyển dữ liệu sản xuất hiện có của mình.

Chúc may mắn!

CẬP NHẬT

Kể từ khi thực hiện chuyển đổi này, tôi đã nhận thấy một vấn đề mà trước đây tôi chưa nhận thấy. Trong ứng dụng Rails của tôi, các trường văn bản của tôi được định nghĩa là 'chuỗi' và điều này mang đến lược đồ cơ sở dữ liệu. Quá trình được phác thảo ở đây dẫn đến kết quả được xác định là VARCHAR (255) trong cơ sở dữ liệu MySQL. Điều này đặt giới hạn 255 ký tự cho các kích thước trường này - và bất cứ điều gì ngoài phạm vi này đều bị cắt ngắn trong quá trình nhập. Để hỗ trợ độ dài văn bản lớn hơn 255, lược đồ MySQL sẽ cần sử dụng 'TEXT' thay vì VARCHAR (255), tôi tin. Quá trình được xác định ở đây không bao gồm chuyển đổi này.


Đây là tập lệnh Python được hợp nhất và sửa đổi, hoạt động cho dữ liệu của tôi:

#!/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,

1
Cảm ơn. Kịch bản như được viết ở trên có lỗi cú pháp trong đó; "other:" trên dòng 41 không ở mức thụt lề thích hợp. Nó không rõ ràng cho tôi liệu các dòng trên nó nên được thụt lề hoặc nếu có điều gì khác đang xảy ra. Muốn cập nhật?
Dan Tenenbaum

5

Gần đây tôi đã phải di chuyển từ MySQL sang JavaDB cho một dự án mà nhóm chúng tôi đang thực hiện. Tôi đã tìm thấy một thư viện Java được viết bởi Apache có tên là DdlUtils , điều này làm cho việc này khá dễ dàng. Nó cung cấp một API cho phép bạn làm như sau:

  1. Khám phá lược đồ của cơ sở dữ liệu và xuất nó dưới dạng tệp XML.
  2. Sửa đổi một DB dựa trên lược đồ này.
  3. Nhập bản ghi từ DB này sang DB khác, giả sử chúng có cùng lược đồ.

Các công cụ mà chúng tôi đã kết thúc với không hoàn toàn tự động, nhưng chúng hoạt động khá tốt. Ngay cả khi ứng dụng của bạn không có trong Java, thì cũng không quá khó khăn để sử dụng một vài công cụ nhỏ để thực hiện di chuyển một lần. Tôi nghĩ rằng tôi đã có thể thực hiện việc di chuyển của chúng tôi với ít hơn 150 dòng mã.


4

Không cần bất kỳ kịch bản, lệnh, v.v ...

bạn chỉ phải xuất cơ sở dữ liệu sqlite của mình dưới dạng .csvtệp và sau đó nhập nó vào Mysql bằng phpmyadmin.

Tôi đã sử dụng nó và nó hoạt động tuyệt vời ...


Kết hợp với điều này , đây là câu trả lời duy nhất làm việc cho tôi.
cdauth

3

Dựa trên giải pháp của Jims: Cách nhanh chóng dễ dàng để di chuyển SQLite3 sang 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  

Điều này làm việc cho tôi. Tôi sử dụng sed chỉ để ném dòng đầu tiên, không giống như mysql, nhưng bạn cũng có thể sửa đổi tập lệnh dump.txt để ném dòng này đi.


1
Tôi đã gặp một số vấn đề mã hóa UTF-8 với dữ liệu đã nhập, nhưng việc thêm --default-character-set = utf8 vào lệnh nhập dường như đã khắc phục điều đó. Lấy từ Q / A này: stackoverflow.com/questions
4326092

Ok, tôi đã thêm cái này - có ổn không?
alekwisnia

Đó là nơi tôi đang sử dụng công tắc phụ, vâng.
Snips

3

Nhận một kết xuất SQL

moose@pc08$ sqlite3 mySqliteDatabase.db .dump > myTemporarySQLFile.sql

Nhập kết xuất vào MySQL

Đối với hàng nhập khẩu nhỏ:

moose@pc08$ mysql -u <username> -p
Enter password:
....
mysql> use somedb;
Database changed
mysql> source myTemporarySQLFile.sql;

hoặc là

mysql -u root -p somedb < myTemporarySQLFile.sql

Điều này sẽ nhắc bạn nhập mật khẩu. Xin lưu ý: Nếu bạn muốn nhập mật khẩu trực tiếp, bạn phải thực hiện mà KHÔNG cần dung lượng, ngay sau -p:

mysql -u root -pYOURPASS somedb < myTemporarySQLFile.sql

Đối với bãi lớn hơn:

mysqlimport hoặc các công cụ nhập khẩu khác như BigDump .

BigDump cung cấp cho bạn một thanh tiến trình:

nhập mô tả hình ảnh ở đây


12
Điều này không hoạt động vì sự khác biệt cú pháp nhỏ và cờ trong sqlite so với mysql. Bạn vẫn cần phải tự chuyển đổi nó.
dlite922

1

Ha ... tôi ước tôi đã tìm thấy cái này đầu tiên! Phản hồi của tôi là bài viết này ... script để chuyển đổi tập tin mysql dump sql thành định dạng có thể được nhập vào sqlite3 db

Kết hợp cả hai sẽ chính xác những gì tôi cần:


Khi cơ sở dữ liệu sqlite3 sẽ được sử dụng với ruby, bạn có thể muốn thay đổi:

tinyint([0-9]*) 

đến:

sed 's/ tinyint(1*) / boolean/g ' |
sed 's/ tinyint([0|2-9]*) / integer /g' |

than ôi, điều này chỉ hoạt động được một nửa vì mặc dù bạn đang chèn 1 và 0 vào một trường được đánh dấu boolean, sqlite3 lưu trữ chúng là 1 và 0 nên bạn phải trải qua và làm một cái gì đó như:

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)

nhưng thật hữu ích khi có tệp sql để xem để tìm tất cả các booleans.


1

Tôi đã viết kịch bản đơn giản này trong Python3. Nó có thể được sử dụng như một lớp bao gồm hoặc tập lệnh độc lập được gọi thông qua trình bao đầu cuối. Theo mặc định, nó nhập tất cả các số nguyên dưới dạng int(11)và chuỗi nhưvarchar(300) , nhưng tất cả những gì có thể được điều chỉnh trong các đối số của hàm tạo hoặc tập lệnh tương ứng.

GHI CHÚ: Nó yêu cầu Trình kết nối MySQL / Python 2.0.4 trở lên

Đây là một liên kết đến nguồn trên GitHub nếu bạn thấy mã dưới đây khó đọc: https://github.com/techouse/sqlite3-to-mysql

#!/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()

0

Kịch bản này ổn, ngoại trừ trường hợp này, tất nhiên, tôi đã gặp:

XÁC NHẬN VÀO "requestcomparison_stopword" GIÁ TRỊ (149, 'f');
XÁC NHẬN VÀO "requestcomparison_stopword" GIÁ TRỊ (420, 't');

Kịch bản sẽ cung cấp đầu ra này:

XÁC NHẬN VÀO requestcomparison_stopword GIÁ TRỊ (149, 'f');
XÁC NHẬN VÀO requestcomparison_stopword GIÁ TRỊ (420, 't');

Nhưng thay vào đó là đầu ra:

XÁC NHẬN VÀO requestcomparison_stopword GIÁ TRỊ (1490;
XÁC NHẬN VÀO requestcomparison_stopword GIÁ TRỊ (4201;

với một số ký tự không phải ascii lạ xung quanh 0 và 1 cuối cùng.

Điều này đã không xuất hiện nữa khi tôi nhận xét các dòng mã sau (43-46) nhưng các vấn đề khác xuất hiện:


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

Đây chỉ là một trường hợp đặc biệt, khi chúng tôi muốn thêm một giá trị là 'f' hoặc 't' nhưng tôi không thực sự thoải mái với các biểu thức thông thường, tôi chỉ muốn phát hiện trường hợp này để được ai đó sửa.

Dù sao cũng cảm ơn rất nhiều cho kịch bản tiện dụng đó !!!


0

Giải pháp đơn giản này đã làm việc cho tôi:

<?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 ) );
    }
}

-5
echo ".dump" | sqlite3 /tmp/db.sqlite > db.sql

coi chừng báo cáo TẠO

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.