前回のおさらい

前回はすぐに使える!DBデータの論理削除(1/4) そもそも「論理削除」って何?ということで、SQLやテーブルの構成を変更することなくデータの論理削除を実現できる「In-Database Archiving」の概要について紹介しました。

今回は、In-Database Archivingによる論理削除が、どのように実現されているかを紹介します。

前提として、今回も少しOracle Databaseの知識がある人向け(マニア向け)の内容になっているかと思いますが、どうぞよろしくお願いいたします。

どのように削除フラグを管理しているのか?

論理削除の運用において、データが「削除されている」かどうかを管理するためには、テーブルにデータが削除されているかの目印(削除フラグ)を格納する列を追加する必要があります。
しかし、前回、descコマンドでテーブルの構成を確認しても、そのような列は追加されていないように見えました。

では、In-Database Archivingではどのように削除フラグを管理しているのでしょうか?

今回は実際にコマンドとその結果を見ながら進めていきたいと思います。
もし実際に検証用にDBを使用できるのであれば、是非実際に試してみてください!

まずは今回検証で使用するDB環境についてご紹介します。

DBバージョン 12.2.0.1.0
Oracle DB所有者ユーザ oracle
ORACLE_BASE /u01/app/oracle
ORACLE_HOME /u01/app/oracle/product/12.2.0/dbhome_1
リスナー名 LISTENER
DBインスタンス名 orcl
Oracle SID orcl
PDB名(※1) orclpdb
PDB接続用接続記述子(※1) orclpdb

※1
12c以降、Oracle DatabaseではCDBアーキテクチャを使用した構成が推奨となっています。
そのため、今回はPDBを一つだけ作成したシングルテナント構成としています。
シングルテナント構成であれば、CDBアーキテクチャの使用に特別なライセンスは必要ありません。
(PDBを複数作成するとOracle Multitenantオプションが必要となります。)

CDBアーキテクチャについては、後日、本ブログでも取り上げる予定です!

こちらは一例で、DBの構成によっては、実行結果が異なる場合があります。


それでは、実際にコマンドを見ていきましょう!

まずは検証環境のDBを起動します。

# 今回の検証は、DB所有者ユーザでログインし、実行します。
# まず、環境変数を確認します。
[oracle$] env | grep ORA
ORACLE_BASE=/u01/app/oracle
ORACLE_HOME=/u01/app/oracle/product/12.2.0/dbhome_1
ORACLE_SID=orcl
# シングルテナント構成では、ORACLE_SIDはCDBのものになります。
# そのため、直接PDBに接続するにはPDB用の接続記述子の定義が必要です。

[oracle$] env | grep PATH
PATH=/u01/app/oracle/product/12.2.0/dbhome_1/bin:/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/oracle/.local/bin:/home/oracle/bin

# 次に、リスナーを起動します。
[oracle$] lsnrctl start LISTENER

[略]
リスナーのステータス
------------------------
別名                      LISTENER
バージョン                TNSLSNR for Linux: Version 12.2.0.1.0 - Production
開始日                    26-3月 -2018 20:01:35
稼働時間                  0 日 0 時間 0 分 1 秒
[略]

# 次に、DBを起動します。

[oracle$] sqlplus / as sysdba

SQL*Plus: Release 12.2.0.1.0 Production on 月 3月 26 20:02:54 2018
Copyright (c) 1982, 2016, Oracle.  All rights reserved.

アイドル・インスタンスに接続しました。

SQL> startup

ORACLEインスタンスが起動しました。

Total System Global Area  624951296 bytes
Fixed Size		    8795856 bytes
Variable Size		  310380848 bytes
Database Buffers	  297795584 bytes
Redo Buffers		    7979008 bytes
データベースがマウントされました。
データベースがオープンされました。

SQL> select instance_name, status from v$instance;

INSTANCE_NAME	 STATUS
---------------- ------------
orcl		 OPEN

SQL> show pdbs

    CON_ID CON_NAME			  OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
	 2 PDB$SEED			  READ ONLY  NO
	 3 ORCLPDB			  READ WRITE NO
# PDBのORCLPDBがDB起動と同時に起動しています。

SQL> show con_name

CON_NAME
------------------------------
CDB$ROOT
# 現在の接続先はCDBですね。

次に、PDBに検証用のユーザとテーブルを作成します。

# PDBに接続します。
SQL> alter session set container=orclpdb;

セッションが変更されました。

SQL> show con_name

CON_NAME
------------------------------
ORCLPDB

# 検証用ユーザを作成します。
SQL> create user test identified by test;

ユーザーが作成されました。

# In-Database Archivingに権限が必要かを確認するため、DBA権限は付与しません。
# 接続とテーブル作成に必要な権限を付与します。
SQL> grant create session, create table, unlimited tablespace to test;

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

# testユーザでPDBに接続します。
SQL> conn test/test@orclpdb
接続されました。

SQL> show con_name

CON_NAME
------------------------------
ORCLPDB

SQL> create table test.testtable(col1 number not null, col2 number);

表が作成されました。

SQL> desc test.testtable
 名前                         NULL?    型
 ---------------------------- -------- ----------------------------
 COL1		              NOT NULL NUMBER
 COL2		                       NUMBER

削除フラグ用の列は特に作成していません。
このテーブルに、データを入れていきます。

今回は、Databaseにあらかじめ用意されているサンプルスキーマを使用します。
サンプルスキーマを作成するSQLは、ORACLE_HOME/rdbms/admin配下に格納されています。ただし、このSQLはPDBにサンプルスキーマを作成してくれないため、今回テスト用に使用するPDBにサンプルスキーマが作成されるよう、少しだけ改造したSQLを用意しましょう。

# サンプルスキーマ作成用のSQLを確認します。
SQL> quit

[oracle$ ] cat $ORACLE_HOME/rdbms/admin/utlsampl.sql

[略]
CONNECT SCOTT/tiger
[略]

# 36行目辺りに上記のような行があるかと思います。
# 接続記述を使用していないためCDBにつながってしまいます。
# PDBに接続できるよう、SQLを変更します。
[oracle$] cp -p $ORACLE_HOME/rdbms/admin/utlsampl.sql  $ORACLE_HOME/rdbms/admin/utlsampl_pdborcl.sql
[oracle$] vi $ORACLE_HOME/rdbms/admin/utlsampl_pdborcl.sql
# 上で確認した36行目辺りのコマンドを以下のように変更します。
# [変更前]
# CONNECT SCOTT/tiger
# [変更後]
# CONNECT SCOTT/tiger@orclpdb

# 変更したSQLを実行します。
[oracle$] sqlplus / as sysdba

SQL*Plus: Release 12.2.0.1.0 Production on 金 3月 23 20:02:39 2018
Copyright (c) 1982, 2016, Oracle.  All rights reserved.

Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
に接続されました。

SQL> alter session set container = orclpdb;

セッションが変更されました。

SQL> @?/rdbms/admin/utlsampl_pdborcl.sql

[oracle$] sqlplus scott/tiger@orclpdb

SQL*Plus: Release 12.2.0.1.0 Production on 金 3月 23 20:06:16 2018
Copyright (c) 1982, 2016, Oracle.  All rights reserved.

最終正常ログイン時間: 金 3月  23 2018 20:06:06 +09:00


Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
に接続されました。

SQL> desc emp
 名前                                      NULL?    型
 ----------------------------------------- -------- ----------------------------
 EMPNO					   NOT NULL NUMBER(4)
 ENAME						    VARCHAR2(10)
 JOB						    VARCHAR2(9)
 MGR						    NUMBER(4)
 HIREDATE					    DATE
 SAL						    NUMBER(7,2)
 COMM						    NUMBER(7,2)
 DEPTNO 					    NUMBER(2)

# この時点で、削除フラグ用の列は定義されていません。

SQL> set pages 9999 lin 9999
SQL> select * from emp;

     EMPNO ENAME      JOB	       MGR HIREDATE	   SAL	     COMM     DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK	      7902 80-12-17	   800			  20
      7499 ALLEN      SALESMAN	      7698 81-02-20	  1600	      300	  30
      7566 JONES      MANAGER	      7839 81-04-02	  2975			  20
      7654 MARTIN     SALESMAN	      7698 81-09-28	  1250	     1400	  30
      7698 BLAKE      MANAGER	      7839 81-05-01	  2850			  30
      7782 CLARK      MANAGER	      7839 81-06-09	  2450			  10
      7839 KING       PRESIDENT 	   81-11-17	  5000			  10
      7844 TURNER     SALESMAN	      7698 81-09-08	  1500		0	  30
      7900 JAMES      CLERK	      7698 81-12-03	   950			  30
      7902 FORD       ANALYST	      7566 81-12-03	  3000			  20
      7934 MILLER     CLERK	      7782 82-01-23	  1300			  10

11行が選択されました。

 

まだIn-Database Archiveは無効なため、全ての行が表示されています。

それでは、このempテーブルに対し、In-Database Archivingを有効化してみましょう。

# 以下のコマンドにより、In-Database Archivingを有効化します。
SQL> ALTER TABLE emp ROW ARCHIVAL;

表が変更されました。

# desc	コマンドでempテーブルの定義を確認してみましょう。
SQL> desc emp
 名前                                      NULL?    型
 ----------------------------------------- -------- ----------------------------
 EMPNO					   NOT NULL NUMBER(4)
 ENAME						    VARCHAR2(10)
 JOB						    VARCHAR2(9)
 MGR						    NUMBER(4)
 HIREDATE					    DATE
 SAL						    NUMBER(7,2)
 COMM						    NUMBER(7,2)
 DEPTNO 					    NUMBER(2)

# 特にテーブルの定義は変更されていませんね。
# ここから、削除フラグを立てて動作を確認してみましょう。

# まず、削除フラグ設定前の出力を確認します。
# 出力が多くなってしまうので、ここからは参照の対象を絞ってみます。
SQL> select * from scott.emp where empno in (7369, 7499, 7521, 7566, 7654);

     EMPNO ENAME      JOB	       MGR HIREDATE	   SAL	     COMM     DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK	      7902 80-12-17	   800			  20
      7499 ALLEN      SALESMAN	      7698 81-02-20	  1600	      300	  30
      7521 WARD       SALESMAN	      7698 81-02-22	  1250	      500	  30
      7566 JONES      MANAGER	      7839 81-04-02	  2975			  20
      7654 MARTIN     SALESMAN	      7698 81-09-28	  1250	     1400	  30

# 以下のコマンドにより、EMPNOが7369または7521の行の削除フラグを設定します。
SQL> update emp set ora_archive_state = '1' where empno in (7369, 7521);

2行が更新されました。

# 削除フラグの設定は通常のupdate文と同等なため、コミットを行います。
SQL> commit;

コミットが完了しました。

# 再度テーブルを参照します。
SQL> select * from scott.emp where empno in (7369, 7499, 7521, 7566, 7654);

     EMPNO ENAME      JOB	       MGR HIREDATE	   SAL	     COMM     DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
      7499 ALLEN      SALESMAN	      7698 81-02-20	  1600	      300	  30
      7566 JONES      MANAGER	      7839 81-04-02	  2975			  20
      7654 MARTIN     SALESMAN	      7698 81-09-28	  1250	     1400	  30

 

削除フラグを設定したEMPNOが7369の行と7521の行が表示されなくなりました。
入力しているSQLは、In-Database Archiving有効化前と同様です。また、テーブル参照時の列の見え方も変化ありません。
では、削除フラグはどのように管理されているのでしょうか。

ここで、削除フラグ設定時のアップデート文に注目します。
アップデートの対象列は「ora_archive_state」となっていますね。ということは、テーブルが何らかの形でこのora_archive_state列を持っていて、ここに削除フラグを格納していると考えられます。
descとは別の方法でempテーブルの構造を確認しましょう。

SQL> col table_name for a10
SQL> col column_name for a30
SQL> col data_type for a20

SQL> select table_name, column_name, data_type, hidden_column from user_tab_cols
2  where table_name = 'EMP';

TABLE_NAME COLUMN_NAME			  DATA_TYPE	       HID
---------- ------------------------------ -------------------- ---
EMP	   EMPNO			  NUMBER	       NO
EMP	   ENAME			  VARCHAR2	       NO
EMP	   JOB				  VARCHAR2	       NO
EMP	   MGR				  NUMBER	       NO
EMP	   HIREDATE			  DATE		       NO
EMP	   SAL				  NUMBER	       NO
EMP	   COMM 			  NUMBER	       NO
EMP	   DEPTNO			  NUMBER	       NO
EMP	   SYS_NC00009$ 		  RAW		       YES
EMP	   ORA_ARCHIVE_STATE		  VARCHAR2	       YES
10行が選択されました。

 

HIDDEN_COLUMN列が「YES」のORA_ARCHIVE_STATE列が確認できますね。
HIDDEN_COLUMN列が「YES」の列は非表示列と言い、ユーザの操作から特定の列を隠す12cの新機能です。
非表示列が有効化された列は、明示的に列名を追加しない限り、select文やdelete文などDMLの対象にならず、descコマンドでも表示されません。
非表示列のデータは、明示的に列名を指定することで参照が可能です。

SQL> col ORA_ARCHIVE_STATE for a5
SQL> select emp.*, emp.ora_archive_state from scott.emp
  2  where empno in (7369, 7499, 7521, 7566, 7654);

     EMPNO ENAME      JOB	       MGR HIREDATE	   SAL	     COMM     DEPTNO ORA_A
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- -----
      7499 ALLEN      SALESMAN	      7698 81-02-20	  1600	      300	  30 0
      7566 JONES      MANAGER	      7839 81-04-02	  2975			  20 0
      7654 MARTIN     SALESMAN	      7698 81-09-28	  1250	     1400	  30 0

ora_archive_state列が表示できました。ただ、ora_archive_state列が0以外の行は論理削除されているため、表示されません。
ここで、論理削除のメリットの一つに、削除済みの行のデータも参照可能、という点があったかと思います。
In-Database Archivingでは、論理削除済みの行の表示は以下のように行います。

SQL> ALTER SESSION SET ROW ARCHIVAL VISIBILITY = ALL;
SQL> select emp.*, emp.ora_archive_state from scott.emp
  2  where empno in (7369, 7499, 7521, 7566, 7654);

     EMPNO ENAME      JOB	       MGR HIREDATE	   SAL	     COMM     DEPTNO ORA_A
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- -----
      7369 SMITH      CLERK	      7902 80-12-17	   800			  20 1
      7499 ALLEN      SALESMAN	      7698 81-02-20	  1600	      300	  30 0
      7521 WARD       SALESMAN	      7698 81-02-22	  1250	      500	  30 1
      7566 JONES      MANAGER	      7839 81-04-02	  2975			  20 0
      7654 MARTIN     SALESMAN	      7698 81-09-28	  1250	     1400	  30 0

 

非表示となっていた行が表示され、ora_archive_state列が1になっていることが確認できました。
これはALTER SESSION文で変更しているため、他のセッションからは論理削除された行は見えないままです。
また、自分自身も一度セッションを切断すると論理削除済みの行が見えなくなります。
セッションを維持したまま論理削除済みの行を非表示にする場合は以下のようにします。

SQL> ALTER SESSION SET ROW ARCHIVAL VISIBILITY = ACTIVE;
SQL> select emp.*, emp.ora_archive_state from scott.emp
  2  where empno in (7369, 7499, 7521, 7566, 7654);

     EMPNO ENAME      JOB	       MGR HIREDATE	   SAL	     COMM     DEPTNO ORA_A
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- -----
      7499 ALLEN      SALESMAN	      7698 81-02-20	  1600	      300	  30 0
      7566 JONES      MANAGER	      7839 81-04-02	  2975			  20 0
      7654 MARTIN     SALESMAN	      7698 81-09-28	  1250	     1400	  30 0

 

論理削除された行の物理削除は、ora_archive_state列を条件にdeleteすることで実現できます。
また、In-Database Archivingでは、ora_archive_state列が「0」以外であれば論理削除済みと扱うため、論理削除時は論理削除日をora_archive_state列に入れることで論理削除を行い、delete文でora_archive_state列の条件を作りこむことで、特定期間よりも前に論理削除した行を物理削除する、といった処理が実現可能です。

ちょっと長くなってきたので、このあたりで休憩しましょう。
今回、削除フラグを保持する仕組みが確認できたので、次回はテーブルの参照時に,入力するSQLを変更せずに論理削除済みの行を表示しない仕組みを確認します。