Сведения о вопросе

None

11:47, 5th August, 2020

Теги

mysql   sqlite   migration    

Быстрый простой способ перенести SQLite3 в MySQL?

Просмотров: 2433   Ответов: 18

Кто-нибудь знает быстрый и простой способ переноса базы данных SQLite3 в MySQL?



  Сведения об ответе

lats

08:53, 25th August, 2020

Каждый, кажется, начинает с нескольких greps и выражений perl, и вы вроде как получаете что-то, что работает для вашего конкретного набора данных, но вы понятия не имеете, правильно ли он импортировал данные или нет. Я серьезно удивлен, что никто не построил надежную библиотеку, которая может конвертировать между ними.

Вот список из ALL различий в синтаксисе SQL, которые я знаю о двух форматах файлов: Строки, начинающиеся с:

  • НАЧАТЬ ТРАНЗАКЦИЮ
  • COMMIT
  • 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 (простой regex для этого может не сработать, если у вас есть строка типа: 'I do, you don\'t' inside your INSERT INTO )
  • SQLLite использует AUTOINCREMENT , MySQL использует AUTO_INCREMENT

Вот очень простой взломанный скрипт 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;
    }
}


  Сведения об ответе

appple

10:40, 7th August, 2020

Вот список конвертеров (не обновлялся с 2011 года):


Альтернативный метод, который будет работать хорошо, но редко упоминается, это: используйте класс ORM, который абстрагирует определенные различия в базе данных для вас. например, вы получаете их в PHP (RedBean ), Python (Django-й слой ORM , шторм, SqlAlchemy), Ruby на Rails ( ActiveRecord ), Cocoa ( CoreData )

то есть вы могли бы сделать это:

  1. Загрузите данные из исходной базы данных с помощью класса ORM.
  2. Храните данные в памяти или сериализуйте на диск.
  3. Храните данные в целевой базе данных, используя класс ORM.


  Сведения об ответе

park

21:06, 1st October, 2020

Вот сценарий python, построенный на основе ответа Шалманезе и некоторой помощи Алекса Мартелли в переводе Perl на Python

Я делаю это сообщество wiki, поэтому, пожалуйста, не стесняйтесь редактировать и рефакторировать, пока это не нарушает функциональность (к счастью, мы можем просто откатить назад) - это довольно уродливо, но работает

используйте like so (предполагая, что скрипт называется 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,


  Сведения об ответе

baggs

08:56, 26th August, 2020

Это грязно, потому что файлы дампа зависят от поставщика базы данных.

Если вы используете Rails, для этого существует отличный плагин. Читать: http://blog.heroku.com/archives/2007/11/23/yamldb_for_databaseindependent_data_dumps/

Обновление

В настоящее время поддерживается fork: https://github.com/ludicast/yaml_db


  Сведения об ответе

dumai

10:22, 28th August, 2020

MySQL Workbench (лицензия GPL) очень легко переносится с SQLite с помощью мастера переноса базы данных . Устанавливается на Windows, Ubuntu, RHEL, Fedora и OS X.


  Сведения об ответе

lats

04:40, 24th August, 2020

Удивительно, что никто до сих пор не упомянул об этом, но на самом деле есть инструмент, явно предназначенный для этого. Это в perl, SQL:Translator году: http://sqlfairy.sourceforge.net/

Преобразует между собой почти любую форму табличных данных (различные форматы SQL, Excel таблицы), и даже делает диаграммы вашей схемы SQL.


  Сведения об ответе

SKY

03:28, 26th August, 2020

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


  Сведения об ответе

davran

08:41, 29th August, 2020

Я только что прошел через этот процесс, и в этом Q/A, есть много очень хорошей помощи и информации, но я обнаружил, что мне пришлось собрать вместе различные элементы (плюс некоторые из других Q/As), чтобы получить рабочее решение для успешной миграции.

Однако даже после объединения существующих ответов я обнаружил, что сценарий Python не полностью работает для меня, так как он не работал там, где было несколько булевых вхождений в INSERT. Смотрите здесь , почему это было так.

Поэтому я решил разместить свой объединенный ответ здесь. Разумеется, заслуга принадлежит тем, кто внес свой вклад в других странах. Но я хотел дать что-то взамен и сэкономить другим время, которое последует за этим.

Я опубликую сценарий ниже. Но во-первых, вот инструкции для преобразования...

Я запустил скрипт на OS X 10.7.5 Lion. Python работал из коробки.

Чтобы создать входной файл 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, заключалась в том, что некоторые символы unicode UTF-8 (особенно одинарные кавычки) импортировались неправильно, поэтому мне пришлось добавить переключатель в команду, чтобы указать UTF-8.

Результирующая команда для ввода данных в новую пустую базу данных spanking 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 - хорошая новость для меня заключается в том, что они есть, поэтому я мог бы перенести свои существующие производственные данные.

Удачи вам!

UPDATE

С тех пор как я сделал этот переход, я заметил проблему, которую раньше не замечал. В моем приложении Rails мои текстовые поля определяются как 'string', и это переносится в схему базы данных. Описанный здесь процесс приводит к тому, что они определяются как 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,


  Сведения об ответе

fo_I_K

15:34, 3rd August, 2020

Вероятно, самый быстрый и простой способ-это использование команды 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;

Есть и другие подобные проблемы (варчары и двойные кавычки spring обратно на ум), но ничего найти и заменить не удалось исправить.

Возможно, вам следует спросить, почему вы переходите, если размер базы данных performance/ является проблемой, возможно, посмотрите на реогинизацию схемы, если система переходит на более мощный продукт, это может быть идеальным временем для планирования будущего ваших данных.


  Сведения об ответе

prince

15:46, 12th August, 2020

Если вы используете 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')


  Сведения об ответе

lesha

14:36, 10th August, 2020

Недавно мне пришлось перейти с MySQL на JavaDB для проекта, над которым работает наша команда. Я нашел библиотеку Java, написанную Apache под названием DdlUtils , которая сделала это довольно легко. Он предоставляет API, что позволяет сделать следующее:

  1. Откройте схему базы данных и экспортируйте ее в виде файла XML.
  2. Измените DB на основе этой схемы.
  3. Импортируйте записи из одной DB в другую, предполагая, что они имеют одну и ту же схему.

Инструменты, которые мы получили в итоге, не были полностью автоматизированы, но они работали довольно хорошо. Даже если ваше приложение не находится в Java, не должно быть слишком сложно создать несколько небольших инструментов для выполнения одноразовой миграции. Я думаю, что мне удалось вытащить нашу миграцию с помощью менее чем 150 строк кода.


  Сведения об ответе

VCe znayu

12:57, 1st August, 2020

Получить дамп 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

После этого вам будет предложено ввести пароль. Пожалуйста, обратите внимание: если вы хотите ввести свой пароль непосредственно, вы должны сделать это WITHOUT пробел, непосредственно после -p :

mysql -u root -pYOURPASS somedb < myTemporarySQLFile.sql

Для больших отвалов:

mysqlimport или другие инструменты импорта, такие как BigDump .

BigDump дает вам индикатор выполнения:

enter image description here


  Сведения об ответе

DINO

00:48, 22nd August, 2020

Скрипт 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('"', "'")
...


  Сведения об ответе

VCe znayu

07:19, 29th August, 2020

Там нет необходимости в каких-либо скриптах, командах и т.д...

вам нужно только экспортировать вашу базу данных sqlite как файл .csv , а затем импортировать ее в Mysql с помощью phpmyadmin.

Я использовал его, и он работал потрясающе...


  Сведения об ответе

lesha

12:26, 6th August, 2020

На основе решения Джимса: Быстрый простой способ перенести 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, чтобы выбросить эту строку.


  Сведения об ответе

прога

18:51, 17th August, 2020

Я использую загрузчик данных для переноса практически любых данных, это помогает мне конвертировать MSSQL в MYSQL, MS к MSSQL, mysql, csv погрузчик, foxpro и MSSQL в MS доступ, MYSQl, CSV, foxpro и т. д. На мой взгляд это лучший инструмент миграции данных

Скачать Бесплатно : http://www.dbload.com


  Сведения об ответе

PAGE

19:43, 27th August, 2020

фаллино правильно определил местоположение ошибки в скрипте. У меня есть решение. Проблема заключается в следующих строках:

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

Шаблон замены (2-й параметр) в вызовах re.sub представляет собой строку "regular", поэтому вместо \1, расширяющегося до первого соответствия регулярному выражению, он расширяется до литерала 0x01. Аналогично, \2 расширяется до 0x02. Например, строка, содержащая: ,'t','f', был бы заменен на: <0x01>10<0x02>
(Первая замена изменяется, 't', на <0x1>1<0x2> Вторая замена меняет <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')


  Сведения об ответе

KOMP

14:28, 13th August, 2020

Ха... Жаль, что я не нашел его первым! Мой ответ был на этот пост... скрипт для преобразования файла mysql dump sql в формат, который можно импортировать в базу данных sqlite3

Сочетание этих двух вещей было бы именно тем, что мне нужно:


Когда база данных sqlite3 будет использоваться с ruby, вы можете изменить ее:

tinyint([0-9]*) 

к:

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

увы, это работает только наполовину, потому что даже если вы вставляете 1 и 0 в поле, помеченное boolean, 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, чтобы посмотреть на него, чтобы найти все логические значения.


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться