Oracle NoSQL Database Cloud Serviceのテーブル階層(子表)を試してみる


## はじめに
OCIのOracle NoSQL Database Cloud Service(NDCS)では、子表という機能を使って、テーブルに階層を持たせることが可能になっています。
Oracle NoSQL DatabaseはSQLが利用できるため、この機能を使うと、RDBほどではありませんがテーブル同士の関連性を表現できます。

## 子表の作成
子表の作成のためには親となるテーブルの作成がまず必要です。
メニュー -> データベース -> 表 と選択して、NDCSを開きます。

「表の作成」から親となるテーブルを作成します。
ここでは`parent`と言う名前のテーブルで、id(String), Value(String)だけを持つテーブルを作ってみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482527/d5e328fc-c4a6-7581-9e1f-b709874497d4.png)

テーブルが作成されたらクリックして開くと、左メニューに「子表」とあるのでクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482527/20f42485-7d14-3562-e82f-f83142c96c75.png)

同じようにテーブルが作れます。
注目すべきは「名前」の所に`parent.`と既に記述があるところです。Oracle NoSQLでは、子テーブルは`.`で繋いで表現をします。
同じカラム名は持てないので、`child1_`を接頭辞的につけています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482527/f2c721ae-108c-c1a7-1fbb-0792729b2290.png)

アクティブになったので確認してみると、列に設定していないはずの`id`列が出てきました。
これはつまり親である`parent`テーブルの主キーを継承していることになり、子である`child1`テーブルは複合主キーとして振舞うということが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482527/ba9dff15-13c2-f4c6-6df6-cdd2acb7d18b.png)

## データを挿入
`parent.child1`テーブルにデータを挿入してみます。
「データの探索」からSQLが実行できるので、INSERT文を実行してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482527/c0fe8940-9b43-74ea-db06-c7de254ec305.png)

以下のようにINSERT文を2つ実行します。どのテーブルに対してもクエリの実行は可能です。
```SQL
INSERT INTO parent VALUES ("2023040001", "parent example1")
INSERT INTO parent.child1 VALUES ("2023040001", "2023040001-1", "child1 example1")
```

## クエリの実行

子表はテーブルとして**独立して**利用できます。データを取得してみましょう。
```SQL
SELECT * FROM parent.child1
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482527/c7b13673-1996-ab60-bedc-bb11b7a74142.png)

次は親テーブルのデータを取得してみましょう。
```SQL
SELECT * FROM parent
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482527/00545b09-3201-cb5a-cef6-dc005daf2534.png)
parent.child1のデータは取得されないことが分かります。
これこそがこの子表のメリットです。**必要のないデータを設計レベルでそもそも取得しないようにする**ことが可能です。
テーブルが大きくなりすぎている場合などに利用できそうです。

勿論、必要に応じて親テーブル + 子テーブルのようにデータを取得することも可能です。
Oracle NoSQLの子表ではLEFT OUTER JOINがサポートされています。
```SQL
SELECT p.id, c1.child1_value
FROM parent  p LEFT OUTER JOIN parent.child1 c1
on p.id = c1.id
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482527/7651f42b-3190-f675-950a-0ea3eced2a3e.png)

## テーブルの階層
子表にはさらに子表を作ることが可能です。
`parent.child1`テーブルと同じように`id`と`value`だけを持つ`parent.child1.grandchild1`テーブルを作ってみます。
さらに、`parent.child1`テーブルと同じ階層に`parent.child2`テーブルを作ります。
テーブルの階層は以下のようになります。
```
parent 
  ├ child1 
  │   └ grandchild1
  └ child2
```

SQLを実行して作成した2つのテーブルに対してデータを挿入します。
```SQL
INSERT INTO parent.child1.grandchild1 VALUES ("2023040001", "2023040001-1", 2023040001-1-1, "grandchild1 example1")
INSERT INTO parent.child2 VALUES ("2023040001", "2023040001-2", "child2 example1")
```

しかし、2つ以上のLEFT OUTER JOINはサポートされていません。
```SQL
SELECT p.id, p.value, c1.child1_value, c2.child2_value
FROM parent p 
LEFT OUTER JOIN parent.child1 c1 ON p.id = c1.id
LEFT OUTER JOIN parent.child2 c2 ON p.id = c2.id
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482527/3896879c-2718-89bd-a48c-7f7b2de5dad1.png)

そのような場合は[NESTED TABLES](https://docs.oracle.com/en/database/other-databases/nosql-database/22.3/nsdev/using-nested-tables-join-parent-child-tables1.html#GUID-3709E119-FC6F-490A-8627-DA92C592D40B)句を利用します。
`descendants`句と同時に利用して、`descendants`の後の括弧内のテーブルが子の関係にあることを表現します。
```SQL
SELECT p.id, p.value, c1.child1_value, c2.child2_value
FROM NESTED TABLES (parent p descendants(parent.child1 c1, parent.child2 c2))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482527/4d6fa7bc-61a2-3b81-f3e4-5c921c7bf328.png)

これを利用すれば、孫(もしくはそれ以降)の関係にあるテーブルの同時取得も可能になります。
```SQL
SELECT p.id, p.value, c1.child1_value, c2.child2_value, gc1.grandchild1_value
FROM NESTED TABLES (parent p descendants(parent.child1 c1, parent.child2 c2, parent.child1.grandchild1 gc1))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482527/19c8249f-06dd-c3bb-c2d0-aa6bc87430f0.png)

## まとめ
今回はOCIのNoSQL Database(NDCS)を使って、テーブル階層を試してみました。
これを使うことで、より柔軟なデータモデルの設計や、データ取得量の軽減によるコスト(スループット)の節約に活用できそうです。

## 参考資料
https://blogs.oracle.com/nosql/post/table-hierarchy-nosql-cloud
https://docs.oracle.com/en/database/other-databases/nosql-database/22.3/index.html