PostgreSQL JSON 构建一个没有空值的数组

PostgreSQL JSON building an array without null values

以下查询

SELECT jsonb_build_array(jsonb_build_object('use', 'Home'), 
        CASE WHEN 1 = 2 THEN jsonb_build_object('use', 'Work')
        END)

产生

[{"use":"Home"},null]

当我真正想要的时候

[{"use":"Home"}]

我该怎么做? json_strip_nulls() 对我不起作用。

创建自定义函数似乎是最简单的方法。

create or replace function jsonb_build_array_without_nulls(variadic anyarray)
returns jsonb language sql immutable as $$
    select jsonb_agg(elem)
    from unnest() as elem
    where elem is not null
$$;

select 
    jsonb_build_array_without_nulls(
        jsonb_build_object('use', 'home'), 
        case when 1 = 2 then jsonb_build_object('use', 'work') end
    )

 jsonb_build_array_without_nulls 
---------------------------------
 [{"use": "home"}]
(1 row) 

通过使用像这样的 PostgreSQL 数组:

SELECT array_to_json(array_remove(ARRAY[jsonb_build_object('use', 'Home'), 
        CASE WHEN 1 = 2 THEN jsonb_build_object('use', 'Work') END], null))

确实会产生:

[{"use": "Home"}]

同时,可以肯定的是:

SELECT array_to_json(array_remove(ARRAY[jsonb_build_object('use', 'Home'), 
        CASE WHEN 1 = 2 THEN jsonb_build_object('use', 'Work') END,
        jsonb_build_object('real_use', 'NotHome')], null))

产生:

[{"use": "Home"},{"real_use": "NotHome"}]

我假设这个查询是以某种方式动态生成的。如果您控制了 SQL 代,您也可以改用 ARRAY_AGG(...) FILTER(...),根据您的实际查询,这可能比使用所有不同的 更方便。

SELECT (
  SELECT json_agg(v) FILTER (WHERE v IS NOT NULL)
  FROM (
    VALUES 
      (jsonb_build_object('use', 'Home')),
      (CASE WHEN 1 = 2 THEN jsonb_build_object('use', 'Work') END)
  ) t (v)
)

或者:

SELECT (
  SELECT json_agg(v)
  FROM (
    VALUES 
      (jsonb_build_object('use', 'Home')),
      (CASE WHEN 1 = 2 THEN jsonb_build_object('use', 'Work') END)
  ) t (v)
  WHERE v IS NOT NULL
)

另一种处理方法如下:

SELECT jsonb_build_array(
  jsonb_build_object('use', 'Home'), 
  CASE 
    WHEN 1 = 2 THEN jsonb_build_object('use', 'Work')
    ELSE '"null"'
  END
) - 'null'

(不幸的是,在 postgres 或大多数其他数据库中,null 本身不可能做很多事情)

在上面的例子中,'"null"' 可以替换为任何不会被误认为数组中实时数据的唯一字符串。我不会使用数字,因为 - 0 实际上会尝试从数组中删除第一项,而不是数组中的数字。但是你可以使用 '"0"' 并根据需要使用 - '0' 之类的东西删除。

对于那些不使用 CASE 的人,可以使用 COALESCE 将空值转换为所需的字符串(唉,没有 NVLIFNULLISNULL 在 postgres 中,但至少 COALESCE 是可移植的)