Oracle 23ai 新機能 – JSONリレーショナル二面性ビュー[ORDS によるデータの変更編]

こんにちは。AIです。(Artificial Intelligence ではありません)

最近はブルーベリーにハマっており、先週 2 パック買って消費してしまいました。。食べすぎといわれても致し方なし。。
ちなみにカナダのスーパーでは、たくさんの種類の生のベリーが並んでおり、手軽にみずみずしいベリーを楽しむことができます。
初夏にはブルーベリー狩りもできますので、カナダに訪れた際はぜひ体験してみてください。

さて、前回の 23ai 新機能の記事では JSONリレーショナル二面性ビューの概要や作成方法についてお伝えしましたが、今回は ORDS を使用したデータの取得や更新について検証していきます!

■事前準備

まずは、今回使用する JSONリレーショナル二面性ビューを作成します。
今回の元表は hr.employees 表となります。

SQL> CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW employees_dv AS
  2    SELECT JSON {'_id' IS e.employee_id,
  3                 'first_name'   IS e.first_name,
  4                 'last_name'   IS e.last_name,
  5                 'email'   IS e.email,
  6                 'phone_number' IS e.phone_number
  7                 } FROM employees e WITH INSERT UPDATE DELETE;

View created.

このビューに対して ORDS からアクセスできるよう、ORDS.ENABLE_OBJECT を実行します。

SQL> BEGIN
  2    ORDS_METADATA.ORDS.ENABLE_OBJECT(
  3        p_enabled => TRUE, 
  4        p_schema => 'HR',
  5        p_object => 'EMPLOYEES_DV',
  6        p_object_type => 'VIEW',
  7        p_object_alias => 'employees_dv',
  8        p_auto_rest_auth => FALSE);
  9      COMMIT; 
 10  END;
 11  /

PL/SQL procedure successfully completed.

■ORDS を使用してデータを挿入

それでは早速、ORDS を使用してテーブルのデータを変更してみましょう。
※リクエストは curl コマンドを使用します。

データを変更する際の SQL コマンドと、HTTP リクエストメソッドはそれぞれ下記のように対応しています。

INSERT = POST
UPDATE = PUT
DELETE = DELETE

まずは POST をリクエストしてデータを挿入します。
なお今回の記事で指定する URL については以下のような構造となっています。

プロトコル : http
ホスト名 : localhost:8080
コンテキスト・ルート : /ords
リクエスト・パス : /スキーマ名/オブジェクト名 or /スキーマ名/オブジェクト名/_id

[oracle@localhost ~]$ curl --request POST \
>   --url http://localhost:8080/ords/hr/employees_dv/ \
>   --header 'Content-Type: application/json' \
>   --data '{"first_name" : "Ryunosuke",
>            "last_name" : "Akutagawa",
>            "email"   : "RAKUTAGAWA",
>            "hire_date"   : "2013-06-17T00:00:00",
>            "job_id"   : "ST_MAN"
>            }'
{"_id":208★,"first_name":"Ryunosuke","last_name":"Akutagawa","email":"RAKUTAGAWA","hire_date":"2013-06-17T00:00:00","job_id":"ST_MAN","_metadata":{"etag":"2679E2E75FDE0E35B77BC65314DBC942","asof":"0000000000D0C930"},"links":[{"rel":"self","href":"http://localhost:8080/ords/hr/employees_dv/208"},{"rel":"describedby","href":"http://localhost:8080/ords/hr/metadata-catalog/employees_dv/item"},{"rel":"collection","href":"http://localhost:8080/ords/hr/employees_dv/"}]}

今回は、_id を指定せずにデータを挿入したため自動的に採番されましたが、明示的に指定して挿入することも可能です。

それでは、早速 POST したデータを確認してみましょう!
ORDS から JSON リレーショナル二面性ビューの特定のデータを確認したい場合は _id の値を URL の末尾に入れます。

[oracle@localhost ~]$ curl --location http://localhost:8080/ords/hr/employees_dv/208

{"_id":208,"first_name":"Ryunosuke","last_name":"Akutagawa","email":"RAKUTAGAWA","hire_date":"2013-06-17T00:00:00","job_id":"ST_MAN","_metadata":{"etag":"2679E2E75FDE0E35B77BC65314DBC942","asof":"0000000000E02EA8"},"links":[{"rel":"self","href":"http://localhost:8080/ords/hr/employees_dv/208"},{"rel":"describedby","href":"http://localhost:8080/ords/hr/metadata-catalog/employees_dv/item"},{"rel":"collection","href":"http://localhost:8080/ords/hr/employees_dv/"}]}[oracle@localhost ~]$

更に JSON リレーショナル二面性ビューと実際の表も確認してみます。

SQL> SELECT * FROM employees_dv WHERE DATA LIKE '%208%';

DATA
--------------------------------------------------------------------------------
:

{"_id":208,"_metadata":{"etag":"2679E2E75FDE0E35B77BC65314DBC942","asof":"000000
0000E032FA"},"first_name":"Ryunosuke","last_name":"Akutagawa","email":"RAKUTAGAW
A","hire_date":"2013-06-17T00:00:00","job_id":"ST_MAN"}
SQL> SELECT employee_id,first_name,last_name FROM employees
  2  WHERE employee_id=208;

EMPLOYEE_ID FIRST_NAME           LAST_NAME
----------- -------------------- -------------------------
        208 Ryunosuke            Akutagawa

リレーショナル二面性ビューの元表にもデータが追加されたのがわかりますね!

■ORDS を使用してデータを更新

では続いて、追加したデータの内容を更新してみましょう。
変更するのは、employee_id が 208 のデータの EMAIL 列とします。
現在は employees 表を確認すると、「RAKUTAGAWA」となっていますね。

SQL> SELECT employee_id,email FROM employees
  2  WHERE employee_id=208;

EMPLOYEE_ID EMAIL
----------- -------------------------
        208 RAKUTAGAWA

これを、PUT で「RAKUTAGAWA_02」に変更してみます。
PUT で既存のデータを更新する際は、前回の記事でもお伝えしている通り etag 情報を含めます。

[oracle@localhost ~]$ curl --request PUT \
>   --url http://localhost:8080/ords/hr/employees_dv/208 \
>   --header 'Content-Type: application/json' \
>   --data '{"_id" : "208",
>            "_metadata" : {"etag":"2679E2E75FDE0E35B77BC65314DBC942"},★
>            "first_name" : "Ryunosuke",
>            "last_name" : "Akutagawa",
>            "email"   : "RAKUTAGAWA_02",
>            "hire_date"   : "2013-06-17T00:00:00",
>            "job_id"   : "ST_MAN"
>            }'
{"_id":208,"first_name":"Ryunosuke","last_name":"Akutagawa","email":"RAKUTAGAWA_02","hire_date":"2013-06-17T00:00:00","job_id":"ST_MAN","_metadata":{"etag":"F81E9D06B528EAB2684BFCF974A33BA2","asof":"0000000000E03655"},"links":[{"rel":"self","href":"http://localhost:8080/ords/hr/employees_dv/208"},{"rel":"describedby","href":"http://localhost:8080/ords/hr/metadata-catalog/employees_dv/item"},{"rel":"collection","href":"http://localhost:8080/ords/hr/employees_dv/"}]}[oracle@localhost ~]$

employees 表を確認すると、EMAIL 列の値が変更されていることが確認できました!

SQL> SELECT employee_id,email FROM employees
  2  WHERE employee_id=208;

EMPLOYEE_ID EMAIL
----------- -------------------------
        208 RAKUTAGAWA_02

■ORDS を使用してデータを削除

最後に DELETE をリクエストして、employee_id が 208 のデータを削除します。

[oracle@localhost ~]$ curl --request DELETE \
>   --url http://localhost:8080/ords/hr/employees_dv/208
{"rowsDeleted":1}
SQL> SELECT employee_id,email FROM employees
  2  WHERE employee_id=208;

no rows selected

指定したデータが削除されていることが確認できました。
(コミットとかないのでちょっとドキドキ)

■まとめ

今回の記事はいかがでしたでしょうか。

コーソルカナダでは Oracle Database に対して Zabbix の監視を導入する案件を受けたことがありますが、その際も SELECT したデータを JSON データとして扱う際に苦労しました…
JSONリレーショナル二面性ビューは、JSON データを更新や挿入にも使用できるようになったものなので、方向性としては逆になりますがこちらも助かる人が多くいるでしょう。

それでは次回の更新もお楽しみに!

Oracle 23ai 新機能 – JSONリレーショナル二面性ビュー[概要・作成編]

こんにちは。AIです。(Artificial Intelligence ではありません)
いつのまにか 2025 年になっており、びっくりしております。年が明けてからのトロントはとても寒く、最低気温が-17℃になることも!

さて、少し間は空いてしまいましたが、今回は 23ai の目玉新機能の一つともいえる JSONリレーショナル二面性ビューについて掘り下げていきましょう!

■JSON とは

機能の紹介に入る前に、まずは JSON とはなんぞやというところを整理しましょう。

JSON とは [JavaScript Object Notation] の略称であり Java Script をベースとしたテキストベースのデータ交換用フォーマットです。
可読性の高さと、マシンが解析可能な形式であることを両立しており、多様なプログラミング言語で利用されています。

JSON形式やXML形式などでデータを保存するデータベースをドキュメント型データベースと呼びます。
ドキュメント型データベースは、リレーショナル・データベースと比べ自由な形でデータを保存することができますが、一方でデータの整合性については保証されない場合もあります。

■Oracle で JSON 形式でデータを表示する方法

23ai では注目すべき機能としてJSONリレーショナル二面性ビューが実装されますが、実はそれ以前も JSON 自体は 12.1.0.2 から Oracle Database でサポートされていました。

以下は 12.2.0.1 から実装された json_object 関数を使用してテーブルに格納したデータを JSON 形式で表示させています。

@json_object 関数

SQL> SELECT json_object('id' VALUE s.student_id,
  2                     'name' VALUE s.name)
  3  FROM students s;

JSON_OBJECT('ID'VALUES.STUDENT_ID,'NAME'VALUES.NAME)
--------------------------------------------------------
{"id":1,"name":"Ann"}
{"id":2,"name":"Brian"}
{"id":3,"name":"Cathy"}

■JSON リレーショナル二面性ビューとは

JSONリレーショナル二面性ビューは、リレーショナル・データベース表に格納されているデータから更新可能で一貫性のある JSON ドキュメント・ビューを提供する機能です。

JSON リレーショナル二面性ビューとこれまでの関数による表示の大きな違いはテーブルのデータを JSON 形式で更新可能という点です。

更新は SQL としても可能ですし、REST API を使用して更新することも可能です。

■JSON リレーショナル二面性ビューの作成

では、ここからは実際に JSON リレーショナル二面性ビューを作成してみます。
今回は単一の表から JSONリレーショナル二面性ビューを作成します。
(挿入や更新といった操作は次の記事で取り扱う予定です…!)

(1) 元となる表の準備

students という以下のテーブルを元にビューを作成していきます。
注意する点として、JSONリレーショナル二面性ビューを作成するテーブルには
主キー制約、外部キー制約、一意キー制約のいずれかが設定されていなければいけません。
今回は student_id 列に主キーを作成しています。

SQL> SELECT * FROM students;

STUDENT_ID NAME                                GRADE ENROLLMENT_YEAR GRADUATION_YEAR
---------- ------------------------------ ---------- --------------- ---------------
         1 Ann                                     3            2020
         2 Brian                                   2            2021
         3 Cathy                                   1            2022
         
         
SQL> SELECT cc.owner, cc.constraint_name,cc.table_name,cc.column_name,c.constraint_type
  2  FROM dba_constraints c JOIN dba_cons_columns cc ON c.constraint_name=cc.constraint_name
  3  WHERE c.constraint_name='STUDENT_ID_PK';

OWNER      CONSTRAINT_NAME TABLE_NAME COLUMN_NAME          C
---------- --------------- ---------- -------------------- -
TEACHER    STUDENT_ID_PK   STUDENTS   STUDENT_ID           P ★

(2) JSONリレーショナル二面性ビューの作成

では、さっそく JSONリレーショナル二面性ビューを作成していきます。
今回は SQL の CREATE JSON RELATIONAL DUALITY VIEW 文を使用して作成します。
※ちなみにこの文の実行には、CREATE VIEW 権限が必要なのでお忘れなく!
※WITH 句の指定によって、JSONリレーショナル二面性ビューを更新可能にするか否かを制御できます。表レベル、または列レベルで更新可否を設定することができます。

SQL> CREATE JSON RELATIONAL DUALITY VIEW student_dv AS
  2  SELECT JSON{'id' : s.student_id,
  3              'grade' : s.grade,
  4              'name' : s.name,
  5              'enrollment' : s.enrollment_year,
  6              'graduation' : s.graduation_year}
  7  FROM students s WITH INSERT UPDATE DELETE;
FROM students s WITH INSERT UPDATE DELETE
     *
行7でエラーが発生しました。:
ORA-42647:
JSONリレーショナル二面性ビュー'STUDENT_DV'のルート・レベルに'_id'フィールドがあ
りません。
ヘルプ: https://docs.oracle.com/error-help/db/ora-42647/

おや、_id フィールドがないと怒られてしまいます。
実は、JSONリレーショナル二面性ビューの制約として、ルート表の主キー列の要素名は、必ず「_id」という名前にしなければいけないというものがあります。
今回は student_id 列に主キーを設定しておりますので、student_id 列に対応する要素名を _id としましょう。

SQL> CREATE JSON RELATIONAL DUALITY VIEW student_dv AS
  2  SELECT JSON{'_id' : s.student_id, ★
  3              'grade' : s.grade,
  4              'name' : s.name,
  5              'enrollment' : s.enrollment_year,
  6              'graduation' : s.graduation_year}
  7  FROM students s WITH INSERT UPDATE DELETE;

ビューが作成されました。

無事にJSONリレーショナル二面性ビューを作成することができました!
なお、作成したJSONリレーショナル二面性ビューは DBA_JSON_DUALITY_VIEWS で確認することができます。

SQL> SELECT view_owner,view_name,root_table_name,json_column_name,allow_insert,allow_update,allow_delete
  2  FROM dba_json_duality_views
  3  WHERE view_name='STUDENT_DV';

VIEW_OWNER VIEW_NAME  ROOT_TABLE JSON ALLOW_INSER ALLOW_UPDAT ALLOW_DELET
---------- ---------- ---------- ---- ----------- ----------- -----------
TEACHER    STUDENT_DV STUDENTS   DATA TRUE        TRUE        TRUE

(3)JSONリレーショナル二面性ビューを SELECT してみる

実際に作成された JSONリレーショナル二面性ビューを SELECT するとどのような結果が得られるのでしょうか。
その前に、CREATE JSON RELATIONAL DUALITY VIEW に含めた SELECT 文を実際に実行してみます。

SQL> SELECT JSON{'_id' : s.student_id,
  2              'grade' : s.grade,
  3              'name' : s.name,
  4              'enrollment' : s.enrollment_year,
  5              'graduation' : s.graduation_year}
  6  FROM students s;

JSON{'_ID':S.STUDENT_ID,'GRADE':S.GRADE,'NAME':S.NAME,'ENROLLMENT':S.ENROLLMENT_
--------------------------------------------------------------------------------
{"_id":1,"grade":3,"name":"Ann","enrollment":2020,"graduation":null}
{"_id":2,"grade":2,"name":"Brian","enrollment":2021,"graduation":null}
{"_id":3,"grade":1,"name":"Cathy","enrollment":2022,"graduation":null}

JSON形式でデータが表示されていることがわかります。
ではJSONリレーショナル二面性ビューを SELECT してみましょう。

SQL> SELECT * FROM student_dv;

DATA
------------------------------------------------------------------------------------------------------------------------------------------------------------
{"_id":1,"_metadata":{"etag":"1E2C8A22701254064C2A967460A4499A","asof":"0000000004190F0E"},"grade":3,"name":"Ann","enrollment":2020,"graduation":null}


{"_id":2,"_metadata":{"etag":"AD2DB203F0E3C15C843BF3D179F6A45D","asof":"0000000004190F0E"},"grade":2,"name":"Brian","enrollment":2021,"graduation":null}


{"_id":3,"_metadata":{"etag":"5EC933801D01A0C15590B61978B55D26","asof":"0000000004190F0E"},"grade":1,"name":"Cathy","enrollment":2022,"graduation":null}

JSONリレーショナル二面性ビューを確認すると、JSON形式にしたデータに etag と asof からなる metadata という要素が埋め込まれていることがわかります。

1行で表示されると読みづらい場合は、 json_serialize 関数を使用した方法で表示すると要素ごとに改行された状態で表示されます。
※json_serialize は JSONデータを受け取り、テキスト表現として返してくれる関数です。

SQL> SELECT json_serialize(DATA PRETTY) FROM student_dv;

JSON_SERIALIZE(DATAPRETTY)
---------------------------------------------------------
{
  "_id" : 1,
  "_metadata" :
  {
    "etag" : "1E2C8A22701254064C2A967460A4499A",
    "asof" : "000000000418D558"
  },
  "grade" : 3,
  "name" : "Ann",
  "enrollment" : 2020,
  "graduation" : null
}

{
  "_id" : 2,
  "_metadata" :
  {
    "etag" : "AD2DB203F0E3C15C843BF3D179F6A45D",
    "asof" : "000000000418D558"
  },
  "grade" : 2,
  "name" : "Brian",
  "enrollment" : 2021,
  "graduation" : null
}

{
  "_id" : 3,
  "_metadata" :
  {
    "etag" : "5EC933801D01A0C15590B61978B55D26",
    "asof" : "000000000418D558"
  },
  "grade" : 1,
  "name" : "Cathy",
  "enrollment" : 2022,
  "graduation" : null
}

■etag について

最後にJSONリレーショナル二面性ビューを SELECT した際に表示された etag について注目してみます。
これは JSONドキュメント内の要素値をハッシュ化して組み合わせたものです。

JSONリレーショナル二面性ビューは、REST API による更新も可能とお伝えしていましたが、この更新ではトランザクションは発生しません。
その代わり、この etag を使用してデータの整合性を担保しています。

要素値が更新されると etag も合わせて更新されます。
※厳密にいうと NOCHECK オプションが指定されている要素が更新された場合では、etag は更新されませんが、今回の記事では詳細は触れません。

JSONリレーショナル二面性ビューを更新する際、指定した etag と実際のドキュメント(データ)の etag をチェックして、一致しない場合には更新は失敗します。

■まとめ

今回は、JSONリレーショナル二面性ビューのおおまかな概要と作成方法についてご紹介しましたが、いかがでしたでしょうか。

個人的には、etag によるデータの整合性のチェックや主キーを要素に含めることで、 JSON 形式での更新によって意図せぬデータが更新されてしまうことを防いでいる点が興味深かったです。

次回は、JSONリレーショナル二面性ビューの重要なポイント、JSONリレーショナル二面性ビューの更新について掘り下げていく予定ですのでお楽しみに!

Oracle 23ai 新機能 – エラーの改善

AIです。(Artificial Intelligence ではありません)

最近は朝晩は冷え込み、秋が少しずつ近づいているのを感じます。雑貨屋さんに行けばハロウィングッズも見かけるようになり…ってちょっと早いような気もしますが笑

さて、今回は 23ai のエラーの変更について見ていきます!

■エラーの出力

Oracle Database 23ai の SQL*Plus から、エラーメッセージが親切になることを皆さまご存知でしょうか。実際に 19c と 23ai で SQL*Plus から ORA-1017 のメッセージを発生させて見比べてみましょう。

$ sqlplus AI/AI ★19c にログイン

SQL*Plus: Release 19.0.0.0.0 - Production on Wed Sep 4 06:45:15 2024
Version 19.7.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.

ERROR:
ORA-01017: ユーザー名/パスワードが無効です。ログオンは拒否されました。


Enter user-name:
$ sqlplus AI/AI ★23ai にログイン

SQL*Plus: Release 23.0.0.0.0 - Production on 火 9月 3 17:46:19 2024
Version 23.4.0.24.05

Copyright (c) 1982, 2024, Oracle.  All rights reserved.

ERROR:
ORA-01017: 資格証明が無効であるか、権限がありません。ログオンは拒否されました ヘルプ:
https://docs.oracle.com/error-help/db/ora-01017/★


ユーザー名を入力してください:

はい。23ai では URL が表示されていますね。

この URL を開き、該当のリリースを選択するとそのエラーの原因や取るべきアクションを確認することができます。

エラーの出力内容については、SET ERRORDETAILS で制御可能です。このパラメータを VERBOSE に設定するとエラーの原因、とるべきアクションが表示されるようになります。※デフォルトは ON です。

SET ERRORDETAILS { OFF | ON | VERBOSE }
SQL> conn AI/AI
ERROR:
ORA-01017: 資格証明が無効であるか、権限がありません。ログオンは拒否されました ヘルプ:
https://docs.oracle.com/error-help/db/ora-01017/


警告: Oracleにはもう接続されていません。

SQL> set errordetails VERBOSE
SQL> conn AI/AI
ERROR:
ORA-01017: 資格証明が無効であるか、権限がありません。ログオンは拒否されました ヘルプ:
https://docs.oracle.com/error-help/db/ora-01017/
Cause:  An invalid credential was provided when accessing the Oracle
        Database or you were not authorized to access this database.
Action: Depending on your authentication method, ensure that the correct
        credential is provided when logging in to Oracle Database.
        Retry your credential after checking it. If your credential is
        correct, you may not be authorized to access the database.
        You will need to contact your database administrator or
        identity management administrator to confirm that you are
        authorized to access the database.


警告: Oracleにはもう接続されていません。

■OERR コマンド

詳細なエラーの説明は SQL*Plus の OERR コマンドでいつでも確認することができるようになりました。こちらにも URL が載っていますね。

実際にエラーが発生していない状態でも、原因ととるべきアクションがわかるので調査に便利そう。

SQL> OERR ORA-1017
Message: "invalid credential or not authorized; logon denied"
ヘルプ: https://docs.oracle.com/error-help/db/ora-01017/
Cause:  An invalid credential was provided when accessing the Oracle
        Database or you were not authorized to access this database.
Action: Depending on your authentication method, ensure that the correct
        credential is provided when logging in to Oracle Database.
        Retry your credential after checking it. If your credential is
        correct, you may not be authorized to access the database.
        You will need to contact your database administrator or
        identity management administrator to confirm that you are
        authorized to access the database.

ちなみに OS 側から oerr コマンドを実行することもできます。実はこちらの機能は、10.2.0.5 からあったのだとか。(AIは初めて知りました…)書いてある内容はそう変わりませんが、URL はこちらでは出力されませんね。

$ oerr ORA 1017
01017, 00000, "invalid credential or not authorized; logon denied"
// *Cause:  An invalid credential was provided when accessing the Oracle
//          Database or you were not authorized to access this database.
// *Action: Depending on your authentication method, ensure that the correct
//          credential is provided when logging in to Oracle Database.
//          Retry your credential after checking it. If your credential is
//          correct, you may not be authorized to access the database.
//          You will need to contact your database administrator or
//          identity management administrator to confirm that you are
//          authorized to access the database.

■まとめ

いかがでしたでしょうか。今までは Oracle のエラーに遭遇したら、Database エラー・メッセージのマニュアルを開いて、該当するエラー番号のページを探して、エラーを検索して…といった作業をしていました。地味に工程が多い…。

23ai からは、OERR コマンドやエラーの URL から即座にエラーの概要を確認できるようになり、とても便利になる予感がしております!