FLASHBACK DATA ARCHIVEを試す


# FLASHBACK DATA ARCHIVEとは？
Oracle Databaseは基本的に最新のデータを保存し、利用者に提供します。UPDATE文やDELETE文による更新前のデータは参照整合性維持のために一時的にUNDO表領域に格納されますが、一定時間経過すると自動的に削除されます。

  FLASHBACK DATA ARCHIVE機能は、UNDO表領域に保存される過去データを別領域に自動的に永続化するOracle Database Enterprise Editionの標準機能です。更新前データがUNDO表領域から削除された後でもSELECT * FROM AS OF文を使って過去データを参照することができます。
  過去データを保存するオブジェクトをFLASHBACK ARCHIVEと呼びます。FLASHBACK ARCHIVEオブジェクトは通常のテーブルとは異なる領域／フォーマットで管理されているため、改ざんされていないことを保証してデータベースの過去の状態を取得することができます。アプリケーションに変更を加えずに過去データを簡単に参照することができ、改ざんの検知や過去の状態の検証に有効です。履歴を保存するかの決定はテーブル単位に指定できます。

# FLASHBACK ARCHIVEの管理
  SYSユーザーでもかまいませんが、FLASHBACK ARCHIVEの管理を行うユーザーにはFLASHBACK ARCHIVE ADMINISTERロールを付与する必要があります。
下記の例ではfdadminユーザーにFLASHBACK ARCHIVE ADMIINSTERロールを付与しています。

```
SQL> GRANT FLASHBACK ARCHIVE ADMINISTER TO fdadmin;

権限付与が成功しました。

```

  権限を付与したユーザーはFLASHBACK ARCHIVEオブジェクトを作成することができます。オブジェクト名、データの保存期間、最大容量を指定します。
  作成にはCREATE FLASHBACK ARCHIVE文を使います。下記の例では最大容量10MB、保存期間を1年としています。
作成したFLASHBACK ARCHIVEの情報はDBA_FLASHBACK_ARCHIVEビューとDBA_FLASHBACK_ARCHIVE_TSビューから参照できます。しかしDBA_OBJECTSビューには情報が登録されません。

```
SQL> CREATE FLASHBACK ARCHIVE fla1 TABLESPACE ts1 QUOTA 10M RETENTION 1 YEAR;

フラッシュバック・アーカイブが作成されました。

SQL> SELECT FLASHBACK_ARCHIVE_NAME, RETENTION_IN_DAYS FROM DBA_FLASHBACK_ARCHIVE;

FLASHBACK_ARCHIVE_NAME         RETENTION_IN_DAYS
------------------------------ -----------------
FLA1                                         365

SQL> SELECT FLASHBACK_ARCHIVE_NAME, TABLESPACE_NAME, QUOTA_IN_MB FROM DBA_FLASHBACK_ARCHIVE_TS;

FLASHBACK_ARCHIVE_NAME         TABLESPACE_NAME                QUOTA_IN_MB
------------------------------ ------------------------------ ----------------------------------------
FLA1                           TS1                            10

SQL> SELECT COUNT(*) FROM dba_objects WHERE owner='FDADMIN';

レコードが選択されませんでした。

```

  作成したFLASHBACK ARCHIVEをユーザーが利用できるようにFLASHBACK ARCHIVE権オブジェクト限を付与します。下記の例ではfduserユーザーに対して作成したFLASHBACK ARCHIVE fla1の利用を許可しています。

```
SQL> GRANT FLASHBACK ARCHIVE ON fla1 TO fduser;

権限付与が成功しました。
```

  FLASHBACK ARCHIVEに登録されたテーブルの情報はDBA_FLASHBACK_ARCHIVE_TABLESビューから参照できます。

```
SQL> SELECT TABLE_NAME, FLASHBACK_ARCHIVE_NAME FROM dba_flashback_archive_tables;

TABLE_NAME                     FLASHBACK_ARCHIVE_NAME
------------------------------ ------------------------------
DATA1                          FLA1

```

  FLASHBACK ARCHIVEの設定変更や、履歴データの削除にはALTER FLASHBACK ARCHIVE文を実行します。
ALTER FLASHBACK ARCHIVE文は下記の用途で使用できます。

- デフォルトのFLASHBACK ARCHIVE設定 (ALTER FLASHBACK ARCHIVE SET DEFAULT)
- 領域の追加 (ALTER FLASHBACK ARCHIVE ADD TABLESPACE | MODIFY TABLESPACE)
- 領域の削除 (ALTER FLASHBACK ARCHIVE REMOVE TABLESPACE)
- 履歴データの削除（ALTER FLASHBACK ARCHIVE PURGE BEFORE TIMESTAPM | PURGE ALL)
- 最適化設定（ALTER FLASHBACK ARCHIVE OPTIMIZE DATA)


# FLASHBACK ARCHIVEの利用
FLASHBACK ARCHIVEを利用するユーザーは、CREATE TABLE文またはALTER TABLE文でFLASHBACK ARCHIVE句を指定します。下記の例ではdata1テーブルを作成し、FLASHBACK ARCHIVEとしてfla1を指定しています。FLASHBACK ARCHIVEオブジェクト権限が指定されていない場合、「ORA-55620: フラッシュバック・アーカイブの使用権限がありません」エラーが発生します。

```
SQL> CREATE TABLE data1(c1 NUMBER PRIMARY KEY, c2 VARCHAR2(10)) FLASHBACK ARCHIVE fla1;

表が作成されました。

```

過去データを参照するにはAS OF句を使ってテーブルを検索するだけです。
下記の例では、c1=1000のレコードを更新していますが、10分前のレコードを参照しています。

```
SQL> UPDATE data1 SET c2='update' WHERE c1=1000;

1行が更新されました。

SQL> SELECT * FROM data1 WHERE c1=1000;

        C1 C2
---------- ----------
      1000 update

SQL> SELECT * FROM fduser.data1 AS OF TIMESTAMP(SYSTIMESTAMP - INTERVAL '10' MINUTE) WHERE c1=1000;

        C1 C2
---------- ----------
      1000 data1
```

FLASHBACK ARCHIVE を参照する場合には実行計画に表示が現れます。

```
SQL> SELECT * FROM fduser.data1 AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '5' MINUTE);

レコードが選択されませんでした。


実行計画
----------------------------------------------------------
Plan hash value: 75922806

-----------------------------------------------------------------------------------------------------------------------------------

| Id  | Operation                               | Name                    | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                        |                         |2 |    70 |     5   (0)| 00:00:01 |       |       |
|   1 |  VIEW                                   |                         |2 |    70 |     5   (0)| 00:00:01 |       |       |
|   2 |   UNION-ALL                             |                         |  |       |            |          |       |       |
|*  3 |    FILTER                               |                         |  |       |            |          |       |       |
|   4 |     PARTITION RANGE SINGLE              |                         |1 |    63 |     2   (0)| 00:00:01 |   KEY |     1 |
|*  5 |      TABLE ACCESS FULL                  | SYS_FBA_HIST_26363      |1 |    63 |     2   (0)| 00:00:01 |   KEY |     1 |
|*  6 |    FILTER                               |                         |  |       |            |          |       |       |
|   7 |     NESTED LOOPS OUTER                  |                         |1 |  2075 |     3   (0)| 00:00:01 |       |       |
|*  8 |      TABLE ACCESS FULL                  | DATA1                   |1 |    47 |     2   (0)| 00:00:01 |       |       |
|*  9 |      TABLE ACCESS BY INDEX ROWID BATCHED| SYS_FBA_TCRV_26363      |1 |  2028 |     1   (0)| 00:00:01 |       |       |
|* 10 |       INDEX RANGE SCAN                  | SYS_FBA_TCRV_IDX1_26363 |1 |       |     1   (0)| 00:00:01 |       |       |
-----------------------------------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter("TIMESTAMP_TO_SCN"(SYSTIMESTAMP(6)-INTERVAL'+00 00:05:00' DAY(2) TO SECOND(0))<1203424)
   5 - filter("ENDSCN"<=1203424 AND ("OPERATION" IS NULL OR "OPERATION"<>'D') AND
              "ENDSCN">"TIMESTAMP_TO_SCN"(SYSTIMESTAMP(6)-INTERVAL'+00 00:05:00' DAY(2) TO SECOND(0)) AND ("STARTSCN" IS NULL OR
              "STARTSCN"<="TIMESTAMP_TO_SCN"(SYSTIMESTAMP(6)-INTERVAL'+00 00:05:00' DAY(2) TO SECOND(0))))
   6 - filter("STARTSCN"<="TIMESTAMP_TO_SCN"(SYSTIMESTAMP(6)-INTERVAL'+00 00:05:00' DAY(2) TO SECOND(0)) OR "STARTSCN" IS NULL)
   8 - filter(("T"."VERSIONS_OPERATION" IS NULL OR "T"."VERSIONS_OPERATION"<>'D') AND ("T"."VERSIONS_STARTSCN" IS NULL OR
              "T"."VERSIONS_STARTSCN"<="TIMESTAMP_TO_SCN"(SYSTIMESTAMP(6)-INTERVAL'+00 00:05:00' DAY(2) TO SECOND(0))) AND
              ("T"."VERSIONS_ENDSCN" IS NULL OR "T"."VERSIONS_ENDSCN">"TIMESTAMP_TO_SCN"(SYSTIMESTAMP(6)-INTERVAL'+00 00:05:00' DAY(2)
              TO SECOND(0))))
   9 - filter(("ENDSCN"(+) IS NULL OR "ENDSCN"(+)>1203424) AND ("STARTSCN"(+) IS NULL OR "STARTSCN"(+)<1203424))
  10 - access("RID"(+)=ROWIDTOCHAR("T".ROWID))

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)

統計
----------------------------------------------------------
        341  recursive calls
         37  db block gets
        322  consistent gets
          1  physical reads
       7032  redo size
        425  bytes sent via SQL*Net to client
        596  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
         22  sorts (memory)
          0  sorts (disk)
          0  rows processed
```