複数のテーブルから階層的な JSON 結果を作成しています。これらは単なる例ですが、このデモンストレーションの目的を理解するのに十分なはずです。
CREATE TABLE book (
id INTEGER PRIMARY KEY NOT NULL,
data JSONB
);
CREATE TABLE author (
id INTEGER PRIMARY KEY NOT NULL,
data JSONB
);
CREATE TABLE book_author (
id INTEGER PRIMARY KEY NOT NULL,
author_id INTEGER,
book_id INTEGER
);
CREATE UNIQUE INDEX pk_unique ON book_author (author_id, book_id);
テストデータ:
INSERT INTO book (id, data) VALUES
(1, '{"pages": 432, "title": "2001: A Space Odyssey"}')
, (2, '{"pages": 300, "title": "The City And The City"}')
, (3, '{"pages": 143, "title": "Unknown Book"}');
INSERT INTO author (id, data) VALUES
(1, '{"age": 90, "name": "Arthur C. Clarke"}')
, (2, '{"age": 43, "name": "China Miéville"}');
INSERT INTO book_author (id, author_id, book_id) VALUES
(1, 1, 1)
, (2, 1, 2);
次の関数を作成しました。
CREATE OR REPLACE FUNCTION public.book_get()
RETURNS json AS
$BODY$
DECLARE
result json;
BEGIN
SELECT to_json(array_agg(_b)) INTO result
FROM (
SELECT
book.id id,
book.data->>'title' title,
book.data->>'pages' pages,
(
SELECT to_json(array_agg(_a))
FROM (
SELECT
author.id id,
author.data->>'name' "name",
author.data->>'age' age
FROM
author, book_author ba
WHERE
ba.author_id = author.id AND
ba.book_id = book.id
ORDER BY id
) _a
) authors
FROM
book
ORDER BY id ASC
) _b;
RETURN result;
END;
$BODY$ LANGUAGE plpgsql VOLATILE;
関数の実行book_get
SELECT book_get();
次の結果が生成されます
[
{
"id":1,
"title":"2001: A Space Odyssey",
"pages":432,
"authors":[
{
"id":1,
"name":"Arthur C. Clarke",
"age":90
}
]
},
{
"id":2,
"title":"The City And The City",
"pages":300,
"authors":[
{
"id":2,
"name":"China Miéville",
"age":43
}
]
},
{
"id":3,
"title":"Unknown Book",
"pages":143,
"authors":null
}
]
WHERE
これで、句を使用してデータをフィルタリングできるようになりました。
SELECT to_json(array_agg(_b)) INTO result
FROM (
...
) _b
-- give me the book with id 1
WHERE _b.id = 1;
-- or give me all titles with the occurrence of 'City' anywhere
WHERE _b.title LIKE '%City%';
-- or has more than 200 pages
WHERE _b.pages > 200;
でフィルタリングできるようにするにはどうすればよいauthors
ですか? たとえば、 に相当するものWHERE _b.authors.'name' = 'Arthur C. Clarke'
。
authors
どのタイプになるか全くわかりません。それとも?それはまだレコード(配列)ですか?もうJSONですか?にアクセスできるからだと思いますid
が、アクセスすることはそれほど問題ではありませんか?title
pages
_b.authors
アクセス_b.authors
すると私にERROR: missing FROM-clause entry for table "authors"
JSON 演算子でアクセスすると、_b.authors->>..
または_b->authors->>..
operator does not exist: record -> json Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
句で使用GROUP BY
したことを覚えています:HAVING
GROUP BY _b.authors
HAVING _b.authors->>'name' = 'Arthur C. Clarke';
しかし、それは私にエラーを与えます:
エラー: タイプ json の等価演算子を識別できませんでした
もう少し明確にするために:
SELECT to_json(array_agg(_b)) INTO result
FROM (
...
) _b
WHERE _b.authors->0->>'name' = 'Arthur C. Clarke';
基本的に必要なことを行います。これは、インデックスの作成者が である場合にのみ一致し0
ますArthur C. Clarke
。彼が本を共同執筆し、彼が 2 位 (インデックス 1) だった場合、一致はありませんでした。だから私が見つけようとしているのは、_b.authors
たまたま著者で満たされた JSON 配列であるスキャンの正しい構文です。どんな試みも受け入れません。私が理解している限りでは@>
、#>
のみがサポートされていJSONB
ます。_b.authors
では、値に対して任意の列を選択する際に正しい構文を取得するにはどうすればよいですか。
更新 2
ドキュメントをもう一度読んでください... Postgresのドキュメントから、関数に関してJSONとJSONBに違いがあるという部分を取得できなかったようです。データ型のみに関するものだと思いました。where 句で etc のような演算子を使用すると、と置き換えるto_json
とうまくいくようです。to_jsonb
@>
アップデート 3
@ErwinBrandstetter: 理にかなっています。LATERAL は私にはまだ知られていませんでしたが、存在することを知ってよかったです。私は JSON/JSONB の関数と演算子に慣れてきました。今ではとても理にかなっています。私には明確ではないのはLIKE
、たとえばWHERE
節での出現を見つけることです。
jsonb_array_elements
配列内のオブジェクトのネストを解除するために使用する必要がある場合(最後のWHERE
節では、の内容がb.authors
JSONB データ型であるため)。私はそれからすることができました
SELECT * FROM jsonb_array_elements('[
{"age": 90, "name": "the Arthur C. Clarke"},
{"age": 43, "name": "China Miéville"},
{"age": null, "name": "Erwin the Brandstetter"}
]'::jsonb) author
WHERE
author->>'name' LIKE '%the%';
望ましい結果が得られます。
1: {"age": 90, "name": "the Arthur C. Clarke"}
2: {"age": null, "name": "Erwin the Brandstetter"}
しかし、私の例の最後の(最後の)WHERE
節でこれを達成するためのアプローチは何ですか? WHERE
結果の完全なセットをフィルタリングし、サブ選択の途中で部分的にフィルタリングしたくないため、最後の句を指摘します。したがって、一般的には、著者のミドルネームが「C」である本を最終的な結果セットから除外したいと考えています。または名前「アーサー」。
更新 4
FROM
もちろん節で。すべての可能性を見つけたら、最後にパフォーマンスの微調整を行う必要がありますが、これが私が思いついたものです。
SELECT json_agg(_b) INTO result
FROM (
...
) _b,
jsonb_array_elements(_b.authors) AS arrauthors
WHERE arrauthors->>'name' LIKE 'Arthur %';
'Arthur' で始まる著者名を持つすべての本を提供します。このアプローチへのコメントや更新に感謝します。