PostgreSQLのPL/pgSQLのJSONについて。
|
PostgreSQLにはJSON(JavaScript Object Notation)を扱うデータ型としてjsonとjsonbがある。
jsonは入力データをそのまま保持し、使用するときに解析するらしい。
jsonbは入力時に解析するので、使用するのは高速らしい。
したがって、通常はjsonbを使えば良いらしい。
jsonbの変数へは、文字列でJSONを書いて渡せばいい。
declare j jsonb; begin j := '{"id":1, "name":"zzz"}'; raise info 'json = %', j; raise info 'json = %', jsonb_pretty(j); end
↓実行結果
INFO: json = {"id": 1, "name": "zzz"} INFO: json = { "id": 1, "name": "zzz" }
jsonb_prettyを使うと、整形された文字列が返ってくる。
(json_prettyは無いっぽい。json型の場合は解析せずにそのまま保持しているからかな?)
''
で囲んだJSONの後ろに「::jsonb
」を付けることでも指定できる。
明示的に変換するにはto_jsonbを使う。
raise info 'json = %', '{"id":1, "name":"zzz"}'::jsonb; raise info 'json = %', '123'::jsonb; raise info 'json = %', to_jsonb(123); raise info 'json = %', '"aaa"'::jsonb; raise info 'json = %', to_jsonb('aaa'::text);
↓実行結果
INFO: json = {"id": 1, "name": "zzz"} INFO: json = 123 INFO: json = 123 INFO: json = "aaa" INFO: json = "aaa"
テーブルのレコードをJSONに変換するのもto_jsonbで出来る。
declare h hoge%rowtype; i int; j jsonb; a jsonb[]; begin i := 0; for h in select * from hoge loop raise info 'h = %', h; i := i + 1; a[i] := to_jsonb(h); end loop; raise info 'a = %', a; foreach j in array a loop raise info 'j = %', j; end loop; end
→jsonb_populate_record(JSONからテーブルへの変換)
jsonb_build_objectでキーと値を並べて指定することが出来る。(JavaのMap.
of相当)
declare j jsonb; begin j := jsonb_build_object("key1", "value1", "key2", 123); raise info 'json = %', j; end
↓実行結果
INFO: json = {"key1": "value1", "key2": 123}
SELECTした結果をJSONにするには、jsonb_aggを使う。
1行が各カラム名とデータであり、各行を要素とする配列が返る。
(この配列はJSONとしての配列であり、PL/pgSQLの配列ではない。PL/pgSQLの配列への変換→jsonb_array_elements)
declare
j jsonb;
begin
select jsonb_agg(h.*) into j from hoge h;
raise info 'json = %', j;
end
↓実行結果
INFO: json = [{"h_id": 1, "h_name": "hoge1"}, {"h_id": 2, "h_name": "hoge2"}, {"h_id": 3, "h_name": "hoge3"}]
--サンプルデータ
hishidamadb=> select * from hoge;
h_id | h_name
------+--------
1 | hoge1
2 | hoge2
3 | hoge3
(3 行)
トップレベルが配列であるJSONは、以下のようにfor文を使って各要素を取得できる。
declare
j jsonb;
r jsonb;
begin
select jsonb_agg(h.*) into j from hoge h;
for r in select * from jsonb_array_elements(j) loop
raise info 'r = %', r;
end loop;
end
↓実行結果
INFO: r = {"h_id": 1, "h_name": "hoge1"} INFO: r = {"h_id": 2, "h_name": "hoge2"} INFO: r = {"h_id": 3, "h_name": "hoge3"}
jsonb_array_lengthでJSON配列の長さを取得できるので、それを使ってループさせることも出来る。
declare
j jsonb;
i int;
begin
select jsonb_agg(h.*) into j from hoge h;
for i in 0..jsonb_array_length(j) - 1 loop
raise info 'r = %', j->i;
end loop;
end
「jsonbの(PL/pgSQLとしての)配列」に変換する例。
declare
j jsonb;
a jsonb[];
begin
select jsonb_agg(h.*) into j from hoge h;
a = array(select * from jsonb_array_elements(j));
end
逆に、PL/pgSQLの配列をJSON配列にするにはarray_to_jsonという関数がある。
declare a int[]; j jsonb; begin a := array[1, 2, 3]; j := array_to_json(a); raise info 'j = %', j; end
JSON配列の各要素がテーブルのレコードである場合は、以下のようにfor文を使ってテーブルの1行分を取得できる。
declare j jsonb; r hoge%rowtype; begin select jsonb_agg(h.*) into j from hoge h; for r in select * from jsonb_populate_recordset(null::hoge, j) loop raise info 'r = %', r; raise info 'r.id = %', r.h_id; raise info 'r.nm = %', r.h_name; end loop; end
↓実行結果
INFO: r = (1,hoge1) INFO: r.id = 1 INFO: r.nm = hoge1 INFO: r = (2,hoge2) INFO: r.id = 2 INFO: r.nm = hoge2 INFO: r = (3,hoge3) INFO: r.id = 3 INFO: r.nm = hoge3
配列でなく、テーブルのレコードであるようなJSONに対しては、jsonb_populate_recordを使えば良い。
declare h hoge%rowtype; i int; j jsonb; a jsonb[]; begin i := 0; for h in select * from hoge loop i := i + 1; a[i] := to_jsonb(h); end loop; foreach j in array a loop h = jsonb_populate_record(null::hoge, j); raise info 'h = %', h; end loop; end
jsonb_to_recordsetの例。
for文で受け取る変数の型はrecordとし、select文のas句でカラム名とデータ型を指定する必要がある。
declare
j jsonb;
r record;
begin
select jsonb_agg(h.*) into j from hoge h;
for r in select * from jsonb_to_recordset(j) as(h_id int, h_name text) loop
raise info 'r = %', r;
raise info 'r.id = %', r.h_id;
raise info 'r.nm = %', r.h_name;
end loop;
end
配列でなく、テーブルのレコードであるようなJSONに対しては、jsonb_to_recordを使えば良さそう。
JSONの各要素の取得はjsonbの演算子によって行う。
declare
j jsonb;
jj jsonb;
i int;
begin
select jsonb_agg(h.*) into jj from hoge h;
for i in 0..jsonb_array_length(jj) - 1 loop
j := jj->i;
raise info 'j = %', j;
raise info 'j.id = %', j->>'h_id';
end loop;
end
演算子 | 返り型 | 例 | 例の結果 | 説明 |
---|---|---|---|---|
->添字 |
jsonb |
j := '[{"a":"foo"}, {"b":"zzz"}]'; raise info 'j[0] = %', j->0; |
INFO: j[0] = {"a": "foo"} |
JSON配列の要素を取得する。 添字は(PL/pgSQLの配列と異なり)0から始まる。 範囲外の添字を指定するとNULLが返る。 |
->'キー' |
jsonb |
j := '{"a":"foo", "b":"zzz"}'; raise info 'j[a] = %', j->'a'; |
INFO: j[a] = "foo" |
指定されたキーの値を取得する。 存在しないキーを指定するとNULLが返る。 |
->>添字 |
text |
j := '[{"a":"foo"}, {"b":"zzz"}]'; raise info 'j[0] = %', j->>0; |
INFO: j[0] = {"a": "foo"} |
JSON配列の要素をtextとして取得する。 |
->>'キー' |
text |
j := '{"a":"foo", "b":"zzz"}'; raise info 'j[a] = %', j->>'a'; |
INFO: j[a] = foo |
指定されたキーの値をtextとして取得する。 |
#>'{添字orキー,…}' |
jsonb |
j := '{"a": {"b":{"c": "foo"}}}'; raise info 'j[a/b] = %', j#>'{a,b}'; |
INFO: j[a/b] = {"c": "foo"} |
指定された位置にあるJSONオブジェクトを取得する。 |
#>>'{添字orキー,…}' |
text |
JSONオブジェクト(jsonb)として欲しい場合は「->
」、textとして欲しい場合(JSONの末端)は「->>
」を使うのが良いようだ。
JSONオブジェクト(jsonb)を直接更新する方法は無いっぽい?
jsonbを変更した新しいjsonbを作る方法はある。
関数・ 演算子 |
返り型 | 例 | 例の結果 | 説明 |
---|---|---|---|---|
jsonb_set |
jsonb |
j := '{"a":"foo", "b":"zzz"}'; j := jsonb_set(j, '{a}', to_jsonb('bar'::text)); raise info 'j = %', j; |
INFO: j = {"a": "bar", "b": "zzz"} |
第2引数が更新対象のキー。 第3引数が新しい値(jsonb)。 |
jsonb_insert |
jsonb |
j := '[{"a":"foo"}, {"a":"zzz"}]'; j := jsonb_insert(j, '{1}', '{"a":"bar"}'::jsonb, true); raise info 'j = %', j; |
INFO: j = [{"a": "foo"}, {"a": "zzz"}, {"a":
"bar"}] |
第2引数が挿入する位置。 第3引数が新しい値(jsonb)。 第4引数は、挿入する位置が指定された位置の後ろか前か(デフォルトはfalseで前)。 |
|| |
jsonb |
j := '[{"a":"foo"}, {"a":"zzz"}]'; j := j || '{"a":"bar"}'::jsonb; raise info 'j = %', j; |
INFO: j = [{"a": "foo"}, {"a": "zzz"}, {"a":
"bar"}] |
JSONを追加する。 |
- |
jsonb |
j := '{"a":"foo", "b":"zzz"}'; j := j - 'b'; raise info 'j = %', j; |
INFO: j = {"a": "foo"} |
JSONから要素を削除する。 |
#- |
jsonb |
j := '{"a":"foo", "b":"zzz"}'; j := j #- '{b}'; raise info 'j = %', j; |
INFO: j = {"a": "foo"} |