пятница, июня 15, 2007

Перенос данных с Mysql на Oracle


Есть самый простой вариант - это MySQL Migration Toolkit(флэш-туториал), доступен под все платформы. Очень удобно и просто.

Но если вам нужна миграция под ваши потребности и вы хотите понять как все это работает, то предлагаю сделать все самими своими руками.

Для начала создайте копию структуры базыданных с MySQL в Oracle:

mysqldump --no-data -uUSER -pPASSWORD -hHOST DATABASE

понятно, что вместо USER, PASSWORD, HOST, DATABASE вписать свои значения.

Измените вручную описание таблиц и помните, что длина имен таблиц и полей не должна превышать 30 символов:

mysql> CREATE TABLE some_table (
table_id INT(11) NOT NULL AUTO_INCREMENT,
table_field_name1 INT(11) NOT NULL,
table_field_name2 VARCHAR(255) NULL DEFAULT 'some',
table_field_name3 TEXT NOT NULL,
PRIMARY KEY (table_id),INDEX(table_field_name1)
);
Query OK, 0 rows affected (0.05 sec)



oracle> CREATE TABLE some_table (
table_id NUMBER(11) NOT NULL,
table_field_name1 NUMBER(11) NOT NULL,
table_field_name2 VARCHAR2(255) NOT NULL,
table_field_name3 VARCHAR2(4000) NOT NULL
);
Table created.


Пропало описание AUTO_INCREMENT - в Oracle для этого существует sequences(последовательности), PRIMARY KEY и INDEX - ключи создаются иначе, DEFAULT 'some' - в Oracle не совместимо понятие NULL и DEFAULT, т.е. надо выбрать либо DEFAULT 'some', либо NULL.

Изменилось описание INT на NUMBER, VARCHAR - VARCHAR2, TEXT - VARCHAR2(4000). Таблицу соответствия можно посмотреть здесь

Далее сделайте дамп данных с MySQL:
span class="code">
mysqldump --add-drop-table --default-character-set=latin1 --no-create-info --single-transaction=TRUE --add-locks=FALSE --extended-insert=FALSE -uUSER -pPASSWORD -hHOST DATABASE > data.sql

Важным параметром является single-transaction=TRUE - создаст записи по одной строчке, что позволит по строчно обработать данные с помощью sed.


  1. cat './tables/table1.table' \
  2. | sed -e 's/[`]//g' \
  3. | sed -e 's/^\/\*/\-\-/' \
  4. | sed -e 's/^LOCK TABLES/\-\-/' \
  5. | sed -e 's/^UNLOCK TABLES/\-\-/' \
  6. | sed -e \"s/'\(\-\)\{0,1\}\([0-9]\{1,\}\)\,\([0-9]\{0,2\}\)'/'\1\2.\3'/g\" \
  7. | sed -e \"s/'0000-00-00 00:00:00'/NULL/\" > 'tables/table1.sql'



Пояснение:

  1. построчное чтение
  2. замена всех символов `
  3. замена коментариев начинающихся с /* на --
  4. комментирование строк начинающихся с LOCK TABLES
  5. комментирование строк начинающихся с UNLOCK TABLES
  6. замена строкового значения числа вида '99,99' на 99.99 (об этом ниже)
  7. замена '0000-00-00 00:00:00' на NULL


В базе данных MySQL были строковые поля к которых были числа в формате 99,99 и 99.99, было решено при переходе на Oracle привести эти поля в тип NUMBER и вставка '99,99' привела бы к ошибке.

После того как данные подготовлены, внести их можно из sqlplus:

sqlplus> @./tables/table1.sql

Теперь когда целостность данных сохранена, можно создать sequences(последовательности), которые заменяют AUTO_INCREMENT в MySQL.
Создадим одну последовательность на все таблицы, и чтобы небыло дублирования id-номеров, последовательность нужно начать с максимального значения id-поля среди всех таблиц.

<?php
$oracle = array(
'name'=>'DB_NAME',
'user'=>'USER_NAME',
'password'=>'PASSWORD',
'host'=>'XXX.XXX.XXX.XXX',
'port'=>'PORT');
$mode = OCI_COMMIT_ON_SUCCESS;

$config = "
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS =
(COMMUNITY = PHP)
(PROTOCOL = TCP)
(Host = {$oracle['host']})
(Port = {$oracle['port']})
)
)
(CONNECT_DATA =
(SERVICE_NAME = {$oracle['name']})
)
)";

$link = oci_connect($oracle['user'],
$oracle['password'],
$config)
or die("Couldn't connect");

#Читаю список таблиц
$tables = file("tables.dat");
array_shift($tables);
$max_row_id = 1;
foreach($tables as $table){
$table = preg_replace("/[\r\n]/","",$table);
$sql = "SELECT * FROM $table WHERE rownum=1 ORDER BY 1 DESC \n";
$result = oci_parse($link, $sql);
oci_execute($result,$mode);
$row = oci_fetch_row($result);
if($row[0] > $max_row_id){
$max_row_id = $row[0];
}
$column_name = oci_field_name($result, 1);
#Создаем триггер для последовательности
$sql_trigger[] = "
CREATE OR REPLACE trigger \"TG_".strtoupper($table)."_BI\"
before insert on \"".strtoupper($table)."\"
for each row
begin
select \"MY_SEQ\".nextval into :NEW.".$column_name." from dual;
end TG_$table;
/";

}
print "Create sequence and start from $max_row_id+1\n";
#Удаляем последовательность
print $sql = "DROP sequence MY_SEQ ;/\n";
#Создаем последовательность
print $sql = "CREATE sequence MY_SEQ START WITH ".($max_row_id+1)." ;/\n";

#Создаем триггеры
foreach($sql_trigger as $sql){
print $sql."\n";
#$db->($sql);
}

oci_close($link);
?>


Все файлы для миграции можно скачать одним архивом здесь

Дополнительные ссылки:

  1. переход к другой кодировке в MySQL
  2. Мануал по sed


Другие статьи:

  1. Установка и настройка Oracle
  2. Разработка ПО на php под Oracle 10.X и MySQL
  3. Перенос данных с Mysql на Oracle

Комментариев нет: