SQLite3 を MySQL に移行する簡単な方法はありますか?
質問
SQLite3 データベースを MySQL に移行する簡単な方法を知っている人はいますか?
解決
コンバータのリストは次のとおりです (2011 年以降更新されていません)。
うまく機能する代替方法は次のとおりですが、ほとんど言及されていません。特定のデータベースの違いを抽象化する ORM クラスを使用します。例えばこれらは PHP で取得します (小豆)、Python (Django の ORM 層、 嵐, SQL錬金術)、Ruby on Rails (アクティブレコード)、 ココア (コアデータ)
つまりこれを行うことができます:
- ORM クラスを使用してソース データベースからデータをロードします。
- データをメモリに保存するか、ディスクにシリアル化します。
- ORM クラスを使用して、データを宛先データベースに保存します。
他のヒント
誰もがいくつかの grep と perl 式から始めるようで、特定のデータセットで機能するものはなんとなく得られますが、データが正しくインポートされているかどうかはわかりません。この 2 つを変換できる堅牢なライブラリを誰も構築していないことに、私は真剣に驚きました。
以下は、私が知っている 2 つのファイル形式間の 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 は1
そして0
(次のような文字列がある場合、これに対する単純な正規表現は失敗する可能性があります:「私はそうします、あなたはそうしません」INSERT INTO
) - SQLLite が使用するもの
AUTOINCREMENT
, 、MySQL が使用するAUTO_INCREMENT
これは非常に基本的なハックアップされた Perl スクリプトです。 私の データセットを作成し、Web 上で見つけた他の 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;
}
}
これは、Shalmanese の回答と Alex martelli の助けから構築された Python スクリプトです。 Perl から Python への翻訳
コミュニティ Wiki として作成しているので、機能を損なわない限り、自由に編集したりリファクタリングしてください (ありがたいことに、ロールバックするだけで済みます) - かなり見苦しいですが、機能します
このように使用します(スクリプトが呼び出されると仮定します) 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,
ダンプ ファイルはデータベース ベンダー固有であるため、面倒です。
Rails を使用している場合は、このための優れたプラグインが存在します。読む: http://blog.heroku.com/archives/2007/11/23/yamldb_for_databaseindependent_data_dumps/
アップデート
現在メンテナンスされているフォーク: https://github.com/ludicast/yaml_db
MySQL Workbench (GPL ライセンス) は、SQLite から非常に簡単に移行できます。 データベース移行ウィザード. 。にインストールします Windows、Ubuntu、RHEL、Fedora、OS X.
今のところ誰もこれについて言及していないことに驚いていますが、実際にはこれを明示的に行うツールがあります。Perl の SQL:Translator です。http://sqlfairy.sourceforge.net/
ほとんどすべての形式の表形式データ (さまざまな SQL 形式、Excel スプレッドシート) の間で変換し、SQL スキーマの図も作成します。
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 でスクリプトを実行しました。Python はそのまま使えました。
既存の 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 ファイルをインポートするときに発生したもう 1 つの問題は、一部の 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 が要求したように、方法を知っていれば、すばやく簡単に実行できます。:-)
余談ですが、この移行を検討する前に確信が持てなかったことの 1 つは、created_at フィールドと updated_at フィールドの値が保持されるかどうかでした。私にとって良いニュースは、これらの値は保持されるため、既存の実稼働データを移行できるということです。
幸運を!
アップデート
この切り替えを行ってから、これまで気付かなかった問題に気づきました。私の Rails アプリケーションでは、テキスト フィールドが「文字列」として定義されており、これがデータベース スキーマに引き継がれます。ここで説明するプロセスにより、これらは 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,
おそらく最も簡単な方法は、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;
他にも同様の問題がありますが(varchar と二重引用符が思い浮かびます)、検索と置換では解決できませんでした。
おそらく、移行する理由を尋ねる必要があります。パフォーマンスやデータベースのサイズが問題である場合は、スキーマの再検討を検討してください。システムがより強力な製品に移行している場合は、データの将来について計画するのに最適な時期である可能性があります。
Python/Django を使用している場合は、非常に簡単です。
settings.pyに2つのデータベースを作成します(ここのように) 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')
最近、私たちのチームが取り組んでいるプロジェクトのために MySQL から JavaDB に移行する必要がありました。見つけました DdlUtils と呼ばれる Apache によって作成された Java ライブラリ これでかなり簡単になりました。これは、次のことを可能にする API を提供します。
- データベースのスキーマを検出し、XML ファイルとしてエクスポートします。
- このスキーマに基づいて DB を変更します。
- 同じスキーマを持っていると仮定して、ある DB から別の DB にレコードをインポートします。
私たちが最終的に使用したツールは完全には自動化されていませんでしたが、かなりうまく機能しました。アプリケーションが Java でなくても、1 回限りの移行を行うための小さなツールをいくつか作成するのはそれほど難しくありません。150 行未満のコードで移行を完了できたと思います。
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;
または
mysql -u root -p somedb < myTemporarySQLFile.sql
これにより、パスワードの入力を求められます。ご注意ください:パスワードを直接入力する場合は、パスワードの直後にスペースを入れずに入力する必要があります。 -p
:
mysql -u root -pYOURPASS somedb < myTemporarySQLFile.sql
大規模なダンプの場合:
mysqlimport またはその他のインポート ツール ビッグダンプ.
BigDump には進行状況バーが表示されます。
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('"', "'")
...
スクリプトやコマンドなどは必要ありません。
sqlite データベースを .csv
ファイルを作成し、phpmyadmin を使用して MySQL にインポートします。
私はそれを使用しました、そしてそれは素晴らしかったです...
私はほぼすべてのデータの移行にデータ ローダーを使用しています。これは、MSSQL から MYSQL、MS アクセスから MSSQL、mysql、csv ローダー、foxpro および MSSQL から MS アクセス、MYSQl、CSV、foxpro などに変換するのに役立ちます。私の考えでは、これは最高のデータ移行ツールです
無料ダウンロード : http://www.dbload.com
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 スクリプトを変更してこの行をスローすることもできます。
はあ…最初にこれを見つけていればよかったです!この投稿に対する私の返答は... mysqlダンプSQLファイルをsqlite3データベースにインポートできる形式に変換するスクリプト
2 つを組み合わせると、まさに私が必要としていたものになります。
sqlite3 データベースを Ruby で使用する場合は、次のように変更する必要があるかもしれません。
tinyint([0-9]*)
に:
sed 's/ tinyint(1*) / boolean/g ' |
sed 's/ tinyint([0|2-9]*) / integer /g' |
残念ながら、これは半分しか機能しません。ブール値とマークされたフィールドに 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 ファイルを確認するのは役に立ちました。
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 呼び出しの置換パターン (2 番目のパラメーター) は「通常の」文字列であるため、\1 は最初の正規表現一致に展開されるのではなく、リテラル 0x01 に展開されます。同様に、\2 は 0x02 に展開されます。たとえば、次のような行が含まれます。t','f'、
と置き換わるだろう:<0x01>10<0x02>
(最初の置換は、't'を<0x1>1<0x2>に変更する
2番目の置換は、<0x02>'f'を<0x1>0<0x1>に変更する)
修正するには、接頭辞「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')
このソフトウェアは箱から出してすぐに使えます - 私にとっては役に立ちます。試して他の人に知らせてください。
https://dbconvert.com/sqlite/mysql/
加えて:
小さな変更を 1 つ加える必要がありました。どういうわけか、1 つのフィールド (エラー メッセージから見つかったフィールド) の auto_increment が有効になっていませんでした。したがって、phpmyadminでこのフィールドのプロパティA_Iをチェックすると、完全に機能します。それが役に立てば幸い。
ダン。
この簡単なスクリプトは Python3 で作成しました。これは、組み込みのクラスとして使用することも、ターミナル シェル経由で呼び出すスタンドアロン スクリプトとして使用することもできます。デフォルトでは、すべての整数を次のようにインポートします。 int(11)
そして文字列として varchar(300)
, ですが、これらはすべてコンストラクターまたはスクリプトの引数でそれぞれ調整できます。
注記: MySQL コネクタ/Python 2.0.4 以降が必要です
以下のコードが読みにくい場合は、GitHub のソースへのリンクを参照してください。 https://github.com/techouse/sqlite3-to-mysql/blob/master/sqlite3mysql.py
#!/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()
もちろん、私が遭遇したこのケースを除いて、このスクリプトは問題ありません。
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」の値を追加したいが、正規表現にあまり慣れていない場合です。このケースを見つけて誰かに修正してもらいたかっただけです。
とにかく、便利なスクリプトをありがとう!!!
このシンプルな解決策が私にとってはうまくいきました。
<?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 ) );
}
}
Pythonスクリプトはから取得しました https://stackoverflow.com/a/32243979/746459 (上記) 独自の sqlite スキーマに対応できるように修正しました。対処すべき問題がいくつかありました。
以下のソース管理で見つけることができます。 https://bitbucket.org/mjogltd/sqlite3mysql
Docker イメージとしてラップされたものと同じものも利用できます。 https://hub.docker.com/r/mjog/sqlite3mysql/ - Windowsデスクトップ下でも十分に使用可能です。
この投稿のすべての回答と、別の関連投稿の回答を注意深く確認しました。 Perl から Python への翻訳. 。しかし、私の問題を完全に解決できるものはありませんでした。
私のシナリオでは、Trac のデータベースを sqlite から MySQL に移行する必要があり、データベースには技術ベースの Wiki コンテンツが多数含まれています。したがって、内部では INSERT INTO
値には、次のような SQL ステートメントが存在する可能性があります。 CREATE TABLE
そして AUTOINCREMENT
. 。ただし、行ごとの置換では、間違った置換が行われる可能性があります。
最終的に、この目的のために独自のツールを作成しました。
https://github.com/motherapp/sqlite_sql_parser
使用方法は比較的簡単です。
python parse_sqlite_sql.py export.sql
次の 2 つのファイルが生成されます。 export.sql.schema.sql
そして export.sql.data.sql
. 。1 つは更新された DB スキーマ用で、もう 1 つは更新された DB データ用です。
コンテンツの変更を心配することなく、任意のテキスト エディタを使用して DB スキーマ ファイルを手動でさらに変更できます。
将来的に他の人にも役立つことを願っています。
echo ".dump" | sqlite3 /tmp/db.sqlite > db.sql
CREATE ステートメントに注意してください