OracleDBのマテリアライズド・ビューによるデータ移行を試す

# 概要

DBLINKとマテリアライズド・ビューを組み合わせたデータ同期について検証するために、DockerでOracleDBを2台立て、OracleDB間でのデータ移行とリフレッシュを試してみます。

# 実行環境

Dockerイメージの利用には自身でのDockerビルドが必要になりますが割愛します。

docker-composeを利用してDock上でOracleDBを2台立ち上げます。

ディレクトリoradata1、oradata2の所有者のユーザIDは54321にchownで変更しています。

oracle1が移行元、oracle2が移行先になります。

```yaml
version: '3'
services:

  oracle1:
    image: oracle/database:19.3.0-ee
    environment:
      - ORACLE_SID=ORCLCDB1
      - ORACLE_PWD=p@ssw0rd
      - ORACLE_CHARACTERSET=AL32UTF8
    ports:
      - 11521:1521
      - 15500:5500
    volumes:
      - ./oradata1:/opt/oracle/oradata

  oracle2:
    image: oracle/database:19.3.0-ee
    environment:
      - ORACLE_SID=ORCLCDB2
      - ORACLE_PWD=p@ssw0rd
      - ORACLE_CHARACTERSET=AL32UTF8
    ports:
      - 21521:1521
      - 25500:5500
    volumes:
      - ./oradata2:/opt/oracle/oradata
```    

# 作業概要

以下のシナリオで作業を進めます。

1. （共通）ユーザ、スキーマ、表領域を用意
2. （Oracle1）テーブルの作成とデータを挿入
3. （Oracle2）データベースリンクの設定
4. （Oracle1）マテリアライズド・ビューログを作成
5. （Oracle2）マテリアライズド・ビューの作成
6. （Oracle1）データの追加挿入確認

# 作業詳細

## （共通）ユーザ、スキーマ、表領域を用意

（Oracle1)まずは、テーブルを作成する下準備をします。CDBに接続します。

```bash
sqlplus / as sysdba;
```

PDBに接続して表領域作成します。

```sql
ALTER SESSION SET CONTAINER = ORCLPDB1;

CREATE TABLESPACE TABLE_SPACE_MONSTER_GAME /*表領域名*/
DATAFILE '/opt/oracle/oradata/ORCLCDB1/ORCLPDB1/monster_game_1.dbf'
SIZE 100M /*サイズ*/
AUTOEXTEND OFF /*自動拡張*/
;

CREATE TEMPORARY TABLESPACE TEMP_SPACE_MONSTER_GAME　/*表領域名*/
TEMPFILE '/opt/oracle/oradata/ORCLCDB1/ORCLPDB1/temp_monster_game_2.dbf'
SIZE 100M /*サイズ*/
;
```

ユーザ作成して権限付与します。

```sql
CREATE USER MONSTER_GAME
IDENTIFIED BY password123
DEFAULT TABLESPACE TABLE_SPACE_MONSTER_GAME
TEMPORARY TABLESPACE TEMP_SPACE_MONSTER_GAME
QUOTA 100M ON TABLE_SPACE_MONSTER_GAME
PROFILE DEFAULT
ACCOUNT UNLOCK
;

GRANT CREATE SESSION, 
 CREATE TABLE,
 CREATE VIEW,
 CREATE SEQUENCE,
 CREATE TRIGGER,
 CREATE SYNONYM,
 UNLIMITED TABLESPACE TO MONSTER_GAME;
```

作成したユーザでPDBにログインします。

```bash
sqlplus MONSTER_GAME@//localhost:1521/ORCLPDB1
```

同様のことをOracle2でも実施します。

```bash
sqlplus / as sysdba;
```

```sql
ALTER SESSION SET CONTAINER = ORCLPDB1;

CREATE TABLESPACE TABLE_SPACE_MONSTER_GAME /*表領域名*/
DATAFILE '/opt/oracle/oradata/ORCLCDB2/ORCLPDB1/monster_game_1.dbf'
SIZE 100M /*サイズ*/
AUTOEXTEND OFF /*自動拡張*/
;

CREATE TEMPORARY TABLESPACE TEMP_SPACE_MONSTER_GAME　/*表領域名*/
TEMPFILE '/opt/oracle/oradata/ORCLCDB2/ORCLPDB1/temp_monster_game_2.dbf'
SIZE 100M /*サイズ*/
;
```

```sql
CREATE USER MONSTER_GAME
IDENTIFIED BY password123
DEFAULT TABLESPACE TABLE_SPACE_MONSTER_GAME
TEMPORARY TABLESPACE TEMP_SPACE_MONSTER_GAME
QUOTA 100M ON TABLE_SPACE_MONSTER_GAME
PROFILE DEFAULT
ACCOUNT UNLOCK
;

GRANT CREATE SESSION, 
 CREATE TABLE,
 CREATE VIEW,
 CREATE SEQUENCE,
 CREATE TRIGGER,
 CREATE SYNONYM,
 UNLIMITED TABLESPACE TO MONSTER_GAME;
```

## （Oracle1）テーブルの作成とデータを挿入

同期したいテーブルを作成し適当にデータを入れておきます。

```sql
CREATE TABLE PLAYER(
  NAME VARCHAR2(100) NOT NULL,
  HP NUMBER(4, 0) NOT NULL,
  STR NUMBER(3,0) NOT NULL,
  DEF NUMBER(3,0) NOT NULL
);

INSERT INTO PLAYER VALUES ('MAGICIAN', 10, 10, 30);
INSERT INTO PLAYER VALUES ('THIEF', 20, 40, 30);
INSERT INTO PLAYER VALUES ('ASSASSIN', 20, 60, 30);
INSERT INTO PLAYER VALUES ('FIGHTER', 40, 30, 30);
```

## （Oracle2）データベースリンクの設定

Oracle2にSYSでログインしてOracle2のMONSTER_GAMEに権限を追加します。

```sql
GRANT CREATE DATABASE LINK TO MONSTER_GAME;
```

Oracle2のMONSTER_GAMEにログインしてOracle1のMONSTER_GAMEに対してデータベースリンクを設定します。

```sql
CREATE DATABASE LINK DBLINK_PLAYER
CONNECT TO MONSTER_GAME IDENTIFIED BY password123 
USING '//oracle1:1521/ORCLPDB1';
```

SELECTしてみるとOracle1から無事データを取得できていることを確認できました。

```sql
SQL> COL NAME FORMAT A10
SQL> SELECT * FROM PLAYER@DBLINK_PLAYER;

NAME               HP        STR        DEF
---------- ---------- ---------- ----------
MAGICIAN           10         10         30
THIEF              20         40         30
ASSASSIN           20         60         30
FIGHTER            40         30         30
```

## （Oracle1）マテリアライズド・ビュー・ログを作成

リフレッシュに必要なマテリアライズド・ビュー・ログを作成します。

```sql
CREATE MATERIALIZED VIEW LOG ON PLAYER WITH ROWID;
```

## （Oracle2）マテリアライズド・ビューの作成

Oracle2にSYSでログインしてOracle2のMONSTER_GAMEに権限を追加します。

```sql
GRANT CREATE MATERIALIZED VIEW TO MONSTER_GAME;
```

Oracle2のMONSTER_GAMEでマテリアライズド・ビューを作成します。

```sql
CREATE MATERIALIZED VIEW PLAYER
AS SELECT * FROM PLAYER@DBLINK_PLAYER;
```

SELECT文でOracle1のテーブルと同期できていることを確認できました。

```sql
SQL> COL NAME FORMAT A10
SQL> SELECT * FROM PLAYER;

NAME               HP        STR        DEF
---------- ---------- ---------- ----------
MAGICIAN           10         10         30
THIEF              20         40         30
ASSASSIN           20         60         30
FIGHTER            40         30         30
```

## （Oracle1）データの追加挿入確認

Oracle1のMONSTER_GAMEでデータ挿入します。

```sql
INSERT INTO PLAYER VALUES ('KNIGHT', 80, 30, 20);
INSERT INTO PLAYER VALUES ('BISHOP', 30, 30, 50);
```

Oracle2のMONSTER_GAMEでリフレッシュします。

```sql
EXECUTE DBMS_MVIEW.REFRESH('PLAYER', 'c');
```

Oracle2のMONSTER_GAMEでSELECT文を実施し、同期できていることを確認できました。

```sql
SQL>  SELECT * FROM PLAYER;

NAME               HP        STR        DEF
---------- ---------- ---------- ----------
MAGICIAN           10         10         30
THIEF              20         40         30
ASSASSIN           20         60         30
FIGHTER            40         30         30
KNIGHT             80         30         20
BISHOP             30         30         50

6 rows selected.
```

# 後片付け

（Oracle2）DBLINK削除

```sql
DROP DATABASE LINK DBLINK_PLAYER;
```

（共通）スキーマと表領域削除

```sql
DROP USER MONSTER_GAME CASCADE;
DROP TABLESPACE TABLE_SPACE_MONSTER_GAME INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS;
DROP TABLESPACE TEMP_SPACE_MONSTER_GAME INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS;
```

# 感想

実際に試してみることでどちらのサーバで何をする必要があるかがわかりました。