web-dev-qa-db-ja.com

NULL要素のない複数の列から配列を作成する

次のような構造で保存されているレガシーテーブルの複数の列を集約するクエリを作成しようとしています。

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を含めないようにすることはできますか?

8
Sathariel

私は、JSON配列を使用せず、代わりにネイティブSQL配列構文を使用することをお勧めします。これは、おそらくはるかに高速で、より効率的に格納されます。また、より強く型付けされています。 JSON配列は、ドキュメントごとに"possibly-heterogeneously-typed"です。

私もこれを日常的にするつもりはありません。テーブルのスキーマを変更して、テーブルにARRAY(SQLが望ましい)を持たせ、列にnullを格納せずにタグを格納します。これにより、スキーマを修正する道が開かれます。

アレイの構築

厳密に型指定されたPostgreSQL配列

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}

JSON配列

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)

手動でのNullのフィルタリングcoalesce

厳密に型指定されたPostgreSQL配列

上記を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 | {}

JSON配列

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"]
11
Evan Carroll

これはうまくいくはずです:

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  ;
3
ypercubeᵀᴹ