سؤال

هل يعرف أحد طريقة سهلة وسريعة لترحيل قاعدة بيانات SQLite3 إلى MySQL؟

هل كانت مفيدة؟

المحلول

فيما يلي قائمة بالمحولات (لم يتم تحديثها منذ 2011):


الطريقة البديلة التي قد تعمل بشكل جيد ولكن نادرًا ما يتم ذكرها هي:استخدم فئة ORM التي تلخص اختلافات قاعدة البيانات المحددة لك.على سبيل المثاليمكنك الحصول على هذه في PHP (فاصوليا حمراء)، بايثون (طبقة ORM الخاصة بـ Django، عاصفة, SqlAlchemy)، روبي على القضبان (سجل نشط)، كاكاو (البيانات الأساسية)

أي.يمكنك القيام بذلك:

  1. تحميل البيانات من قاعدة البيانات المصدر باستخدام فئة ORM.
  2. تخزين البيانات في الذاكرة أو إجراء تسلسل لها على القرص.
  3. تخزين البيانات في قاعدة البيانات الوجهة باستخدام فئة ORM.

نصائح أخرى

يبدو أن الجميع يبدأون ببعض تعبيرات greps و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 1 و 0 (يمكن أن يفشل التعبير العادي البسيط لهذا عندما يكون لديك سلسلة مثل:"أنا أفعل، أنت لا" داخل الخاص بك INSERT INTO)
  • يستخدم SQLLite AUTOINCREMENT, يستخدم MySQL AUTO_INCREMENT

فيما يلي نص Perl الأساسي الذي تم اختراقه والذي يعمل من أجله لي مجموعة البيانات والتحقق من العديد من هذه الشروط التي وجدتها في نصوص بيرل الأخرى على الويب.لا تضمن أن هذا سيعمل مع بياناتك ولكن لا تتردد في تعديلها ونشرها مرة أخرى هنا.

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

إليك نص برمجي بيثون، مبني على إجابة شلمانيز وبعض المساعدة من أليكس مارتيلي في ترجمة بيرل إلى بايثون

أنا أقوم بإنشاء مجتمع ويكي، لذا لا تتردد في التعديل وإعادة البناء طالما أن ذلك لا يعطل الوظيفة (لحسن الحظ يمكننا التراجع) - إنه قبيح جدًا ولكنه يعمل

استخدم مثل ذلك (على افتراض أن البرنامج النصي يسمى 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.

من المستغرب أن لا أحد ذكر هذا حتى الآن، ولكن هناك في الواقع أداة واضحة لهذا الغرض.إنه في بيرل، 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

لقد مررت بهذه العملية للتو، وهناك الكثير من المساعدة والمعلومات الجيدة جدًا في هذه الأسئلة والأجوبة، لكنني وجدت أنه يتعين علي جمع عناصر مختلفة معًا (بالإضافة إلى بعض العناصر من أسئلة وأجوبة أخرى) للحصول على حل عملي من أجل الهجرة بنجاح.

ومع ذلك، حتى بعد دمج الإجابات الموجودة، وجدت أن نص بايثون لم يعمل بشكل كامل بالنسبة لي لأنه لم يعمل عندما كانت هناك عدة أحداث منطقية في INSERT.يرى هنا لماذا كان هذا هو الحال.

لذا، اعتقدت أنني سأقوم بنشر إجابتي المدمجة هنا.يذهب الفضل إلى أولئك الذين ساهموا في أماكن أخرى، بطبيعة الحال.لكنني أردت أن أقدم شيئًا في المقابل، وأن أوفّر على الآخرين الوقت الذي يلي ذلك.

سأقوم بنشر البرنامج النصي أدناه.لكن أولاً، إليك تعليمات التحويل...

لقد قمت بتشغيل البرنامج النصي على OS X 10.7.5 Lion.عملت بايثون خارج الصندوق.

لإنشاء ملف إدخال MySQL من قاعدة بيانات SQLite3 الموجودة لديك، قم بتشغيل البرنامج النصي على ملفاتك الخاصة كما يلي:

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

قمت بعد ذلك بنسخ ملف dumped_sql.sql الناتج إلى صندوق Linux يعمل بنظام Ubuntu 10.04.4 LTS حيث كانت قاعدة بيانات MySQL موجودة.

هناك مشكلة أخرى واجهتني عند استيراد ملف 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، يكون الأمر سريعًا وسهلاً، عندما تعرف كيف!:-)

جانبًا، هناك شيء واحد لم أكن متأكدًا منه قبل النظر في عملية الترحيل هذه، وهو ما إذا كان سيتم الاحتفاظ بقيمتي الحقول create_at وupdate_at - والخبر السار بالنسبة لي هو أنهما كذلك، حتى أتمكن من ترحيل بيانات الإنتاج الحالية الخاصة بي.

حظ سعيد!

تحديث

منذ إجراء هذا التبديل، لاحظت مشكلة لم ألاحظها من قبل.في تطبيق Rails الخاص بي، يتم تعريف الحقول النصية الخاصة بي على أنها "سلسلة"، وينتقل هذا إلى مخطط قاعدة البيانات.تؤدي العملية الموضحة هنا إلى تعريفها على أنها VARCHAR(255) في قاعدة بيانات MySQL.يؤدي هذا إلى وضع حد يبلغ 255 حرفًا لأحجام الحقول هذه - وأي شيء يتجاوز هذا يتم اقتطاعه بصمت أثناء الاستيراد.لدعم طول النص الأكبر من 255، سيحتاج مخطط MySQL إلى استخدام "TEXT" بدلاً من VARCHAR(255)، على ما أعتقد.لا تتضمن العملية المحددة هنا هذا التحويل.


إليك نص 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

يمكنك بعد ذلك (نظريًا) استيراد هذا إلى قاعدة بيانات 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')

لقد اضطررت مؤخرًا إلى الانتقال من MySQL إلى JavaDB لمشروع يعمل عليه فريقنا.لقد وجدت ال مكتبة Java مكتوبة بواسطة Apache تسمى DdlUtils مما جعل هذا الأمر سهلاً جدًا.يوفر واجهة برمجة التطبيقات (API) التي تتيح لك القيام بما يلي:

  1. اكتشف مخطط قاعدة البيانات وقم بتصديره كملف XML.
  2. قم بتعديل قاعدة البيانات بناءً على هذا المخطط.
  3. قم باستيراد السجلات من قاعدة بيانات إلى أخرى، على افتراض أنها تحتوي على نفس المخطط.

الأدوات التي انتهى بنا الأمر إليها لم تكن آلية بالكامل، لكنها عملت بشكل جيد.حتى لو لم يكن تطبيقك بلغة Java، فلن يكون من الصعب جدًا استخدام بعض الأدوات الصغيرة لإجراء عملية ترحيل لمرة واحدة.أعتقد أنني تمكنت من تنفيذ عملية الترحيل الخاصة بنا بأقل من 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.

يمنحك BigDump شريط التقدم:

enter image description here

لقد عمل سكربت بايثون بعد بعض التعديلات كما يلي:

# 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 الملف ثم قم باستيراده في Mysql باستخدام phpmyadmin.

انا استخدمته وكان نتيجته رائعة

أستخدم أداة تحميل البيانات لترحيل أي بيانات تقريبًا، فهي تساعدني في تحويل MSSQL إلى MYSQL، ووصول MS إلى MSSQL، وmysql، ومحمل csv، وfoxpro، وMSSQL إلى وصول MS، وMYSQl، وCSV، وfoxpro وما إلى ذلك.من وجهة نظري، هذه أفضل أداة لترحيل البيانات

حمل مجانا : http://www.dbload.com

بناءً على حل جيمز:طريقة سهلة وسريعة لترحيل 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 dump SQL إلى تنسيق يمكن استيراده إلى sqlite3 db

الجمع بين الاثنين سيكون بالضبط ما أحتاجه:


عندما سيتم استخدام قاعدة بيانات sqlite3 مع روبي، قد ترغب في تغيير:

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 هو سلسلة "عادية"، لذا بدلاً من توسيع \1 إلى أول تطابق regexp، فإنه يتوسع إلى 0x01 حرفيًا.وبالمثل، \2 يتوسع إلى 0x02.على سبيل المثال، سطر يحتوي على:، "T" ، "F" ، سيتم استبداله بـ:10
(تغييرات الإحلال الأول ، 't' ، إلى <0x1> 1 <0x2> يتغير الاستبدال الثاني <0x02> 'f' ، إلى <0x1> 0 <0x1>)

الحل هو إما تغيير السلاسل البديلة عن طريق إضافة البادئة 'r'، أو عن طريق إلغاء \1 و\2 في السلسلة الموجودة.نظرًا لأن المعالجة السهلة لسلاسل regexp هي الغرض من السلاسل الأولية، فإليك الحل باستخدام تلك:

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/

فضلاً عن ذلك:

كان عليّ إجراء تغيير بسيط:بطريقة ما، لم يتم تمكين الزيادة التلقائية لحقل واحد (حقل تم العثور عليه من خلال رسالة خطأ).لذلك في phpmyadmin قمت بفحص الخاصية A_I لهذا الحقل وهي تعمل بشكل كامل.نأمل أن يساعد.

دان.

لقد كتبت هذا البرنامج النصي البسيط في Python3.يمكن استخدامه كفئة مضمنة أو برنامج نصي مستقل يتم استدعاؤه عبر غلاف طرفي.افتراضيًا، يقوم باستيراد كافة الأعداد الصحيحة كـ int(11)والسلاسل كما varchar(300), ، ولكن كل ذلك يمكن تعديله في وسيطات المُنشئ أو البرنامج النصي على التوالي.

ملحوظة: يتطلب MySQL Connector/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()

عادةً ما أستخدم تصدير/استيراد الجداول ميزة إنتيليج داتا جريب.

step 1 step 2 step 3

يمكنك رؤية التقدم في الزاوية اليمنى السفلى.

[enter image description here]

هذا البرنامج النصي على ما يرام باستثناء هذه الحالة التي التقيت بها بالطبع:

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;

مع بعض الأحرف الغريبة غير ascii حول آخر 0 و 1.

لم يظهر هذا بعد الآن عندما علقت على الأسطر التالية من الكود (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 ) );
    }
}

لقد أخذت نص بايثون من https://stackoverflow.com/a/32243979/746459 (أعلاه) وقمنا بإصلاحه للتعامل مع مخططات sqlite الخاصة بنا.كانت هناك بعض القضايا للتعامل معها.

قد تجده في التحكم بالمصادر هنا: https://bitbucket.org/mjogltd/sqlite3mysql

يتوفر أيضًا نفس الشيء المغلف كصورة Docker، هنا: https://hub.docker.com/r/mjog/sqlite3mysql/ - إنه قابل للاستخدام بالكامل حتى على سطح مكتب Windows.

لقد قمت بفحص جميع الإجابات في هذا المنشور بعناية، بالإضافة إلى الإجابات في منشور آخر ذي صلة ترجمة بيرل إلى بايثون.ومع ذلك، لم يتمكن أي منها من حل مشكلتي بشكل كامل.

السيناريو الخاص بي هو أنني بحاجة إلى ترحيل قاعدة بيانات Trac من sqlite إلى MySQL، وتحتوي قاعدة البيانات على الكثير من محتوى wiki المعتمد على التكنولوجيا.لذلك داخل INSERT INTO القيم، يمكن أن يكون هناك عبارات SQL مثل CREATE TABLE و AUTOINCREMENT.لكن الاستبدال سطرًا بسطر قد يكون به بدائل خاطئة هناك.

في النهاية كتبت أداتي الخاصة لهذا الغرض:

https://github.com/motherapp/sqlite_sql_parser

الاستخدام بسيط نسبيًا:

python parse_sqlite_sql.py export.sql

سيتم إنشاء ملفين: export.sql.schema.sql و export.sql.data.sql.أحدهما لمخطط قاعدة البيانات المحدث، والآخر لبيانات قاعدة البيانات المحدثة.

يمكن إجراء المزيد من التعديلات اليدوية على ملف مخطط قاعدة البيانات باستخدام أي محرر نصوص، دون القلق بشأن تغيير المحتوى.

نأمل أن يساعد الآخرين في المستقبل.

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

احترس من إنشاء البيانات

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top