次のような構造で保存されているレガシーテーブルの複数の列を集約するクエリを作成しようとしています。
CREATE TEMPORARY TABLE foo AS
SELECT * FROM ( VALUES
(1,'Router','Networking','Sale',NULL),
(2,NULL,'Router','Networking','Sale'),
(3,NULL,NULL,'Networking','Sale'),
(4,NULL,NULL,NULL,NULL)
) AS t(id,tag_1,tag_2,tag_3,tag_4);
これは私が構築したいクエリの例です:
SELECT ID, json_build_array(Tag_1, Tag_2, Tag_3, Tag_4) AS tags
FROM table
問題は上記のクエリはNULL値を追加するを行から配列に追加することです。
ID Tags
--------------------------------------------------
1 ['Router', 'Networking', 'Sale', null]
2 [null, 'Router', 'Networking', 'Sale']
3 [null, null, 'Networking', 'Sale']
4 [null, null, null, null]
過度に複雑なCASE WHEN
ステートメントでNULLを除外しますでも、PostgreSQLのJSONデータ型を使用するのは初めてです。とにかく、PostgresでJSON配列を構築するときにNULLを含めないようにすることはできますか?
私は、JSON配列を使用せず、代わりにネイティブSQL配列構文を使用することをお勧めします。これは、おそらくはるかに高速で、より効率的に格納されます。また、より強く型付けされています。 JSON配列は、ドキュメントごとに"possibly-heterogeneously-typed"です。
私もこれを日常的にするつもりはありません。テーブルのスキーマを変更して、テーブルにARRAY
(SQLが望ましい)を持たせ、列にnullを格納せずにタグを格納します。これにより、スキーマを修正する道が開かれます。
ARRAYリテラルコンストラクターを使用するだけです。
SELECT id, ARRAY[tag_1,tag_2,tag_3,tag_4] FROM foo;
id | array
----+-------------------------------
1 | {Router,Networking,Sale,NULL}
2 | {NULL,Router,Networking,Sale}
3 | {NULL,NULL,Networking,Sale}
4 | {NULL,NULL,NULL,NULL}
SELECT id, json_build_array(tag_1,tag_2,tag_3,tag_4) FROM foo;
id | json_build_array
----+----------------------------------------
1 | ["Router", "Networking", "Sale", null]
2 | [null, "Router", "Networking", "Sale"]
3 | [null, null, "Networking", "Sale"]
4 | [null, null, null, null]
(4 rows)
coalesce
上記をarray_remove
でラップすることにより、1回のパスでnullを簡単にフィルタリングできます。
SELECT id, array_remove(ARRAY[tag_1,tag_2,tag_3,tag_4], null)
FROM foo;
id | array_remove
----+--------------------------
1 | {Router,Networking,Sale}
2 | {Router,Networking,Sale}
3 | {Networking,Sale}
4 | {}
SELECT id,jsonb_agg(elem)
FROM (SELECT id, ARRAY[tag_1,tag_2,tag_3,tag_4] FROM foo) AS g
CROSS JOIN LATERAL unnest(g.array)
WITH ORDINALITY AS t(elem,ord)
WHERE elem IS NOT NULL
GROUP BY id
ORDER BY id;
id | jsonb_agg
----+----------------------------------
1 | ["Router", "Networking", "Sale"]
2 | ["Router", "Networking", "Sale"]
3 | ["Networking", "Sale"]
これはうまくいくはずです:
SELECT
id,
( SELECT json_agg(tag ORDER BY no)
FROM
( SELECT 1, tag_1 UNION ALL
SELECT 2, tag_2 UNION ALL
SELECT 3, tag_3 UNION ALL
SELECT 4, tag_4
) AS x (no, tag)
WHERE tag IS NOT NULL
) AS tags
FROM t ;