すべてのプロダクト
Search
ドキュメントセンター

AnalyticDB:JSON および JSONB データ型の操作

最終更新日:Mar 29, 2026

AnalyticDB for PostgreSQL では、半構造化データの格納およびクエリ実行に JSON および JSONB データ型をサポートしています。JSONB はバイナリ形式でデータを格納し、インデックス作成をサポートするため、ほとんどのワークロードにおいて推奨される選択肢です。一方、JSON は入力テキストを正確に保持するため、空白文字やキーの順序を厳密に維持する必要がある場合に有効です。

JSON と JSONB の比較

項目JSONJSONB
格納形式入力されたテキストをそのまま保持バイナリ形式
クエリ性能遅い(各アクセス時に再解析が必要)高速
インデックス作成対応いいえ可(GIN、B-tree)
可用性全バージョンV6.0

ほとんどのワークロードには JSONB を使用してください — 読み取りが高速で、インデックス作成および包含クエリ(containment query)の効率性も優れています。元の入力フォーマットを厳密に保持する必要がある場合のみ、JSON を使用します。

制限事項

  • JSON および JSONB の列はパーティションキーとして使用できません。

  • JSON の集計関数はサポートされていません。

JSON 入力構文

JSON 値は、以下のいずれかである必要があります:オブジェクト、配列、数値、文字列、または小文字のリテラル名 truefalsenull。仕様の詳細については、「RFC 7159」をご参照ください。

-- スカラ値:数値、引用符で囲まれた文字列、またはリテラル名
SELECT '5'::json;

-- 配列:任意の型の要素を 0 個以上含む
SELECT '[1, 2, "foo", null]'::json;

-- オブジェクト:キーと値のペア(キーは必ず引用符で囲む)
SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json;

-- ネストされた構造
SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json;

すべての JSON 値は JSONB へキャスト可能です:

SELECT '5'::jsonb;

演算子

JSON および JSONB 共通の演算子

演算子右オペランドの型説明結果
->intゼロベースのインデックスで JSON 配列の要素を取得'[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json->2{"c":"baz"}
->textキーで JSON オブジェクトのフィールドを取得'{"a":{"b":"foo"}}'::json->'a'{"b":"foo"}
->>intJSON 配列の要素を文字列として取得'[1,2,3]'::json->>23
->>textJSON オブジェクトのフィールドを文字列として取得'{"a":1,"b":2}'::json->>'b'2
#>text[]指定されたパスにある JSON オブジェクトを取得'{"a":{"b":{"c":"foo"}}}'::json#>'{a,b}'{"c":"foo"}
#>>text[]指定されたパスにある JSON オブジェクトを文字列として取得'{"a":[1,2,3],"b":[4,5,6]}'::json#>>'{a,2}'3

JSONB 専用の演算子

演算子右オペランドの型説明
=jsonb2 つの JSONB 値が等しいかどうかをチェック'[1,2]'::jsonb = '[1,2]'::jsonb
@>jsonb左側の値が右側の値を含むかどうかをチェック'{"a":1,"b":2}'::jsonb @> '{"b":2}'::jsonb
<@jsonb左側の値が右側の値に含まれるかどうかをチェック'{"b":2}'::jsonb <@ '{"a":1,"b":2}'::jsonb
?text文字列が JSON 値のキーまたは文字列として存在するかどうかをチェック'{"a":1,"b":2}'::jsonb ? 'b'
?|text[]文字列のいずれかが JSON 値のキーまたは文字列として存在するかどうかをチェック'{"a":1,"b":2,"c":3}'::jsonb ?| array['b','c']
?&text[]すべての文字列が JSON 値のキーまたは文字列として存在するかどうかをチェック'["a","b"]'::jsonb ?& array['a','b']

関数

JSON 生成関数

関数説明結果
to_json(anyelement)値を JSON に変換します。配列および複合型は再帰的に変換されます。数値、ブール値、または null でないスカラ値は、引用符で囲まれた文字列として返されます。to_json('Fred said "Hi."'::text)"Fred said \"Hi.\""
array_to_json(anyarray [, pretty_bool])配列を JSON 配列に変換します。pretty_booltrue の場合、最上位要素間に改行を挿入します。array_to_json('{{1,5},{99,100}}'::int[])[[1,5],[99,100]]
row_to_json(record [, pretty_bool])行を JSON オブジェクトに変換します。pretty_booltrue の場合、最上位要素間に改行を挿入します。row_to_json(row(1,'foo')){"f1":1,"f2":"foo"}

JSON 処理関数

関数戻り値の型説明
json_each(json)key text、value json のセット / key text、value jsonb のセット最上位のオブジェクトをキーと値のペアに展開しますselect * from json_each('{"a":"foo","b":"bar"}')
json_each_text(json)(key text、value text) のセット最上位のオブジェクトをキーと値のペアに展開します。値は文字列として返されますselect * from json_each_text('{"a":"foo","b":"bar"}')
json_extract_path(from_json json, VARIADIC path_elems text[])json指定されたパスにある JSON 値を返します。#> 演算子と同等です。json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}','f4')
json_extract_path_text(from_json json, VARIADIC path_elems text[])text指定されたパスにある JSON 値を文字列として返します。#>> 演算子と同等です。json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}','f4','f6')
json_object_keys(json)text のセット最上位のオブジェクト内のキーのセットを返しますjson_object_keys('{"f1":"abc","f2":{"f3":"a","f4":"b"}}')
json_populate_record(base anyelement, from_json json)anyelementJSON オブジェクトを、baseselect * from json_populate_record(null::myrowtype, '{"a":1,"b":2}')
json_populate_recordset(base anyelement, from_json json)anyelement のセット最上位の JSON 配列(オブジェクトの配列)を、baseselect * from json_populate_recordset(null::myrowtype, '[{"a":1,"b":2},{"a":3,"b":4}]')
json_array_elements(json)json のセットJSON 配列を JSON 値のセットに展開しますselect * from json_array_elements('[1,true,[2,false]]')

JSONB インデックスの作成

JSONB 列では GIN インデックスおよび B-tree インデックスがサポートされています。GIN インデックスは、@> 演算子を使用した包含クエリに対して最も効果的です。

利用可能な GIN オペレータークラスは 2 種類あります:

-- デフォルトのオペレータークラス (jsonb_ops):各キーおよび値を個別にインデックス化
CREATE INDEX idx_name ON table_name USING gin (idx_col);

-- パスベースのオペレータークラス (jsonb_path_ops):値のみをインデックス化
CREATE INDEX idx_name ON table_name USING gin (idx_col jsonb_path_ops);

オペレータークラスの選択方法:

jsonb_ops(デフォルト)jsonb_path_ops
索引項目キーごとに 1 件、値ごとに 1 件値ごとに 1 件
推奨用途キーの存在をフィルターするクエリ(?)および包含クエリ包含クエリのみ(@>

jsonb_ops を使用するのは、??\|?& 演算子に加えて @> も使用する場合です。jsonb_path_ops を使用するのは、すべてのクエリで @> のみを使用する場合です。

サンプル

JSON データの格納およびクエリ実行

-- JSON および配列の列を含むテーブルを作成
CREATE TABLE tj (id serial, ary int[], obj json, num integer);

-- 行を挿入
INSERT INTO tj (ary, obj, num) VALUES ('{1,5}'::int[], '{"obj":1}', 5);
INSERT INTO tj (ary, obj, num) VALUES ('{2,5}'::int[], '{"obj":2}', 5);

-- 行を JSON オブジェクトに変換
SELECT row_to_json(q) FROM (SELECT id, ary, obj, num FROM tj) AS q;
--                  row_to_json
-- -------------------------------------------
--  {"f1":1,"f2":[1,5],"f3":{"obj":1},"f4":5}
--  {"f1":2,"f2":[2,5],"f3":{"obj":2},"f4":5}

JSON フィールドによるテーブル結合

結合条件で JSON フィールドを文字列として抽出するには、->> を使用します:

CREATE TABLE tj2 (id serial, ary int[], obj json, num integer);
INSERT INTO tj2 (ary, obj, num) VALUES ('{2,5}'::int[], '{"obj":2}', 5);

-- ->> 演算子を使用した結合
SELECT * FROM tj, tj2 WHERE tj.obj->>'obj' = tj2.obj->>'obj';

-- json_object_field_text() を使用した同等の結合
SELECT * FROM tj, tj2
  WHERE json_object_field_text(tj.obj, 'obj') = json_object_field_text(tj2.obj, 'obj');

JSON 列に対する関数インデックスの作成

関数インデックスを使用すると、JSON パス式の結果をインデックス化でき、ネストされたフィールドに対する効率的な等価検索が可能になります:

CREATE TEMP TABLE test_json (json_type text, obj json);
INSERT INTO test_json VALUES ('aa', '{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}');
INSERT INTO test_json VALUES ('cc', '{"f7":{"f3":1},"f8":{"f5":99,"f6":"foo"}}');

-- 抽出されたパス値に対するインデックスを作成
CREATE INDEX i ON test_json (json_extract_path_text(obj, '{f4}'));

-- 実行計画は等価クエリでこのインデックスを利用できます
SELECT * FROM test_json WHERE json_extract_path_text(obj, '{f4}') = '{"f5":99,"f6":"foo"}';
--  json_type |                 obj
-- -----------+-------------------------------------------
--  aa        | {"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}

JSONB 列における GIN インデックスのパフォーマンス比較

以下の例では、1,000 万行のデータを使用して、GIN インデックスが包含クエリに与える影響を示します。

-- テストテーブルを作成
CREATE TABLE jtest1 (id int, jdoc json);
CREATE TABLE jtest2 (id int, jdoc jsonb);
CREATE TABLE jtest3 (id int, jdoc jsonb);

-- ランダム文字列ヘルパー関数を作成
CREATE OR REPLACE FUNCTION random_string(INTEGER)
RETURNS TEXT AS
$BODY$
SELECT array_to_string(
    ARRAY (
        SELECT substring(
            '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
            FROM (ceil(random()*62))::int FOR 1
        )
        FROM generate_series(1, $1)
    ),
    ''
)
$BODY$
LANGUAGE sql VOLATILE;

-- jtest1 に 1,000 万行を挿入
INSERT INTO jtest1
SELECT t.seq,
       ('{"a":{"a1":"a1a1","a2":"a2a2"},"name":"' || random_string(10) || '","b":"bbbbb"}')::json
FROM generate_series(1, 10000000) AS t(seq);

-- JSONB テーブルにコピー
INSERT INTO jtest2 SELECT id, jdoc::jsonb FROM jtest1;
INSERT INTO jtest3 SELECT id, jdoc::jsonb FROM jtest1;

-- GIN インデックスを作成
CREATE INDEX idx_jtest2 ON jtest2 USING gin(jdoc);                        -- jsonb_ops
CREATE INDEX idx_jtest3 ON jtest3 USING gin(jdoc jsonb_path_ops);         -- jsonb_path_ops

クエリパフォーマンスの比較:

-- インデックスなしの JSON テーブルでの逐次スキャン:約 1,778 ms
EXPLAIN ANALYZE SELECT * FROM jtest1 WHERE jdoc @> '{"name":"N9WP5txmVu"}';

-- jsonb_ops を使用した GIN インデックス:約 1.3 ms
EXPLAIN ANALYZE SELECT * FROM jtest2 WHERE jdoc @> '{"name":"N9WP5txmVu"}';

-- jsonb_path_ops を使用した GIN インデックス:約 1.3 ms
EXPLAIN ANALYZE SELECT * FROM jtest3 WHERE jdoc @> '{"name":"N9WP5txmVu"}';

どちらの GIN インデックスタイプも、1,000 万行におけるクエリ時間を約 1,778 ms から約 1.3 ms まで短縮します。jsonb_path_ops@> のみを使用するクエリに、jsonb_ops はキーの存在をチェックする演算子(??\|?&)も併用するクエリにそれぞれ適しています。

Python からの JSONB データのクエリ実行

psycopg2 を使用して接続し、JSON クエリを実行します:

#!/usr/bin/env python
import json
import psycopg2

def run_query(sql):
    conn = psycopg2.connect("dbname=<your-database>")
    conn.autocommit = True
    cur = conn.cursor()
    cur.execute(sql)
    return cur.fetchall()

rows = run_query("SELECT obj FROM tj;")
for row in rows:
    print(json.loads(row[0]))

<your-database> をご利用のデータベース名に置き換えてください。