無料で多くのオプション機能が使えるOracle Database 21c Express Edition (XE) でIn-Memoryを試してみた

前回はOracle Database 21c XEの構築を行いましたが、今回は21c XE上でオプション機能のIn-Memoryを試してみようと思います。
なおIn-Memoryの解説は以下をご参照ください。
津島博士のOracle Database In-Memory (DBIM) の基礎 と 最新情報

データの準備

データについてはお馴染みのサンプルスキーマを活用しました。
サンプルスキーマを作成するスクリプトは以下から入手できます。
Oracle Database 21c Sample Schemas

DBサーバにアップロードして適当な場所に解凍しましょう。
なおスクリプト内のパスで「__SUB__CWD__」となっている箇所を置換しないとエラーになりますので、例えば次のようなコマンドで置き換えましょう。

perl -p -i.bak -e 's#__SUB__CWD__#'$(pwd)'#g' *.sql */*.sql */*.dat

PDBにログインしてスクリプトを実行します。

$ sqlplus sys@localhost:1521/xepdb1 as sysdba
SQL> @<path to dir>/mksample <SYSTEM_password> <SYS_password> <HR_password> <OE_password> <PM_password> <IX_password> <SH_password> <BI_password> USERS TEMP <path to log dir> localhost:1521/xepdb1

以上でデータの準備は完了です。

In-Memoryの有効化

ルート・コンテナにログインしてinmemory_sizeを設定した後、インスタンスを再起動してIn-Memoryを有効化します。
今回は適当に300Mで確保しました。

$ sqlplus / as sysdba
SQL> alter system set inmemory_size=300M scope=spfile;

 System altered.

SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.

Total System Global Area 1610612080 bytes
Fixed Size    9686384 bytes
Variable Size  335544320 bytes
Database Buffers  939524096 bytes
Redo Buffers    7090176 bytes
In-Memory Area  318767104 bytes
Database mounted.
Database opened.

上記ログからIn-Memory用の領域が確保されていると分かります。

In-Memoryを使用しない状態でクエリ実行

手始めにIn-Memoryを使わない状態でクエリを実行してみます。
Sales Historyの表に対して集計するクエリを発行します。

-- バッファキャッシュのクリア
$ sqlplus sys@localhost:1521/xepdb1 as sysdba
SQL> ALTER SYSTEM FLUSH BUFFER_CACHE;
SQL> exit

-- 実行時間と実行計画の出力設定
$ sqlplus sh@localhost:1521/xepdb1
SQL> set timing on
SQL> set autotrace traceonly

-- 表示設定
SQL> set pagesize 1000
SQL> set linesize 1000

-- 集計クエリ実行
SQL> SELECT channel_desc, TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$,
     RANK() OVER (ORDER BY SUM(amount_sold)) AS default_rank,
     RANK() OVER (ORDER BY SUM(amount_sold) DESC NULLS LAST) AS custom_rank
     FROM sh.sales, sh.products, sh.customers, sh.times, sh.channels, sh.countries
     WHERE sales.prod_id=products.prod_id AND sales.cust_id=customers.cust_id
     AND customers.country_id = countries.country_id AND sales.time_id=times.time_id
     AND sales.channel_id=channels.channel_id
     AND times.calendar_month_desc IN ('2000-09', '2000-10')
     AND country_iso_code='US'
     GROUP BY channel_desc;

Elapsed: 00:00:00.20

Execution Plan
----------------------------------------------------------
Plan hash value: 1456095225

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

| Id  | Operation                          | Name        | Rows  | Bytes | Cost
(%CPU)| Time     | Pstart| Pstop |

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

|   0 | SELECT STATEMENT                   |             |     5 |   380 |   969
   (2)| 00:00:01 |       |       |

|   1 |  WINDOW SORT                       |             |     5 |   380 |   969
   (2)| 00:00:01 |       |       |

|   2 |   WINDOW SORT                      |             |     5 |   380 |   969
   (2)| 00:00:01 |       |       |

|   3 |    HASH GROUP BY                   |             |     5 |   380 |   969
   (2)| 00:00:01 |       |       |

|   4 |     NESTED LOOPS                   |             |  2016 |   149K|   966
   (2)| 00:00:01 |       |       |

|*  5 |      HASH JOIN                     |             |  2016 |   141K|   966
   (2)| 00:00:01 |       |       |

|   6 |       TABLE ACCESS FULL            | CHANNELS    |     5 |    65 |     3
   (0)| 00:00:01 |       |       |

|*  7 |       HASH JOIN                    |             |  2016 |   116K|   963
   (2)| 00:00:01 |       |       |

|   8 |        PART JOIN FILTER CREATE     | :BF0000     |    61 |   976 |    18
   (0)| 00:00:01 |       |       |

|*  9 |         TABLE ACCESS FULL          | TIMES       |    61 |   976 |    18
   (0)| 00:00:01 |       |       |

|* 10 |        HASH JOIN                   |             | 48360 |  2030K|   945
   (2)| 00:00:01 |       |       |

|* 11 |         HASH JOIN                  |             |  2921 | 52578 |   426
   (1)| 00:00:01 |       |       |

|* 12 |          TABLE ACCESS FULL         | COUNTRIES   |     1 |     8 |     3
   (0)| 00:00:01 |       |       |

|  13 |          TABLE ACCESS FULL         | CUSTOMERS   | 55500 |   541K|   423
   (1)| 00:00:01 |       |       |

|  14 |         PARTITION RANGE JOIN-FILTER|             |   918K|    21M|   516
   (2)| 00:00:01 |:BF0000|:BF0000|

|  15 |          TABLE ACCESS FULL         | SALES       |   918K|    21M|   516
   (2)| 00:00:01 |:BF0000|:BF0000|

|* 16 |      INDEX UNIQUE SCAN             | PRODUCTS_PK |     1 |     4 |     0
   (0)| 00:00:01 |       |       |

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


Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access("SALES"."CHANNEL_ID"="CHANNELS"."CHANNEL_ID")
   7 - access("SALES"."TIME_ID"="TIMES"."TIME_ID")
   9 - filter("TIMES"."CALENDAR_MONTH_DESC"='2000-09' OR "TIMES"."CALENDAR_MONTH
_DESC"='2000-10')

  10 - access("SALES"."CUST_ID"="CUSTOMERS"."CUST_ID")
  11 - access("CUSTOMERS"."COUNTRY_ID"="COUNTRIES"."COUNTRY_ID")
  12 - filter("COUNTRY_ISO_CODE"='US')
  16 - access("SALES"."PROD_ID"="PRODUCTS"."PROD_ID")

Note
-----
   - this is an adaptive plan


Statistics
----------------------------------------------------------
       2097  recursive calls
          0  db block gets
       5391  consistent gets
       1875  physical reads
        308  redo size
        958  bytes sent via SQL*Net to client
         52  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
        138  sorts (memory)
          0  sorts (disk)
          3  rows processed

In-Memoryを使用した状態でクエリ実行

次にIn-Memoryを使用して同じクエリを再実行してみます。

-- バッファキャッシュのクリア
$ sqlplus sys@localhost:1521/xepdb1 as sysdba
SQL> ALTER SYSTEM FLUSH BUFFER_CACHE;
SQL> exit

-- SHスキーマの対象テーブルをIn-Memory領域にポピュレート
$ sqlplus sh@localhost:1521/xepdb1
SQL> alter table sales inmemory priority critical;
SQL> alter table products inmemory priority critical;
SQL> alter table customers inmemory priority critical;
SQL> alter table times inmemory priority critical;
SQL> alter table channels inmemory priority critical;
SQL> alter table countries inmemory priority critical;

-- 表がポピュレートされたことを確認
SQL> set pagesize 1000
SQL> set linesize 1000
SQL> col owner for a10
SQL> col segment_name for a20
SQL> SELECT v.owner, v.segment_name,
     v.bytes orig_size,
     v.inmemory_size in_mem_size,
     v.bytes / v.inmemory_size comp_ratio
     FROM v$im_segments v;

OWNER      SEGMENT_NAME          ORIG_SIZE IN_MEM_SIZE COMP_RATIO
---------- -------------------- ---------- ----------- ----------
SH         SALES                    876544     1310720     .66875
SH         SALES                    647168     1310720     .49375
SH         SALES                    868352     1310720      .6625
SH         SALES                    753664     1310720       .575
SH         SALES                    819200     1310720       .625
SH         CUSTOMERS              12427264     5505024 2.25744048
SH         SALES                    696320     1310720     .53125
SH         SALES                    999424     1310720      .7625
SH         SALES                    843776     1310720     .64375
SH         SALES                    868352     1310720      .6625
SH         SALES                    884736     1310720       .675
SH         SALES                    786432     1310720         .6
SH         SALES                    557056     1310720       .425
SH         SALES                    917504     1310720         .7
SH         SALES                    901120     1310720      .6875
SH         TIMES                    434176     1310720     .33125
SH         SALES                    770048     1310720      .5875
SH         SALES                    802816     1310720      .6125

-- 集計クエリ実行
SQL> SELECT channel_desc, TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$,
     RANK() OVER (ORDER BY SUM(amount_sold)) AS default_rank,
     RANK() OVER (ORDER BY SUM(amount_sold) DESC NULLS LAST) AS custom_rank
     FROM sh.sales, sh.products, sh.customers, sh.times, sh.channels, sh.countries
     WHERE sales.prod_id=products.prod_id AND sales.cust_id=customers.cust_id
     AND customers.country_id = countries.country_id AND sales.time_id=times.time_id
     AND sales.channel_id=channels.channel_id
     AND times.calendar_month_desc IN ('2000-09', '2000-10')
     AND country_iso_code='US'
     GROUP BY channel_desc;

Elapsed: 00:00:00.18

Execution Plan
----------------------------------------------------------
Plan hash value: 1538282445

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

| Id  | Operation                            | Name        | Rows  | Bytes | Cos
t (%CPU)| Time     | Pstart| Pstop |

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

|   0 | SELECT STATEMENT                     |             |     5 |   380 |
70  (29)| 00:00:01 |       |       |

|   1 |  WINDOW SORT                         |             |     5 |   380 |
70  (29)| 00:00:01 |       |       |

|   2 |   WINDOW SORT                        |             |     5 |   380 |
70  (29)| 00:00:01 |       |       |

|   3 |    HASH GROUP BY                     |             |     5 |   380 |
70  (29)| 00:00:01 |       |       |

|   4 |     NESTED LOOPS                     |             |  2016 |   149K|
67  (26)| 00:00:01 |       |       |

|*  5 |      HASH JOIN                       |             |  2016 |   141K|
67  (26)| 00:00:01 |       |       |

|   6 |       TABLE ACCESS INMEMORY FULL     | CHANNELS    |     5 |    65 |
 3   (0)| 00:00:01 |       |       |

|*  7 |       HASH JOIN                      |             |  2016 |   116K|
64  (27)| 00:00:01 |       |       |

|   8 |        JOIN FILTER CREATE            | :BF0001     |    61 |   976 |
 1   (0)| 00:00:01 |       |       |

|   9 |         PART JOIN FILTER CREATE      | :BF0000     |    61 |   976 |
 1   (0)| 00:00:01 |       |       |

|* 10 |          TABLE ACCESS INMEMORY FULL  | TIMES       |    61 |   976 |
 1   (0)| 00:00:01 |       |       |

|* 11 |        HASH JOIN                     |             | 48360 |  2030K|
62  (26)| 00:00:01 |       |       |

|  12 |         JOIN FILTER CREATE           | :BF0002     |  2921 | 52578 |
20   (5)| 00:00:01 |       |       |

|* 13 |          HASH JOIN                   |             |  2921 | 52578 |
20   (5)| 00:00:01 |       |       |

|  14 |           JOIN FILTER CREATE         | :BF0003     |  2921 | 52578 |
20   (5)| 00:00:01 |       |       |

|* 15 |            TABLE ACCESS INMEMORY FULL| COUNTRIES   |     1 |     8 |
 3   (0)| 00:00:01 |       |       |

|  16 |           JOIN FILTER USE            | :BF0003     | 55500 |   541K|
16   (0)| 00:00:01 |       |       |

|* 17 |            TABLE ACCESS INMEMORY FULL| CUSTOMERS   | 55500 |   541K|
16   (0)| 00:00:01 |       |       |

|  18 |         JOIN FILTER USE              | :BF0001     |   918K|    21M|
40  (33)| 00:00:01 |       |       |

|  19 |          JOIN FILTER USE             | :BF0002     |   918K|    21M|
40  (33)| 00:00:01 |       |       |

|  20 |           PARTITION RANGE JOIN-FILTER|             |   918K|    21M|
40  (33)| 00:00:01 |:BF0000|:BF0000|

|* 21 |            TABLE ACCESS INMEMORY FULL| SALES       |   918K|    21M|
40  (33)| 00:00:01 |:BF0000|:BF0000|

|* 22 |      INDEX UNIQUE SCAN               | PRODUCTS_PK |     1 |     4 |
 0   (0)| 00:00:01 |       |       |

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


Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access("SALES"."CHANNEL_ID"="CHANNELS"."CHANNEL_ID")
   7 - access("SALES"."TIME_ID"="TIMES"."TIME_ID")
  10 - inmemory("TIMES"."CALENDAR_MONTH_DESC"='2000-09' OR "TIMES"."CALENDAR_MON
TH_DESC"='2000-10')

       filter("TIMES"."CALENDAR_MONTH_DESC"='2000-09' OR "TIMES"."CALENDAR_MONTH
_DESC"='2000-10')

  11 - access("SALES"."CUST_ID"="CUSTOMERS"."CUST_ID")
  13 - access("CUSTOMERS"."COUNTRY_ID"="COUNTRIES"."COUNTRY_ID")
  15 - inmemory("COUNTRY_ISO_CODE"='US')
       filter("COUNTRY_ISO_CODE"='US')
  17 - inmemory(SYS_OP_BLOOM_FILTER(:BF0003,"CUSTOMERS"."COUNTRY_ID"))
       filter(SYS_OP_BLOOM_FILTER(:BF0003,"CUSTOMERS"."COUNTRY_ID"))
  21 - inmemory(SYS_OP_BLOOM_FILTER_LIST(SYS_OP_BLOOM_FILTER(:BF0002,"SALES"."CU
ST_ID"),SYS_OP_BLOOM_FILTER(

              :BF0001,"SALES"."TIME_ID")))
       filter(SYS_OP_BLOOM_FILTER_LIST(SYS_OP_BLOOM_FILTER(:BF0002,"SALES"."CUST
_ID"),SYS_OP_BLOOM_FILTER(:B

              F0001,"SALES"."TIME_ID")))
  22 - access("SALES"."PROD_ID"="PRODUCTS"."PROD_ID")

Note
-----
   - this is an adaptive plan


Statistics
----------------------------------------------------------
       2283  recursive calls
          0  db block gets
       3855  consistent gets
        135  physical reads
          0  redo size
        958  bytes sent via SQL*Net to client
         52  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
        149  sorts (memory)
          0  sorts (disk)
          3  rows processed

軽いクエリなので実行時間に大きな差は出ていませんが、「TABLE ACCESS INMEMORY FULL」などIn-Memoryを利用したことが実行計画に表れています。
また8行目などにブルーム・フィルタを活用している点が表れており、In-Memory未使用時と実行計画が変わっています。

このようにOracle Database 21c XEなら無料で、本来有償のオプション機能を試せるようになっています。
気になるオプション機能や新機能がありましたら、お気軽に使ってみてください!

Oracle Databaseのパッチについて解説したMOS Noteの紹介

Oracle Databaseのパッチについて詳しく解説したMOS Noteのご紹介です。
Oracle Database向けのパッチを良く知らないという方、知ってるけどPSR/PSUなど一昔前の情報に留まっている方は、以下のMOS Noteを読んでみてください。

Oracle データベース – データベース・パッチ提供方式の概要 – 12.2.0.1 以降 (Doc ID 2358633.1)

Oracle Database向けに提供される最近のパッチはリリース、リリース・アップデート(RU)、リリース・アップデート・リビジョン(RUR)など一昔前とは異なる形態になっています。
こうしたパッチについて、以下の観点で情報が整理されています。

  • Oracle Database向けパッチの種類
  • 各パッチに含まれる修正
  • 各パッチに対してOracleが実施しているテストの内容
  • 各パッチについてユーザが実施するテストの推奨事項

またパッチの前にまずは19cへアップグレードしなきゃ、という方はアップグレードのセミナーもありますので、是非こちらも参考にしてみてください。

待ったなし! 最新事情 Oracle Database 11g, 12c からのアップグレード対策

Oracle Database 19cを使用する際に留意すべき個別パッチや設定など

Oracle Database 19cを利用する上で、事前に確認しておくべき重要なドキュメントがMOSにいくつかありましたので、こちらの記事にまとめておこうと思います。

  1. Oracle Database 19c Important Recommended One-off Patches (Doc ID 555.1)
  2. Things to Consider to Avoid Database Performance Problems on 19c
  3. Things to Consider to Avoid SQL Performance Problems on 19c
  4. Things to Consider to Avoid Prominent Wrong Result Problems on 19C Proactively
  5. Things to Consider to Avoid SQL Plan Management (SPM) Related Problems on 19c

特に1.の記事には各RUごとに適用を検討すべき個別パッチの情報がまとめられています。
19cの導入を検討される際は1.のドキュメントを参照頂き、ご利用予定の機能に対応する重大な既知バグがないか、確認頂くことをお勧めします。

またOracle Databaseに関する継続的な情報収集という意味では、以下のブログをフォロー頂くと良いです。
Upgrade your Database – NOW!
こちらには各DBバージョンにおける重要な情報やバグ情報などが随時アップデートされています。

Oracle Database マルチテナント環境でPDBに割り当てるリソース設計の指針

Oracle Databaseは12cR1からマルチテナントというアーキテクチャを導入しました(マルチテナントの詳細についてはこちらのスライドをご参照ください)。
当初はPDBごとのリソースを細かく設定できず、share値という設定を利用した相対的なリソース配分のみ定められる仕様でした。
それが12cR2からCPU、メモリ、IOそれぞれについて細かく初期化パラメータで設定できるようになりました。
※PDB単位で設定できるリソース関連の初期化パラメータはこちらのマニュアルをご参照ください。

一方で細かく設定できるようになったがために、各PDBごとのリソース配分の考え方が複雑化したように思います(少なくとも私は混乱してしまいました…)。
そこで今回ご紹介するのは、My Oracle Support(MOS)でドキュメントとして公開されている以下の参考情報です。
How to Provision PDBs, based on CPU_COUNT (Doc ID 2326708.1)
上記ドキュメントに書かれている内容の要点を以下にまとめてみました。

PDBごとのリソース配分

まず以下の計算式で「PDB sizing factor」と呼ばれる係数を決めます。

PDB sizing factor = PDB’s CPU_COUNT / CDB’s CPU_COUNT

次に上記係数を用い、以下のようにPDBごとの主要な初期化パラメータの値を計算します。

CDB Value x PDB Sizing Factor = PDB Value

要は CPU_COUNT に比例する形でPDBにリソースを割り当てる、という考え方です。
なお上記計算式で求められる初期化パラメータは以下が対象となります。

  • CPU_COUNT
  • SGA_TARGET
  • PGA_AGGREGATE_TARGET
  • PGA_AGGREGATE_LIMIT
  • SESSIONS

MOSドキュメントにも記載されていますが、上記以外のパラメータ(例えば SGA_MIN_SIZE など)は原則として必要に迫られない限り、設定しないように推奨されています。
もし SGA_MIN_SIZE など要注意とされている初期化パラメータを利用する際は、ドキュメントに書かれている内容を必ずご確認ください。

CDBの統合密度

ドキュメントでは「consolidation density」としてCDBの統合密度についても触れられています。
consolidation densityは以下の計算式で求められます。

consolidation density = sum(PDB’s CPU_COUNT) / CDB’s CPU_COUNT

値が1以下の場合はオーバーサブスクリプションされていないと言えます。
逆に1より大きい場合はオーバーサブスクリプション状態です。
例えば、もし本番環境などいずれのPDBも常時一定の負荷が掛かる環境であれば、1以下にするべきでしょう。
開発環境で多くの時間、だいたいのPDBがアイドル状態といった環境であれば2や3といったconsolidation densityで問題ないと考えられます。

以上、12cR2以降のOracle Databaseでマルチテナントをご利用の方は、設計に際してこれらの指標を参考にしてみてはいかがでしょうか。

Oracle Database 21c Blockchain Tableを試してみる

Oracle Database 21cに追加された新機能「Blockchain Table」はお馴染みRDBのOracle Database上で、手軽にBlockchain技術を活用できる新機能になります。
Blockchain Tableとして表を作成すれば、Insertのみ実行可能(Delete、Drop、Update、Alterは不可)な表を作れます。
さらにデータ改ざんされていないことを証明するHashチェーンが、付加情報として表に付与されます。
こうした特徴から台帳や監査ログなど、改ざんから守りつつ長期間保存したいデータの格納に活用できます。
ちなみに「Blockchain Table」は19cにもBackportされており、「RU 19.10」かつ「パッチ 32431413」を適用することで利用できます。

今回はこの「Blockchain Table」を試してみたいと思います。
環境はAutonomous Database(ATP)になります。

早速Blockchain Tableを作成します。

SQL> CREATE BLOCKCHAIN TABLE tsttab (employee_id NUMBER, salary NUMBER)
  2                       NO DROP UNTIL 31 DAYS IDLE
  3                       NO DELETE LOCKED
  4                       HASHING USING "SHA2_512" VERSION "v1";

Table created.

上記create文に記載した以下の句はBlockchain Table作成時に必須となります。

  • NO DROP
  • NO DELETE
  • HASHING USING
  • VERSION

作成したBlockchain Tableの情報は「user_blockchain_tables」ディクショナリから確認できます。

SQL> SELECT row_retention, row_retention_locked,
  2                       table_inactivity_retention, hash_algorithm
  3                FROM   user_blockchain_tables
  4                WHERE  table_name='TSTTAB';

ROW_RETENTION ROW TABLE_INACTIVITY_RETENTION HASH_ALG
------------- --- -------------------------- --------
              YES                         31 SHA2_512

なお表のdescription上は出力されませんが、「user_tab_cols」で確認するとBlockchain Table専用のカラムがあるのを確認できます。

SQL> desc tsttab
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 EMPLOYEE_ID                                        NUMBER
 SALARY                                             NUMBER

SQL> COL "Data Length" FORMAT 9999
SQL> COL "Column Name" FORMAT A24
SQL> COL "Data Type" FORMAT A28
SQL> SELECT internal_column_id "Col ID", SUBSTR(column_name,1,30) "Column Name", 
                     SUBSTR(data_type,1,30) "Data Type", data_length "Data Length"
              FROM   user_tab_cols       
              WHERE  table_name = 'TSTTAB' ORDER BY internal_column_id;

    Col ID Column Name              Data Type                    Data Length
---------- ------------------------ ---------------------------- -----------
         1 EMPLOYEE_ID              NUMBER                                22
         2 SALARY                   NUMBER                                22
         3 ORABCTAB_INST_ID$        NUMBER                                22
         4 ORABCTAB_CHAIN_ID$       NUMBER                                22
         5 ORABCTAB_SEQ_NUM$        NUMBER                                22
         6 ORABCTAB_CREATION_TIME$  TIMESTAMP(6) WITH TIME ZONE           13
         7 ORABCTAB_USER_NUMBER$    NUMBER                                22
         8 ORABCTAB_HASH$           RAW                                 2000
         9 ORABCTAB_SIGNATURE$      RAW                                 2000
        10 ORABCTAB_SIGNATURE_ALG$  NUMBER                                22
        11 ORABCTAB_SIGNATURE_CERT$ RAW                                   16
        12 ORABCTAB_SPARE$          RAW                                 2000

12 rows selected.

表にInsertしてみます。

SQL> INSERT INTO tsttab VALUES (106,12000);

1 row created.

SQL> commit;

Commit complete.

Insert結果を、Blockchain Table内部のデータも含めて出力してみます。

SQL> COL "Chain date" FORMAT A17
SQL> COL "Chain ID" FORMAT 99999999
SQL> COL "Seq Num" FORMAT 99999999
SQL> COL "User Num" FORMAT 9999999
SQL> COL "Chain HASH" FORMAT 99999999999999
SQL> SELECT ORABCTAB_CHAIN_ID$ "Chain ID", ORABCTAB_SEQ_NUM$ "Seq Num",
            to_char(ORABCTAB_CREATION_TIME$,'dd-Mon-YYYY hh-mi') "Chain date",
            ORABCTAB_USER_NUMBER$ "User Num", ORABCTAB_HASH$ "Chain HASH"
     FROM   tsttab;

 Chain ID   Seq Num Chain date        User Num Chain HASH
--------- --------- ----------------- -------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        4         1 16-Mar-2021 06-49       85 8FFE762842479B76716C1FED013536F78087CD134F07876D8A28A55681C2FBDFF5F0CF67E89C3D492F0241D5EF7BF066A11812BD4CB687A95C8B427DFE195550

上記の通り、各レコードごとにハッシュ値が付与されます。
このハッシュ値は当該レコードの内容と、その前後のレコード内容をもとに算出されますので、もし何かしら変更を加えられるとハッシュ値は異なってきます。
そのため最初に埋め込まれたこのハッシュ値と整合性がとれた状態であれば、改ざんされていないことの証明となります。

次にユーザを変えてInsertしてみます。

SQL> GRANT insert ON tsttab TO tstusr;

Grant succeeded.

SQL> conn tstusr@adb21c_low
Connected.

SQL> INSERT INTO  admin.tsttab VALUES (106,24000);

1 row created.

SQL> commit;

Commit complete.

元のユーザに戻ってBlockchain Table内部のデータも含めて出力してみます。

SQL> conn admin@adb21c_low
Enter password:
Connected.

SQL> SELECT ORABCTAB_CHAIN_ID$ "Chain ID", ORABCTAB_SEQ_NUM$ "Seq Num",
  2         to_char(ORABCTAB_CREATION_TIME$,'dd-Mon-YYYY hh-mi') "Chain date",
  3         ORABCTAB_USER_NUMBER$ "User Num", ORABCTAB_HASH$ "Chain HASH"
  4  FROM   tsttab;

 Chain ID   Seq Num Chain date        User Num Chain HASH
--------- --------- ----------------- -------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        0         1 16-Mar-2021 06-59      148 CF1C6069C2DEC779A19FCF87B8B8509EA00C2BDA6C360FAB01C03DDFA141352146B33EE0FB47586CE7E52766449CCB8D6EE863F3E28016184C7D611D473C06D4
        4         1 16-Mar-2021 06-49       85 8FFE762842479B76716C1FED013536F78087CD134F07876D8A28A55681C2FBDFF5F0CF67E89C3D492F0241D5EF7BF066A11812BD4CB687A95C8B427DFE195550

User Numberの異なるレコードが追加されているのを確認できます。
Insertの次はDelete、Update、Truncate文を試してみます。

SQL> -- DELETE
SQL> DELETE FROM tsttab WHERE EMPLOYEE_ID = 106;
DELETE FROM tsttab WHERE EMPLOYEE_ID = 106
            *
ERROR at line 1:
ORA-05715: operation not allowed on the blockchain table

SQL> -- UPDATE
SQL> UPDATE tsttab SET SALARY = 32000 WHERE EMPLOYEE_ID = 106;
UPDATE tsttab SET SALARY = 32000 WHERE EMPLOYEE_ID = 106
       *
ERROR at line 1:
ORA-05715: operation not allowed on the blockchain table

SQL> -- TRUNCATE
SQL> TRUNCATE TABLE tsttab;
TRUNCATE TABLE tsttab
               *
ERROR at line 1:
ORA-05715: operation not allowed on the blockchain table

上記の通り一度Insertしたデータを改変する操作はすべて不可となっています。
ちなみにDeleteについてはX日後に削除可能とする設定もあり、その場合は以下のように「DBMS_BLOCKCHAIN_TABLE」パッケージを使って保存期限の切れたレコードを削除します。

SQL> SET SERVEROUTPUT ON
SQL> DECLARE
   NUMBER_ROWS NUMBER;
BEGIN
   DBMS_BLOCKCHAIN_TABLE.DELETE_EXPIRED_ROWS('ADMIN','TSTTAB', null, NUMBER_ROWS);
   DBMS_OUTPUT.PUT_LINE('Number of rows deleted=' || NUMBER_ROWS);
END;
/    2    3    4    5    6    7
Number of rows deleted=0

PL/SQL procedure successfully completed.

Alter文も試してみます。

SQL> ALTER TABLE tsttab NO DELETE UNTIL 15 DAYS AFTER INSERT;
ALTER TABLE tsttab NO DELETE UNTIL 15 DAYS AFTER INSERT
*
ERROR at line 1:
ORA-05731: blockchain table TSTTAB cannot be altered

こちらも弾かれていますが、これは表作成時に「NO DELETE LOCKED(レコード削除不可)」を指定したためです。
Drop文も試してみます。

SQL> DROP TABLE tsttab;
DROP TABLE tsttab
           *
ERROR at line 1:
ORA-05723: drop blockchain table TSTTAB not allowed

Drop文が弾かれているのは表作成時に「NO DROP UNTIL 31 DAYS IDLE」を指定しているためです。
今回のtsttab表については、31日のあいだ新規レコードのInsertがない場合に限りDrop可能です。

最後にInsertされた各レコードの整合性チェックをやってみます。
その際は「DBMS_BLOCKCHAIN_TABLE.VERIFY_ROWS」プロシージャを使います。

SQL> SET SERVEROUTPUT ON
SQL> DECLARE
   row_count NUMBER;
   verify_rows NUMBER;
   instance_id NUMBER;
BEGIN
  FOR instance_id IN 1 .. 2 LOOP
    SELECT COUNT(*) INTO row_count FROM admin.tsttab WHERE ORABCTAB_INST_ID$=instance_id;
    DBMS_BLOCKCHAIN_TABLE.VERIFY_ROWS('ADMIN','TSTTAB', NULL, NULL, instance_id, NULL, verify_rows);
    DBMS_OUTPUT.PUT_LINE('Number of rows verified in instance Id '|| instance_id || ' = '|| row_count);
  END LOOP;
END;
/
Number of rows verified in instance Id 1 = 2
Number of rows verified in instance Id 2 = 0

PL/SQL procedure successfully completed.

以上の通り、使い慣れたOracle Database上で気軽にBlockchain Tableを利用できました。
データに対する強力な耐改ざん性を、コストを掛けずに実現したい場合、Oracle DatabaseのBlockchain Tableが非常に有効ではないでしょうか。

Oracle Database 21c バイナリJSONデータタイプを試してみる

Oracle Database 21cから追加されたバイナリJSONデータタイプを試してみました。
なお環境はOCI上にプロビジョニングしたAutonomous Database(ATP)を使いました。
今回試したコマンドは以下マニュアルを参考にしています。
4 Creating a Table With a JSON Column

po_documentの列型に「JSON」を指定しテーブルを作成します。

SQL> CREATE TABLE j_purchaseorder
  2    (id          VARCHAR2 (32) NOT NULL PRIMARY KEY,
  3     date_loaded TIMESTAMP (6) WITH TIME ZONE,
  4     po_document JSON);

表が作成されました。

ここでテーブルの構成情報を確認してみます。

SQL> desc j_purchaseorder
 名前                                      NULL?    型
 ----------------------------------------- -------- ----------------------------
 ID                                        NOT NULL VARCHAR2(32)
 DATE_LOADED                                        TIMESTAMP(6) WITH TIME ZONE
 PO_DOCUMENT                                        UNDEFINED

ご覧の通りJSON型の列は「UNDEFINED」と表記されており、これだけだと型が分かりません。
JSON型かどうかを確認する際は「XXX_JSON_COLUMNS」ディクショナリを使います。
以下のように、どのテーブルのどの列がJSON型か教えてくれます。

SQL> desc USER_JSON_COLUMNS
 名前                                      NULL?    型
 ----------------------------------------- -------- ----------------------------
 TABLE_NAME                                         VARCHAR2(128)
 OBJECT_TYPE                                        VARCHAR2(5)
 COLUMN_NAME                                        VARCHAR2(128)
 FORMAT                                             VARCHAR2(9)
 DATA_TYPE                                          VARCHAR2(13)

SQL> select table_name, column_name from USER_JSON_COLUMNS;

TABLE_NAME
--------------------------------------------------------------------------------
COLUMN_NAME
--------------------------------------------------------------------------------
J_PURCHASEORDER
PO_DOCUMENT

次にデータを2つ挿入します。
このとき列「po_document」にはJSON形式のデータを指定します。

SQL> INSERT INTO j_purchaseorder
  2    VALUES (
  3      SYS_GUID(),
  4      to_date('2014-12-30'),
  5      '{"PONumber"             : 1600,
  6        "Reference"            : "ABULL-20140421",
  7        "Requestor"            : "Alexis Bull",
  8        "User"                 : "ABULL",
  9        "CostCenter"           : "A50",
 10        "ShippingInstructions" :
 11          {"name"    : "Alexis Bull",
 12           "Address" : {"street"  : "200 Sporting Green",
 13                        "city"    : "South San Francisco",
 14                        "state"   : "CA",
 15                        "zipCode" : 99236,
 16                        "country" : "United States of America"},
 17           "Phone"   : [{"type" : "Office", "number" : "909-555-7307"},
 18                        {"type" : "Mobile", "number" : "415-555-1234"}]},
 19        "Special Instructions" : null,
 20        "AllowPartialShipment" : true,
 21        "LineItems"            :
 22          [{"ItemNumber" : 1,
 23            "Part"       : {"Description" : "One Magic Christmas",
 24                            "UnitPrice"   : 19.95,
 25                            "UPCCode"     : 13131092899},
 26            "Quantity"   : 9.0},
 27           {"ItemNumber" : 2,
 28            "Part"       : {"Description" : "Lethal Weapon",
 29                            "UnitPrice"   : 19.95,
 30                            "UPCCode"     : 85391628927},
 31            "Quantity"   : 5.0}]}');

1行が作成されました。

SQL> INSERT INTO j_purchaseorder
  2    VALUES (
  3      SYS_GUID(),
  4      to_date('2014-12-30'),
  5      '{"PONumber"             : 672,
  6        "Reference"            : "SBELL-20141017",
  7        "Requestor"            : "Sarah Bell",
  8        "User"                 : "SBELL",
  9        "CostCenter"           : "A50",
 10        "ShippingInstructions" : {"name"    : "Sarah Bell",
 11                                  "Address" : {"street"  : "200 Sporting Green",
 12                                               "city"    : "South San Francisco",
 13                                               "state"   : "CA",
 14                                               "zipCode" : 99236,
 15                                               "country" : "United States of America"},
 16                                  "Phone"   : "983-555-6509"},
 17        "Special Instructions" : "Courier",
 18        "LineItems"            :
 19          [{"ItemNumber" : 1,
 20            "Part"       : {"Description" : "Making the Grade",
 21                            "UnitPrice"   : 20,
 22                            "UPCCode"     : 27616867759},
 23            "Quantity"   : 8.0},
 24           {"ItemNumber" : 2,
 25            "Part"       : {"Description" : "Nixon",
 26                            "UnitPrice"   : 19.95,
 27                            "UPCCode"     : 717951002396},
 28            "Quantity"   : 5},
 29           {"ItemNumber" : 3,
 30            "Part"       : {"Description" : "Eric Clapton: Best Of 1981-1999",
 31                            "UnitPrice"   : 19.95,
 32                            "UPCCode"     : 75993851120},
 33            "Quantity"   : 5.0}]}');

1行が作成されました。

では本当にリレーショナルデータとJSON形式のデータを混在させた状態で問い合わせできるか試してみます。

SQL> select * from j_purchaseorder;

ID
----------------------------------------------------------------
DATE_LOADED
---------------------------------------------------------------------------
PO_DOCUMENT
--------------------------------------------------------------------------------
BC0289837E77882AE0533E11000A64AC
14-12-30 00:00:00.000000 +09:00
7B22504F4E756D626572223A313630302C225265666572656E6365223A224142554C4C2D32303134
30343231222C22526571756573746F72223A22416C657869732042756C6C222C2255736572223A22

BC0289837E78882AE0533E11000A64AC
14-12-30 00:00:00.000000 +09:00

ID
----------------------------------------------------------------
DATE_LOADED
---------------------------------------------------------------------------
PO_DOCUMENT
--------------------------------------------------------------------------------
7B22504F4E756D626572223A3637322C225265666572656E6365223A225342454C4C2D3230313431
303137222C22526571756573746F72223A2253617261682042656C6C222C2255736572223A225342

このようにJSONデータタイプをそのままselectすると、JSON型のインスタンスとして出力されます。
JSON内の値を読める形で出力させるには、JSONデータに対してドットを付けて特定の値を問い合わせます。
詳しくは以下マニュアルを参照ください。
14 Simple Dot-Notation Access to JSON Data

SQL> set lines 1000
SQL> set pages 9999
SQL> col id for a40
SQL> col date_loaded for a40
SQL> col Reference for a30
SQL> 
SQL> select id, date_loaded, po.po_document.PONumber.number() PONumber, po.po_document.Reference.string() Reference
  2      from j_purchaseorder po
  3      where po.po_document.PONumber.number() = 672;

ID                                       DATE_LOADED                                PONUMBER REFERENCE
---------------------------------------- ---------------------------------------- ---------- ------------------------------
BC0289837E78882AE0533E11000A64AC         14-12-30 00:00:00.000000 +09:00                 672 SBELL-20141017

なるほど。確かにリレーショナルデータとJSONデータが混在していても問い合わせ結果が返ってきました。
SQLの記法は少し慣れが必要そうです。
では次にJSONデータを更新してみます。
更新操作は21cから新しく追加された「json_transform」を使います。

SQL> UPDATE j_purchaseorder po SET po_document =
  2    json_transform(po_document, SET '$.CostCenter' = 'C20')
  3    where po.po_document.PONumber.number() = 672;

1行が更新されました。

PONumberが「672」のレコードのCostCenterを更新しました。
更新内容を確認してみます。

SQL> col CostCenter for a30
SQL> select po.po_document.PONumber.number() PONumber, po.po_document.CostCenter.string() CostCenter from j_purchaseorder po;

  PONUMBER COSTCENTER
---------- ------------------------------
      1600 A50
       672 C20

確かに更新が反映されてますね。
json_transformには更新意外にも追加や削除などの機能もありますので、詳しくは以下マニュアルを参照ください。
11 Oracle SQL Function JSON_TRANSFORM

以上、簡単ですがバイナリJSONデータタイプを試してみました。
21cはConverged Databaseというコンセプトで様々なデータタイプや機能がOracle Databaseに集約されていますが、実際のところ集約することでどれだけ便利になるのか、色々試して確認したいところです。

Oracle Database 21cで気になった新機能

つい先日Oracle Database 21cがCloud FirstでGAになりました!
ということで個人的に気になった新機能と非サポート/非推奨機能をまとめてみました。

JavaScript Execution using DBMS_MLE

Oracle Database内でJavaScriptのコードを実装して実行できるようになりました。
JavaScriptからSQLやPL/SQLを呼び出せるそうです。
特にWebエンジニアにとってうれしい機能ですかね。

Expression Support for Initialization Parameters

初期化パラメータの値として式を使えるようになりました。
こんな感じで使えるそうです。

ALTER SYSTEM SET sga_target = 'sga_max_size*80/100';
ALTER SYSTEM SET db_recovery_file_dest='$HOME' SCOPE=BOTH;

クラウドのようにVMのスペックが動的にスケールする環境で便利そうです。

SQL Macros

SQL Macros(SQM)は色々なSQLで再利用可能な関数を定義する機能です。
以下はマニュアルの事例です。

SQL> CREATE OR REPLACE FUNCTION concat_self(str varchar2, cnt pls_integer)
            RETURN VARCHAR2 SQL_MACRO(SCALAR) 
      IS BEGIN
            RETURN 'rpad(str, cnt * length(str), str)';
     END;
     /

SQL> SELECT last_name, concat_self(last_name,2) FROM hr.employees;

SQLをより分かりやすい形で記述できそうです。

Oracle Machine Learning for Python (OML4Py)

「OML4Py」はOracle Databaseが提供するMachine Learning機能を、Pythonから呼び出すためのモジュールです。
特に21cから追加された「AutoML」が個人的に気になってます。
AutoMLといえばDataRobotなどが有名ですね。
しかもMachine Learningは追加オプション不要なので、気軽に試せます。

Oracle Data Pump Supports Export to and Import From Cloud Object Stores

クラウド上のオブジェクト・ストレージを使ったexport/importが可能になりました。
Oracle CloudはSwift互換があるので、おそらくS3とも連携できそうです。
ローカルディスク利用時より処理速度は落ちると思いますが、クラウド移行の際は使えそうです。

Enhanced Diagnosability of Oracle Database

新しいログファイルとして「attentionログ」が用意されました。
こちらにはDBAにとって重要なログ情報だけが出力されるそうです。
またフォーマットは文字列処理をしやすいJSON形式です。
以下はマニュアルに掲載されている例です。

{
IMMEDIATE : "PMON (ospid: 3565): terminating the instance due to ORA error 822" 
CAUSE: "PMON detected fatal background process death"
ACTION: "Termination of fatal background is not recommended, Investigate cause of process termination"
CLASS : CDB-INSTANCE / CDB_ADMIN / ERROR / DBAL-35782660
TIME : 2020-03-28T14:15:16.159-07:00
INFO : "Some additional data on error PMON error"
}

従来のalertログやtraceファイルだと重要情報の抽出や文字列処理に限界があったのかもしれませんね。。

Persistent Memory Database

Persistent Memory Database (PMEM) 上へのデータファイル配置に対応しました。
PMEMを使用するとデータをバッファキャッシュにコピーすることなく、PMEMと直接やりとりするようです。
本機能がエンジニアド・システムやOCI以外でも使えるのかは要確認です。

Read-Only Oracle Home Default

Oracle HomeがデフォルトでRead-Onlyになりました。
それに伴い設定ファイルやログファイルはOracle Homeの外に配置される、とあるので、初期化パラメータや諸々のログファイルの初期配置が変わっていそうです。
運用スクリプトなどに対するケアが必要そうですね。
それにしてもOracle HomeをRead-Onlyにして他サーバで再利用するってあるんでしょうか。。

Automatic Materialized Views

ワークロードのモニタリング結果から自動でマテビュを作成、メンテする機能が追加されました。
自動索引に加えてマテビュも自動化されましたね。
こちらもエンジニアド・システムやOCI以外でも使えるのかは要確認です。

Clusterware REST API

REST API経由でクラスタ管理できるようになりました。
サーバログインせずに管理できるなら、sshまわりのセキュリティなど考慮不要になるので、運用が簡素化されそうです。
ただOnPというよりは主にクラウドを意識した機能追加ですね。

Details: Oracle Clusterware 21c Deprecated and Desupported Features

ポリシー管理データベースやドメイン・サービス・クラスターが非推奨になりました。
またメンバー・クラスターは非サポートになりました。
12cR2新機能でクラスター・ドメインについて初めて聞いたときは、いかにも利用しなさそうな印象でした。。
ポリシー管理データベースもOracle Masterのプラチナ試験で勉強した記憶はありますが、実環境で使っているのは見たことないですね。。

Support Per-PDB Capture for Oracle Autonomous Database

GoldenGateでPDBからキャプチャする際に、これまではCDBも意識して共通オブジェクトを作成していたのが、21cからPDB内で完結するようになったようです。

Pluggable Database Cluster Resources

PDBが遂にクラスタ・リソースに追加されました!
これでDBログインすることなく srvctl コマンドでPDB管理ができます。
以下はマニュアルにあったコマンド例です。

## PDB起動
$ srvctl start pdb -db db_name -pdb pdb_name [-startoption start_options]

## PDBと関連するサービスを全ノードでIMMEDIATEオプション指定で停止
$ srvctl stop pdb -db db_name -pdb pdb_name -stopoption IMMEDIATE -drain_timeout 0 -stopsvcoption IMMEDIATE

さいごに

気になる新機能はやはり実際に使ってみたくなりますね。
どこかでOCI上に21cをデプロイして色々試したいです。

参考資料

Oracleのエラー内容の詳細と解決策をコンソールですぐに確認する

「ORA-XXXXX」などOracle DBのエラーに遭遇したときの
初動として使えるコマンドの紹介です。
「oerr」コマンドを使えば外部インターネットが使えない環境でも、
エラー原因と解決策を簡単に確認できます。
使用法は以下のように「ファシリティ識別子」と「エラーコード」を
oerrコマンドの引数として指定するだけです。

## ORA-04031を調べるとき
[oracle@localhost ~]$ oerr ora 04031
04031, 00000, "unable to allocate %s bytes of shared memory (\"%s\",\"%s\",\"%s\",\"%s\")"
// *Cause:  More shared memory is needed than was allocated in the shared
//          pool or Streams pool.
// *Action: If the shared pool is out of memory, either use the
//          DBMS_SHARED_POOL package to pin large packages,
//          reduce your use of shared memory, or increase the amount of
//          available shared memory by increasing the value of the
//          initialization parameters SHARED_POOL_RESERVED_SIZE and 
//          SHARED_POOL_SIZE.
//          If the large pool is out of memory, increase the initialization
//          parameter LARGE_POOL_SIZE.  
//          If the error is issued from an Oracle Streams or XStream process, 
//          increase the initialization parameter STREAMS_POOL_SIZE or increase
//          the capture or apply parameter MAX_SGA_SIZE.

## IMP-00004を調べるとき
[oracle@localhost ~]$ oerr imp 00004
00004, 00000, "invalid username or password\n"
// *Cause:  An invalid username or password was specified. 
// *Action: Retry with a valid username and password.

ご覧の通り「Cause」がエラー原因、「Action」が解決策になります。
oerrコマンドを使えば調査の手間を少しでも省けそうですね。
なお表示言語は英語のみのようです(Oracleで使われる英語なら
辞書がなくても読めるはず…)。

Oracle DBお役立ちSQLまとめサイトのリンク集

個人的に入用だったのでまとめてみました。

よく使うOracleコマンド
・DBAとして必要な基本コマンドが満遍なくまとめられています

Oracle管理者のためのSQLリファレンス
・参照系というよりは構成変更のコマンドがまとめられています
・10gベースのため、より新しいバージョンで利用する際は
 マニュアルも要参照です

ORACLE スクリプト集
・よく使うものから稀に必要なものまで幅広く、かつ詳しくまとめられています

Oracle:よく使うSQL/コマンドまとめ
・exp/impやパフォーマンス調査といった
 特定のケースに対応するコマンドがまとめられています

Oracle Autonomous Transaction Processingにデータをインポートしてみた

前回はAutonomous Transaction Processing (ATP) のインスタンスを作成し、
Oracle ClientからDBに接続してみました。
今回は作成したインスタンスへData Pumpでデータを入れてみたいと思います。

データのエクスポート

今回はエクスポート済みのダンプファイルを利用したので、
実行手順は割愛します。
エクスポート時のコマンドは下記をご参照ください。
Export Your Existing Oracle Database to Import into Autonomous Transaction Processing

確認頂くと分かりますが、
通常のData Pumpの使い方でクスポートしています。

ダンプファイルのアップロード

エクスポートしたダンプファイルをObject Storageへアップロードします。
まずはObjectファイルを格納するためのBucketを作成します。
サイドメニューから”Object Storage”→”Object Storage”を選択します。

01_start_create_bucket

“Create Bucket”を押下します。

02_create_bucket

Bucket名やタイプを入力し、”Create Bucket”を押下します。

03_input_bucket_detail

以下のように作成したBucketを確認できますので、
作成したBucket名のリンクを押下して詳細画面へ行きます。

04_bucket_list

“Upload Object”を押下してアップロード画面へ遷移します。

05_start_ul

エクスポートしたダンプファイルを選択し、”Upload Object”を押下します。

06_select_ul_file

以下のようにObjectリストに表示されますので、
右のメニューアイコンから”Create Pre-Authenticated Request”を押下します。
上記により認証情報を新規作成し、
Object Storageへアクセスできるようにします。

07_create_auth

認証名や認証対象、アクセスタイプを選択し
“Create Pre-Authenticated Request”を押下します。

08_set_auth

すると以下のようにアクセスURLが発行されますので、
そのURLをメモしておきます。

09_check_auth_code

CredentialとDBユーザの作成

ATPからObject StorageへアクセスするにはCredentialが必要です。
ATPへ接続した状態で以下のように専用のプロシージャを実行し、
Credentialを作成します。

BEGIN
  DBMS_CLOUD.CREATE_CREDENTIAL(
    credential_name => 'ATP_TEST',
    username => 'your user name',
    password => 'user password'
  );
END;
/

また必要に応じてインポートに必要なユーザを用意します。
今回はエクスポートしたデータの関係から、
以下のように”apps”ユーザを作成しました。

create user apps identified by OraTestUser#1;
grant create session to apps;
grant create table to apps;

データのインポート

クライアントからData Pumpを使ってインポートを実行します。
コマンドの内容は下記を参考にしました。

Import Data Using Oracle Data Pump Version 18.3 or Later

impdp admin/xxxxxx@wisteriaatp_low \
  directory=data_pump_dir \
  credential=ATP_TEST \
  dumpfile=https://objectstorage.eu-frankfurt-1.oraclecloud.com/p/xxxxxxx/n/wisteria22/b/atp_test/o/atp_test.dmp \
  parallel=16 \
  transform=segment_attributes:n \
  transform=dwcs_cvt_iots:y \
  transform=constraint_use_default_index:y \
  exclude=cluster,db_link

ATPに”LOW”で接続し、先程作成したCredentialを指定しています。
また”dumpfile”パラメータにはメモしておいたURLを記載しています。

なおATPへのインポートで使用するData Pumpですが、
Credentialの入力に対応しているのは
“12.2.0.1”より新しいバージョンとなります。
“12.2.0.1”以下のバージョンでインポートしたい場合は
下記マニュアルを参照ください。

Import Data Using Oracle Data Pump (Versions 12.2.0.1 and Earlier)