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 添加了缺失的功能:
感谢 Laurence Rowe 的补丁和 Andrew Dunstan 的提交!
取消嵌套 JSON 数组。然后使用array_agg()或ARRAY 构造函数从它构建一个 Postgres数组。或者string_agg()构建一个text 字符串。
聚合一个LATERAL或相关子查询中每行的未嵌套元素。然后保留原始顺序,我们不需要ORDER BY,GROUP 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<>在这里摆弄
使用函数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<>在这里小提琴。
有关的:
我们需要 a json_array_elements_text(json),它的孪生体从 JSON 数组json_array_elements(json)返回正确的text值。但是所提供的 JSON 函数库中似乎缺少这一点。或者其他一些text从标JSON量值中提取值的函数。我好像也错过了那个。
所以我即兴创作trim(),但对于非平凡的情况,这将失败......
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)
这个问题是在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)
我测试了几个选项。这是我最喜欢的查询。假设我们有一个包含 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)
| 归档时间: |
|
| 查看次数: |
280636 次 |
| 最近记录: |