如何将 JSON 数组转换为 Postgres 数组?

Chr*_*oph 97 postgresql array postgresql-9.3 json

我有一个data类型的列,json其中包含这样的 JSON 文档:

{
    "name": "foo",
    "tags": ["foo", "bar"]
}
Run Code Online (Sandbox Code Playgroud)

我想将嵌套tags数组转换为连接字符串 ( 'foo, bar')。array_to_string()从理论上讲,使用该函数很容易做到这一点。但是,此功能不接受json输入。所以我想知道如何将这个 JSON 数组变成 Postgres 数组(类型text[])?

Erw*_*ter 134

Postgres 9.4 或更新版本

显然受到这篇文章的启发,Postgres 9.4 添加了缺失的功能:
感谢 Laurence Rowe 的补丁和 Andrew Dunstan 的提交!

取消嵌套 JSON 数组。然后使用array_agg()ARRAY 构造函数从它构建一个 Postgres数组。或者string_agg()构建一个text 字符串

聚合一个LATERAL或相关子查询中每行的未嵌套元素。然后保留原始顺序,我们不需要ORDER BYGROUP BY甚至不需要外部查询中的唯一键。看:

jsonb在以下所有 SQL 代码中将 'json' 替换为 'jsonb' 。

SELECT t.tbl_id, d.list
FROM   tbl t
CROSS  JOIN LATERAL (
   SELECT string_agg(d.elem::text, ', ') AS list
   FROM   json_array_elements_text(t.data->'tags') AS d(elem)
   ) d;
Run Code Online (Sandbox Code Playgroud)

简短的语法:

SELECT t.tbl_id, d.list
FROM   tbl t, LATERAL (
   SELECT string_agg(value::text, ', ') AS list
   FROM   json_array_elements_text(t.data->'tags')  -- col name default: "value"
   ) d;
Run Code Online (Sandbox Code Playgroud)

有关的:

相关子查询中的 ARRAY 构造函数:

SELECT tbl_id, ARRAY(SELECT json_array_elements_text(t.data->'tags')) AS txt_arr
FROM   tbl t;
Run Code Online (Sandbox Code Playgroud)

有关的:

细微差别null元素保留在实际数组中。这在上述生成text字符串的查询中是不可能的,该字符串不能包含null值。的真实表示是一个数组。

函数包装器

为了重复使用,为了更简单,将逻辑封装在一个函数中:

CREATE OR REPLACE FUNCTION json_arr2text_arr(_js json)
  RETURNS text[] LANGUAGE sql IMMUTABLE PARALLEL SAFE AS
'SELECT ARRAY(SELECT json_array_elements_text(_js))';
Run Code Online (Sandbox Code Playgroud)

使其成为SQL 函数,以便可以在更大的查询中内联
使它IMMUTABLE(因为它是)避免在更大的查询中重复计算并允许它在索引表达式中。
使其PARALLEL SAFE(在Postgres 9.6或更高版本中!)不妨碍并行性。看:

称呼:

SELECT tbl_id, json_arr2text_arr(data->'tags')
FROM   tbl;
Run Code Online (Sandbox Code Playgroud)

db<>在这里摆弄


Postgres 9.3 或更高版本

使用函数json_array_elements()。但是我们从中得到了双引号字符串

在外部查询中进行聚合的替代查询。CROSS JOIN删除缺少或空数组的行。也可用于处理元素。我们需要一个唯一的键来聚合:

SELECT t.tbl_id, string_agg(d.elem::text, ', ') AS list
FROM   tbl t
CROSS  JOIN LATERAL json_array_elements(t.data->'tags') AS d(elem)
GROUP  BY t.tbl_id;
Run Code Online (Sandbox Code Playgroud)

ARRAY 构造函数,仍然使用带引号的字符串:

SELECT tbl_id, ARRAY(SELECT json_array_elements(t.data->'tags')) AS quoted_txt_arr
FROM   tbl t;
Run Code Online (Sandbox Code Playgroud)

请注意null,与上面不同,它被转换为文本值“null”。不正确,严格来说,可能是模棱两可的。

穷人不引用trim()

SELECT t.tbl_id, string_agg(trim(d.elem::text, '"'), ', ') AS list
FROM   tbl t, json_array_elements(t.data->'tags') d(elem)
GROUP  BY 1;
Run Code Online (Sandbox Code Playgroud)

从 tbl 中检索单行:

SELECT string_agg(trim(d.elem::text, '"'), ', ') AS list
FROM   tbl t, json_array_elements(t.data->'tags') d(elem)
WHERE  t.tbl_id = 1;
Run Code Online (Sandbox Code Playgroud)

字符串形成相关子查询:

SELECT tbl_id, (SELECT string_agg(trim(value::text, '"'), ', ')
                FROM   json_array_elements(t.data->'tags')) AS list
FROM   tbl t;
Run Code Online (Sandbox Code Playgroud)

数组构造函数:

SELECT tbl_id, ARRAY(SELECT trim(value::text, '"')
                     FROM   json_array_elements(t.data->'tags')) AS txt_arr
FROM   tbl t;
Run Code Online (Sandbox Code Playgroud)

原始(过时)SQL Fiddle
db<>在这里小提琴

有关的:

注释(自第 9.4 页起已过时)

我们需要 a json_array_elements_text(json),它的孪生体从 JSON 数组json_array_elements(json)返回正确的text值。但是所提供的 JSON 函数库中似乎缺少这一点。或者其他一些text从标JSON量值中提取值的函数。我好像也错过了那个。
所以我即兴创作trim(),但对于非平凡的情况,这将失败......

  • @Evan:你会使用 [**`to_jsonb()`**](https://www.postgresql.org/docs/current/static/functions-json.html#FUNCTIONS-JSON-CREATION-TABLE)数组-&gt;jsonb 转换。 (3认同)

and*_*ter 21

PG 9.4+

接受的答案绝对是您所需要的,但为了简单起见,这里是我用于此目的的帮助程序:

CREATE OR REPLACE FUNCTION jsonb_array_to_text_array(p_input jsonb)
 RETURNS text[]
 LANGUAGE sql
 IMMUTABLE
AS $function$

SELECT array_agg(ary)::text[] FROM jsonb_array_elements_text(p_input) AS ary;

$function$;
Run Code Online (Sandbox Code Playgroud)

然后就这样做:

SELECT jsonb_array_to_text_array('["a", "b", "c"]'::jsonb);
Run Code Online (Sandbox Code Playgroud)

2020 年 2 月 23 日更新以回应评论:评论是正确的,这可能会更有效。在我发布时,没有提供模块化的解决方案,所以我认真地提供了一个,如果不是最优的。从那时起,埃尔文用一个简单有效的功能更新了他的答案,所以我从未更新过我的答案。现在更新它,因为仍然有人关注这个答案

再更新一次,因为这只是让我有点null如果没有值,上面的函数将返回。根据您的情况,这可能是不可取的。这是一个函数,如果值不是null,则返回空数组,但如果输入为空,则仍返回空。

CREATE OR REPLACE FUNCTION jsonb_array_to_text_array_strict(p_input jsonb)
 RETURNS text[]
 LANGUAGE sql
 IMMUTABLE
AS $function$

SELECT 
  CASE 
    WHEN p_input IS null 
    THEN null 
    ELSE coalesce(ary_out, ARRAY[]::text[]) 
  END
FROM (
  SELECT array_agg(ary)::text[] AS ary_out
  FROM jsonb_array_elements_text(p_input) AS ary
) AS extracted;

$function$
;
Run Code Online (Sandbox Code Playgroud)

  • 这个函数应该是纯 SQL 以便优化器可以窥视它。这里不需要使用 pgplsql。 (7认同)
  • ...它应该是 IMMUTABLE 而不是 VOLATILE,因为它不接触数据库并且任何给定的输入将始终生成相同的输出。 (2认同)

int*_*tgr 8

这个问题是在PostgreSQL 邮件列表上提出的,我想出了这种通过 JSON 字段提取运算符将 JSON 文本转换为 PostgreSQL 文本类型的hackish 方法:

CREATE FUNCTION json_text(json) RETURNS text IMMUTABLE LANGUAGE sql
AS $$ SELECT ('['||$1||']')::json->>0 $$;

db=# select json_text(json_array_elements('["hello",1.3,"\u2603"]'));
 json_text 
-----------
 hello
 1.3
 ?
Run Code Online (Sandbox Code Playgroud)

基本上它将值转换为单元素数组,然后请求第一个元素。

另一种方法是使用此运算符逐个提取所有字段。但是对于大型数组,这可能会更慢,因为它需要为每个数组元素解析整个 JSON 字符串,从而导致 O(n^2) 复杂度。

CREATE FUNCTION json_array_elements_text(json) RETURNS SETOF text IMMUTABLE LANGUAGE sql
AS $$ SELECT $1->>i FROM generate_series(0, json_array_length($1)-1) AS i $$;

db=# select json_array_elements_text('["hello",1.3,"\u2603"]');
 json_array_elements_text 
--------------------------
 hello
 1.3
 ?
Run Code Online (Sandbox Code Playgroud)


Fis*_*iff 6

我测试了几个选项。这是我最喜欢的查询。假设我们有一个包含 id 和 json 字段的表。json 字段包含数组,我们希望将其转换为 pg 数组。

SELECT * 
FROM   test 
WHERE  TRANSLATE(jsonb::jsonb::text, '[]','{}')::INT[] 
       && ARRAY[1,2,3];
Run Code Online (Sandbox Code Playgroud)

它可以在任何地方工作并且比其他人更快,但看起来很笨拙)

首先将json数组转换为文本,然后我们只需将方括号更改为括号即可。最后,文本被转换为所需类型的数组。

SELECT TRANSLATE('[1]'::jsonb::text, '[]','{}')::INT[];
Run Code Online (Sandbox Code Playgroud)

如果你更喜欢 text[] 数组

SELECT TRANSLATE('[1]'::jsonb::text, '[]','{}')::TEXT[];
Run Code Online (Sandbox Code Playgroud)

  • `SELECT TRANSLATE('{"name": "foo", "tags": ["foo", "bar"]}'::jsonb::text, '[]','{}')::INT[ ]; 错误:格式错误的数组文字:"{"name": "foo", "tags": {"foo", "bar"}}"` 我认为你必须添加一些关于它应该如何工作的解释。 (2认同)

归档时间:

查看次数:

280636 次

最近记录:

6 年,5 月 前