PostgreSQLのjsonb型【基本概要と自作関数の記載】

## はじめに

最近、開発現場でPostgreSQLの`jsonb`型を扱うことが増えました。
けれど、正直なところ「なんとなく使ってる」レベルで、深く理解できているとは言えませんでした。
公式ドキュメントを読んでも、文章が硬くて理解しにくい。
そこで今回は、**自分自身の理解を深めるために**、そして同じように「ちょっとよくわかっていない」と感じている方のために、`jsonb`の基本を整理していきます。

---

## jsonとjsonbの違いをざっくり説明

PostgreSQLには、JSON(JavaScriptのオブジェクトを参考にしたデータ管理方法)を保存できる型が2つあります。

- `json`
- `jsonb`

### 違いを一言で言うと

- `json` → **見た目そのまま保存**
- `jsonb` → **中身を分解して保存（＝機械が扱いやすい）**

例えるなら、

- `json`は写真をアルバムにそのまま貼った状態
- `jsonb`は写真をデータベースに仕分けして保存した状態

人には`json`が見やすいけど、機械には`jsonb`の方が管理しやすいんです。

### jsonとjsonbの違い
| 比較ポイント  | `json`      | `jsonb`          |
| ------- | ----------- | ---------------- |
| 保存形式    | テキストのまま     | バイナリ(データの基本形式?)で整理して保存      |
| 処理スピード  | 遅め（毎回解析が必要） | 速い（解析済みで保存されている） |
| インデックス  | 使えない        | 使える              |
| キーの順序   | 保持する        | 保持しない            |
| 同じキーの扱い | 全部保持        | 最後の1個だけ保持        |

---

## なぜ`jsonb`を使うのか？
- **検索が速い**
- **インデックスが使える**
- **再解析が不要だから処理が早い**

つまり、システムでデータ管理を行うなら、**ほとんどのケースで`jsonb`の方が優れている**と言えます。

---

## jsonbでよく使う関数を超簡単に解説

### `jsonb_set`：JSONの中身を書き換える

まずは基本的な使い方から紹介していきます。

```sql
SELECT jsonb_set(
    '{"name": "田中", "department": "sales"}'::jsonb,
    '{department}',
    '"marketing"'::jsonb,
    true
);
```

### やってること
`{"name": "田中", "department": "sales"}`という JSON があったとします。

これの "department" の値を "marketing" に変えています。

true をつけることで、対象のキーがなくても新しく作ってくれます。

### 結果
```sql
{"name": "田中", "department": "marketing"}
```

### ポイント解説
`jsonb_set(jsonb, path, new_value, create_missing) `という形式。

`{department}` のように、キー名を {} で囲むと「パス」を指定できます。

`to_jsonb(...)`を使うことで、普通の値を jsonb 型に変換できます。

例：`to_jsonb('マーケティング') `→ `"マーケティング"`（JSON文字列として扱える）

このように、`jsonb_set`は特定のキーの値を柔軟に変更できる便利な関数です。

PostgreSQLには`jsonb_set`以外にも便利な関数があるため、気になる人は気になる人は調べてみてください。

公式ページ:

[9.16. JSON関数と演算子](https://www.postgresql.jp/document/17/html/functions-json.html)

## jsonbを使用した自作関数:空の値は無視したい時に

> **「空文字とかNULLなら元の値を残して、値があるなら上書きしたい」**

こんなときに便利なのが、以下の自作関数です。

---

### 自作関数：`jsonb_preferring_empty`

```sql
CREATE OR REPLACE FUNCTION jsonb_preferring_empty(
    json1 JSONB,
    json2 JSONB
) RETURNS JSONB LANGUAGE sql AS $$
    SELECT jsonb_object_agg(key, value)
    FROM (
        SELECT 
            COALESCE(t2.key, t1.key) AS key,
            CASE
                WHEN t2.value IS NULL OR t2.value = '' THEN t1.value
                ELSE t2.value
            END AS value
        FROM jsonb_each_text(json1) t1
        FULL OUTER JOIN jsonb_each_text(json2) t2 ON t1.key = t2.key
    ) merged;
$$;
```

## やってること

- `json1` と `json2` のキーを比較して照らし合わせる  
- `json2` の値が**空文字や NULL** の場合は、`json1` の値を使う  
- それ以外なら、`json2` の値を使う

つまりこの関数を使うと「空の値を無視して結合(マージ)できる」ということです。

---

### 例
```json
json1 = {"name": "田中", "email": ""}
json2 = {"name": "", "email": "b@example.com"}

-- 結果:
{"name": "田中", "email": "b@example.com"}
```

上記の関数はこんなシーンで活躍する想定です:

- 外部データを結合(マージ)する時
- 入力フォームで未入力を無視して更新したい時
- 値が欠けていても元のデータを活かしたい時

## おわりに
自分自身、PostgreSQLの`jsonb`を「なんとなく」で使っていたことに気づきました。
今回紹介した自作関数も、私がテストした段階では正しく動作しましたが、すべてのケースにおいて問題なく動くとは限りません。
実際、条件によっては予期しない挙動になる可能性もあり、汎用性という点では課題が残ります。

とはいえ、「完璧な関数」を最初から作るのは難しくても、より効率的で汎用性の高いアプローチを考えること自体が、自分の成長につながると考えています。

今後は`jsonb_set`以外の関数にも目を向け、PostgreSQLと jsonb の理解をさらに深めていくつもりです。
この取り組みが、読者の皆さんにとっても何かのヒントになれば嬉しいです。