JavaScript Object Notation (JSON) データ型は、インターネットおよびモノのインターネット (IoT) で広く使用されています。 JSONに使用されるプロトコルの詳細については、JSONの紹介をご覧ください。 AnalyticDB for PostgreSQLはJSONデータ型をサポートしています。 AnalyticDB for PostgreSQL V6.0は、JSON-Bデータ型もサポートしています。 このトピックでは、JSONとJSON-Bデータを管理する方法について説明します。
JSONとJSON-Bの違いと類似点
JSONとJSON-Bのデータ型は使用方法が似ていますが、ストレージ方法が異なります。 JSONデータは入力テキストの正確なコピーとして保存され、JSON-Bデータはバイナリ形式で保存されます。 JSON-Bデータ型は、JSONデータ型よりも効率的で処理が高速です。 また、JSON-Bデータ型はインデックス作成をサポートしています。 したがって、AnalyticDB for PostgreSQL V6.0ではJSON-Bデータ型が推奨されます。
JSON入力および出力構文
詳細については、RFC 7159をご参照ください。
JSON値は、オブジェクト、配列、数値、文字列、または小文字のリテラル名 (true、null、false) のいずれかである必要があります。 次のステートメントは有効なJSON式です。
-- A simple scalar or value
-- A simple value must be a number, a string enclosed in a pair of quotation marks, or a literal name (true, false, or null).
SELECT '5'::json;
-- An array of zero or more elements (The elements can be of the same type or different types.)
SELECT '[1, 2, "foo", null]'::json;
-- An object that contains key-value pairs
-- The key in a key-value pair of an object must be a string enclosed in a pair of quotation marks.
SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json;
-- An array or object nested in each other
SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json;
上記のJSON値はすべてJSON-B値に変換できます。 例:
-- A simple scalar or value in the JSON-B format
SELECT '5'::jsonb;
JSON演算子
次の表に、JSONおよびJSON-Bデータ型で使用できる演算子を示します。
演算子 | 右オペランドタイプ | 説明 | 例 | 結果 |
-> | int | ゼロからインデックス付けされたJSON配列要素を取得します。 |
|
|
-> | text | キーに基づいてJSONオブジェクトフィールドを取得します。 |
|
|
->> | int | JSON配列要素をテキストとして取得します。 |
|
|
->> | text | JSONオブジェクトフィールドをテキストとして取得します。 |
|
|
#> | text[] | 指定したパスからJSONオブジェクトを取得します。 |
|
|
#>> | text[] | 指定したパスからJSONオブジェクトをテキストとして取得します。 |
|
|
次の表に、JSON-Bデータ型で使用できる演算子を示します。
JSON-B演算子
次の表に、JSON-Bデータ型で使用できる演算子を示します。
演算子 | 右オペランドタイプ | 説明 | 例 |
= | jsonb | 2つのJSON値が同じかどうかを指定します。 |
|
@> | jsonb | 左のJSON値に右のJSON値が含まれるかどうかを指定します。 |
|
<@ | jsonb | 左のJSON値が右のJSON値に含まれるかどうかを指定します。 |
|
? | text | 文字列がキーとして存在するか、JSON値内の文字列として存在するかを指定します。 |
|
? | | text[] | 正しい配列文字列のいずれかがキーとして存在するか、JSON値内の文字列として存在するかを指定します。 |
|
? & | text[] | すべての右側の配列文字列がキーとして存在するか、JSON値内の文字列として存在するかを指定します。 |
|
JSON作成関数
次の表に、JSON値の作成に使用される関数を示します。
関数 | 説明 | 例 | 結果 |
| 有効なJSONオブジェクトとして値を返します。 配列とコンポジットは、配列とオブジェクトに再帰的に変換されます。 キャスト関数が提供される場合、そのキャスト関数は、入力値をJSONオブジェクトに変換するために呼び出される。 そうでなければ、スカラー値が生成される。 スカラー値が数値、ブール値、またはnullでない場合、有効なJSON文字列にするために使用される引用符とエスケープ文字を含むJSONテキストで表されます。 |
|
|
| 配列をJSON配列として返します。 多次元配列を入力すると、配列のJSON配列が返されます。 説明 pretty_boolパラメーターの値がtrueの場合、ディメンション-1要素の間に改行が追加されます。 |
|
|
| 行をJSONオブジェクトとして返します。 説明 pretty_boolパラメーターの値がtrueの場合、ディメンション-1要素の間に改行が追加されます。 |
|
|
JSON処理関数
次の表に、JSON値の処理に使用される関数を示します。
関数 | 戻り値タイプ | 説明 | 例 | リターン結果の例 |
|
| 最も外側のJSONオブジェクトを一連のキーと値のペアに展開します。 |
|
|
|
| 最も外側のJSONオブジェクトを一連のキーと値のペアに展開します。 戻り値はTEXT型です。 |
|
|
|
| path_elemsパラメーターで指定されたJSON値を返します。 この関数は#> 演算子に相当します。 |
|
|
|
| path_elemsパラメーターで指定されたJSON値をJSONテキストとして返します。 この関数は、#>> 演算子に相当します。 |
|
|
|
| 最も外側のJSONオブジェクトのキーのセットを返します。 |
|
|
|
| from_jsonパラメーターのオブジェクトを、基本パラメーターで定義されたレコードタイプと一致する列を持つ行に展開します。 |
|
|
|
| from_jsonパラメーターのオブジェクトの最も外側の配列を、基本パラメーターで定義されたレコードタイプと一致する列を持つ行のセットに展開します。 |
|
|
|
| JSON配列をJSON値のセットに展開します。 |
|
|
JSON-B関数インデックスの作成
JSON-Bの列にGINおよびBツリーインデックスを作成できます。 次のステートメントを実行して、JSON-Bの列にGINインデックスを作成できます。
CREATE INDEX idx_name ON table_name USING gin (idx_col);
CREATE INDEX idx_name ON table_name USING gin (idx_col jsonb_path_ops);
JSON-Bの列にGINインデックスを作成するには、デフォルトのjsonb_ops演算子とjsonb_path_ops演算子のいずれかを使用できます。 jsonb_opsとjsonb_path_ops GINインデックスの違いは、前者はデータ内の各キーと値に対して独立したインデックスアイテムを作成し、後者はデータ内の各値に対してのみインデックスアイテムを作成することです。
例
テーブルを作成します。
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 0 1
=> 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}
(1 row)
=> insert into tj(ary, obj, num) values('{2,5}'::int[], '{"obj":2}', 5);
INSERT 0 1
=> 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}
(2 rows)
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);
INSERT 0 1
=> select * from tj, tj2 where tj.obj->>'obj' = tj2.obj->>'obj';
id | ary | obj | num | id | ary | obj | num
----+-------+-----------+-----+----+-------+-----------+-----
2 | {2,5} | {"obj":2} | 5 | 1 | {2,5} | {"obj":2} | 5
(1 row)
=> select * from tj, tj2 where json_object_field_text(tj.obj, 'obj') = json_object_field_text(tj2.obj, 'obj');
id | ary | obj | num | id | ary | obj | num
----+-------+-----------+-----+----+-------+-----------+-----
2 | {2,5} | {"obj":2} | 5 | 1 | {2,5} | {"obj":2} | 5
(1 row)
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 0 1
=> insert into test_json values('cc', '{"f7":{"f3":1},"f8":{"f5":99,"f6":"foo"}}');
INSERT 0 1
=> select obj->'f2' from test_json where json_type = 'aa';
? column?
----------
{"f3":1}
(1 row)
=> create index i on test_json (json_extract_path_text(obj, '{f4}'));
CREATE INDEX
=> 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"}}
(1 row)
JSON-B関数インデックスを作成します。
-- Create a test table and generate data.
CREATE TABLE jtest1 (
id int,
jdoc json
);
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;
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);
CREATE TABLE jtest2 (
id int,
jdoc jsonb
);
CREATE TABLE jtest3 (
id int,
jdoc jsonb
);
insert into jtest2 select id, jdoc::jsonb from jtest1;
insert into jtest3 select id, jdoc::jsonb from jtest1;
-- Create an index.
CREATE INDEX idx_jtest2 ON jtest2 USING gin(jdoc);
CREATE INDEX idx_jtest3 ON jtest3 USING gin(jdoc jsonb_path_ops);
-- Execute a query plan without an index.
EXPLAIN ANALYZE SELECT * FROM jtest1 where jdoc @> '{"name":"N9WP5txmVu"}';
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------
Gather Motion 2:1 (slice1; segments: 2) (cost=0.00..162065.73 rows=10100 width=88) (actual time=1343.248..1777.605 rows=1 loops=1)
-> Seq Scan on jtest2 (cost=0.00..162065.73 rows=5050 width=88) (actual time=0.042..1342.426 rows=1 loops=1)
Filter: (jdoc @> '{"name": "N9WP5txmVu"}'::jsonb)
Planning time: 0.172 ms
(slice0) Executor memory: 59K bytes.
(slice1) Executor memory: 91K bytes avg x 2 workers, 91K bytes max (seg0).
Memory used: 2047000kB
Optimizer: Postgres query optimizer
Execution time: 1778.234 ms
(9 rows)
-- Execute a query plan by using the jsonb_ops operator.
EXPLAIN ANALYZE SELECT * FROM jtest2 where jdoc @> '{"name":"N9WP5txmVu"}';
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
Gather Motion 2:1 (slice1; segments: 2) (cost=88.27..13517.81 rows=10100 width=88) (actual time=0.655..0.659 rows=1 loops=1)
-> Bitmap Heap Scan on jtest2 (cost=88.27..13517.81 rows=5050 width=88) (actual time=0.171..0.172 rows=1 loops=1)
Recheck Cond: (jdoc @> '{"name": "N9WP5txmVu"}'::jsonb)
-> Bitmap Index Scan on idx_jtest2 (cost=0.00..85.75 rows=5050 width=0) (actual time=0.217..0.217 rows=1 loops=1)
Index Cond: (jdoc @> '{"name": "N9WP5txmVu"}'::jsonb)
Planning time: 0.151 ms
(slice0) Executor memory: 69K bytes.
(slice1) Executor memory: 628K bytes avg x 2 workers, 632K bytes max (seg1). Work_mem: 9K bytes max.
Memory used: 2047000kB
Optimizer: Postgres query optimizer
Execution time: 1.266 ms
(11 rows)
-- Execute the query plan by using the jsonb_path_ops operator.
EXPLAIN ANALYZE SELECT * FROM jtest3 where jdoc @> '{"name":"N9WP5txmVu"}';
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
Gather Motion 2:1 (slice1; segments: 2) (cost=84.28..13513.81 rows=10101 width=88) (actual time=0.710..0.711 rows=1 loops=1)
-> Bitmap Heap Scan on jtest3 (cost=84.28..13513.81 rows=5051 width=88) (actual time=0.179..0.181 rows=1 loops=1)
Recheck Cond: (jdoc @> '{"name": "N9WP5txmVu"}'::jsonb)
-> Bitmap Index Scan on idx_jtest3 (cost=0.00..81.75 rows=5051 width=0) (actual time=0.106..0.106 rows=1 loops=1)
Index Cond: (jdoc @> '{"name": "N9WP5txmVu"}'::jsonb)
Planning time: 0.144 ms
(slice0) Executor memory: 69K bytes.
(slice1) Executor memory: 305K bytes avg x 2 workers, 309K bytes max (seg1). Work_mem: 9K bytes max.
Memory used: 2047000kB
Optimizer: Postgres query optimizer
Execution time: 1.291 ms
(11 rows)
次の例は、Pythonを使用してデータベースにアクセスする方法を示しています。
#! /bin/env python
import time
import json
import psycopg2
def gpquery(sql):
conn = None
try:
conn = psycopg2.connect("dbname=sanity1x2")
conn.autocommit = True
cur = conn.cursor()
cur.execute(sql)
return cur.fetchall()
except Exception as e:
if conn:
try:
conn.close()
except:
pass
time.sleep(10)
print e
return None
def main():
sql = "select obj from tj;"
#rows = Connection(host, port, user, pwd, dbname).query(sql)
rows = gpquery(sql)
for row in rows:
print json.loads(row[0])
if __name__ == "__main__":
main()