OracleからMySQLへデータ移行したお話

# はじめに

この記事は[第二のドワンゴ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/dwango2)の記事になります。


OracleDB → MySQL移行作業にて、テーブルデータを移行した時のお話です。

異なるデータベース間の移行作業は移行リスク、作業工数の観点から発議し辛い提案ですが、
実際に「異なるデータベース間にて、実データの移行作業はどうやるか」を主軸に進めたいと思います。

# 目次
1. 移行方法の選定
2. 環境と準備
3. テーブル構造移行
4. データ移行
5. まとめ

# 1. 移行方法の選定
同じデータベース間であれば、
1. dump
2. import
上記2手順で済みますが、SQL構造の異なる場合は単純には上手くいきません。

DDL/DMLを別にimportする上で、**SQLのフォーマットをデータベースに合わせた形にconvert**する必要があります。

今回は[Oracle to MySQL Migration](http://www.sqlines.com/oracle-to-mysql)を用いたconvert方法を紹介させて頂こうと思います。

次にデータinsertのDML文は上記ツールでのconvertではなく、

 - 明示的なinsert文では、設定によりエラーになる可能性がある（mysql/STRICTモード等）[^1]
 - csvの方がinsert文より柔軟性があるのも強み

上記理由から**csvファイルとしてデータexportし、csvとしてデータimport**する事にしました。
 
# 2. 環境と準備
**作業**
移行元：**Oracle 11g**
移行先：**MySQL 5.7**

**必要なアプリケーション**
[SQL Plus*](https://www.oracle.com/database/technologies/instant-client/downloads.html)
[Oracle to MySQL Migration](http://www.sqlines.com/oracle-to-mysql)[^2]
[MySQL](https://dev.mysql.com/downloads/installer/)[^3]

<details><summary>SQL Plus* install 時のメモ（クリックで展開）</summary>
sqlplus コマンドが動作せず、libc.so.6 が読み込めないエラーは以下で対処した。<div>

```
# 必要なパッケージを探す
yum whatprovides libc.so.6
# パッケージをインストールする
yum install libc.so.6 
 
# その他、インストールしたコマンド
## ld-linux.so.2
yum install ld-linux.so.2
## libstdc++.so.6 適正バージョンとインストール
yum whatprovides libstdc++.so.6
yum install libstdc++.so.6
yum install libstdc++-4.4.7-23.el6.i686
yum upgrade libstdc++
```

</div>
</details>

<details><summary>GNU lib install 時のメモ（クリックで展開）</summary>
GNU lib のバージョン2.14が求められた
<div>

```
# version確認
# ldd --version
ldd (GNU libc) 2.12
 
# yum upgrade（2.12以上はupdateされない）
yum update glibc
 
# server からDL
wget https://ftp.gnu.org/gnu/glibc/glibc-2.14.tar.gz
tar zxf glibc-2.14.tar.gz
cd glibc-2.14
mkdir build && cd build
../configure --prefix=/opt/local
make -j8
sudo make install

# env追加
echo 'export LD_LIBRARY_PATH=/opt/local/lib:$LD_LIBRARY_PATH'  >> ~/.bash_profile
# 再起動
shutdown -r now
```

</div>
</details>

# 3. データ構造移行
## Oracle Table Struct Dump

### コマンドラインで実行
expdpを用いる[^4]

```
EXPDP/IMPDP {ユーザー}/{パスワード}@{接続文字列} 
 DIRECTORY={ディレクトリ} DUMPFILE={ファイル名} LOG={ログファイル名} TABLES={テーブル} CONTENT=METADATA_ONLY

-- example
EXPDP test/pass@orcl DIRECTORY=dp_dir DUMPFILE=dmp_table.dmp 
　LOGFILE=exp.log TABLES=test.emp CONTENT=METADATA_ONLY
```

### SQL DeveloperのUI上から実行
「ツール」→「データベース・エクスポート」
<img width="179" alt="スクリーンショット 2019-12-04 17.26.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528828/79d6003e-2d66-764f-eeee-87cb4496c81d.png">

「DDLのエクスポート」にチェック
「データのエクスポート」はチェックを外す 
<img width="315" alt="スクリーンショット 2019-12-04 17.26.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528828/a1a35186-95bf-1d23-792d-451db28568df.png">

## Query Convert
Oracle to MySQL Migrationの`sqllines`を用います

```
# DB移行ツール(sqllines)を用いる
ファイル名 - oracle_export.sql
./sqlines -s=oracle -t=mysql -in=oracle_export.sql
 
> oracle_export_output.sql が出力される
```
## MySQL Query Struct Import
```
# mysqlコマンド
MYSQL_PWD='****' mysql -u(ユーザー名) -A -h (サーバー) < oracle_export_output.sql
```

# 4. データ移行
## (1)Oracle Record Export
```
# oracleにログイン
sqlplus / nolog
 
#　接続
conn (ユーザー名)/(パスワード)@(ホスト):(ポート番号)/(スキーマ)
# データdump
exp (ユーザー名)/(パスワード)@(ホスト):(ポート番号)/(スキーマ) tables=(テーブル名) directory=export dumpfile=(dumpファイル名) logfile=(logファイル名)
```

## (2)MySQL Record Import
```
MYSQL_PWD='****' mysql -u(ユーザー名) -A -h (ホスト)
use (スキーマ);LOAD DATA LOCAL INFILE '(logファイル名)' INTO TABLE （テーブル） FIELDS TERMINATED BY ',' ENCLOSED BY '"';
```

## 移行時に使ったScript
テーブル単位でデータ移行するScript
https://github.com/yuichi-sato/query-script

# 5. まとめ
Table、Materialized View も単純なデータレベルでの移行なら可能で、
型を精査する作業時間が短縮される恩恵は大きく、移行作業の助けになりました。

作業に一手間掛かりますが、sqlines / shellで完結するのは大きいです。

---
### 参考
sqlines
http://www.sqlines.com/oracle-to-mysql
oracle exp
https://oracle-chokotto.com/ora_export.html
oracle expdp
https://oracle-chokotto.com/ora_export_expdp.html

[^1]: insert query で errorが発生すると rollbackされるMySQLの設定
[^2]: Linux/Windows版のみ
[^3]: Macであれば `brew install mysql`
[^4]: 最新バージョンではexpdpコマンド推奨、古いバージョンはexpコマンドを用いることになる