data
的列json
,其中包含这样的JSON文档:{
"name": "foo",
"tags": ["foo", "bar"]
}
我想将嵌套的
tags
数组转换为连接的字符串('foo, bar'
)。从理论上讲,使用array_to_string()
函数将很容易做到这一点。但是,此功能不接受json
输入。所以我想知道如何将这个JSON数组转换成Postgres数组(类型text[]
)?#1 楼
Postgres 9.4或更高版本显然受此帖子启发,Postgres 9.4添加了缺少的功能:感谢Laurence Rowe的补丁程序和Andrew Dunstan的贡献!
json_array_elements_text(json)
jsonb_array_elements_text(jsonb)
取消嵌套JSON数组。然后使用
array_agg()
或ARRAY构造函数从中构建一个Postgres数组。或使用string_agg()
构建一个text
字符串。在
LATERAL
或相关子查询中每行汇总未嵌套的元素。然后将保留原始顺序,并且在外部查询中不需要ORDER BY
,GROUP BY
甚至是唯一键。请参阅:如何将ORDER BY和LIMIT与聚合函数结合使用?
在以下所有SQL中将
jsonb
的'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;
短语法:
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;
相关:
LATERAL和PostgreSQL中的子查询有什么区别?
相关子查询中的ARRAY构造函数:
SELECT tbl_id, ARRAY(SELECT json_array_elements_text(t.data->'tags')) AS txt_arr
FROM tbl t;
相关:
如何结合聚合函数应用ORDER BY和LIMIT?
细微差别:
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))';
使其具有SQL函数,因此可以在较大的查询中内联。
使其具有
IMMUTABLE
(因为它是)以避免在较大的查询中重复求值并允许其在索引表达式中使用。 将其设为
PARALLEL SAFE
(在Postgres 9.6或更高版本中!)以免妨碍并行化。请参见:何时将功能标记为PARALLEL RESTRICTED与PARALLEL SAFE?
呼叫: /> db <>在这里拨弄
Postgres 9.3或更早版本
使用功能
json_array_elements()
。但是我们从中得到了双引号字符串。外部查询中带有聚合的替代查询。
CROSS JOIN
删除数组缺失或为空的行。对于处理元素也可能有用。我们需要一个唯一的键来聚合:SELECT tbl_id, json_arr2text_arr(data->'tags')
FROM tbl;
ARRAY构造函数,但仍带有引号的字符串:
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;
请注意,与上述不同,
null
转换为文本值“ null”。从严格意义上讲是不正确的,并且可能有歧义。穷人用
trim()
取消报价:SELECT tbl_id, ARRAY(SELECT json_array_elements(t.data->'tags')) AS quoted_txt_arr
FROM tbl t;
从tbl中检索一行:
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;
字符串形式相关子查询:
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;
ARRAY构造函数:
SELECT tbl_id, (SELECT string_agg(trim(value::text, '"'), ', ')
FROM json_array_elements(t.data->'tags')) AS list
FROM tbl t;
原始的(过时的)SQL Fiddle.db <>在这里拨弄。
相关:
需要从中动态选择JSON数组元素postgresql表
注释(自pg 9.4起已过时)
我们需要
json_array_elements_text(json)
,这是json_array_elements(json)
的孪生子,才能从JSON数组返回正确的text
值。但这似乎是所提供的JSON函数库中所缺少的。或其他一些函数从标量text
值中提取JSON
值。我似乎也错过了那个。所以我即兴使用
trim()
,但是对于非平凡的情况,这会失败... 评论
一如既往的好帖子,但是有了您对内部知识的了解,为什么不从array-> jsonb进行强制转换。我可以理解,由于sql-array具有更强的类型,因此无法实现其他类型的转换。仅仅是因为PostgreSQL反对自动生成要转换的代码(int [],bigint [],text [])等。
–埃文·卡洛尔(Evan Carroll)
17年1月17日在19:02
@Evan:您将使用to_jsonb()进行数组-> jsonb转换。
–欧文·布兰德斯特
17年1月19日在2:12
SELECT ARRAY(SELECT json_array_elements_text(_js))是否真的保证保留数组的顺序?从理论上讲,是否不允许优化器更改从json_array_elements_text出来的行的顺序?
– FelixGeisendörfer
19年8月29日在8:48
@Felix:SQL标准中没有正式的保证。 (同样,在标准SQL的SELECT列表中,甚至不允许设置返回函数。)但是Postgres手册中有一个非正式的断言。请参阅:dba.stackexchange.com/a/185862/3684-要明确-以轻微的性能损失为代价-请参阅:dba.stackexchange.com/a/27287/3684。就我个人而言,我100%确信自9.4起,该特定表达式在Postgres的每个当前和将来版本中都能正常工作。
–欧文·布兰德斯特
19年8月29日在9:52
@ErwinBrandstetter非常感谢您确认这一点!我目前正在做一篇文章的研究,该文章总结了PostgreSQL提供的正式和非正式担保订购担保,您的回答非常有用!如果您有兴趣阅读该文章,请告诉我,如果没有,请不要担心。我非常感谢您对StackOverflow所做的贡献,多年来,您从中学到了很多东西!
– FelixGeisendörfer
19年8月29日在11:36
#2 楼
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$;
然后再做:
SELECT jsonb_array_to_text_array('["a", "b", "c"]'::jsonb);
更新2/23/2020以回应评论:评论是正确的,因为这样做可能更有效。在我发布时,还没有提供模块化的解决方案,所以我认真地提供了一个解决方案,即使不是最优的。从那以后,Erwin用简单高效的功能更新了他的答案,所以我从未更新过我的答案。现在更新它,因为仍然有更多人关注此答案。
又有一个更新,因为这让我有点烦:
如果没有值,上述函数将返回
null
。根据您的情况,这可能不是理想的。这是一个函数,如果值不是null
,则返回空数组,但如果输入为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$
;
评论
我在回答中添加了一些更快的表达式,并且函数更简单。这可以便宜得多。
–欧文·布兰德斯特
2015年10月6日,下午2:46
该函数应该是纯SQL,以便优化程序可以窥视它。无需在此处使用pgplsql。
–划分
16年4月18日在10:00
...,并且应该是IMMUTABLE而不是VOLATILE,因为它不涉及数据库,并且任何给定的输入将始终生成相同的输出。
–user9645
2月19日下午17:55
#3 楼
在PostgreSQL邮件列表上问了这个问题,我想出了一种通过JSON字段提取运算符将JSON文本转换为PostgreSQL文本类型的方法:CREATE FUNCTION json_text(json) RETURNS text IMMUTABLE LANGUAGE sql
AS $$ SELECT ('['||||']')::json->>0 $$;
db=# select json_text(json_array_elements('["hello",1.3,"\u2603"]'));
json_text
-----------
hello
1.3
☃
它将值转换为单元素数组,然后要求第一个元素。
另一种方法是使用此运算符来一次提取所有字段。但是对于大型数组,这可能会更慢,因为它需要为每个数组元素解析整个JSON字符串,从而导致O(n ^ 2)复杂性。
CREATE FUNCTION json_array_elements_text(json) RETURNS SETOF text IMMUTABLE LANGUAGE sql
AS $$ SELECT ->>i FROM generate_series(0, json_array_length()-1) AS i $$;
db=# select json_array_elements_text('["hello",1.3,"\u2603"]');
json_array_elements_text
--------------------------
hello
1.3
☃
#4 楼
我已经测试了一些选项。这是我最喜欢的查询。假设我们有一个包含id和json字段的表。 json字段包含数组,我们希望将其转换为pg数组。SELECT *
FROM test
WHERE TRANSLATE(jsonb::jsonb::text, '[]','{}')::INT[]
&& ARRAY[1,2,3];
它在任何地方都可以工作,并且比其他任何地方都快,但是看起来有些残酷)
首先将json数组转换为文本,然后将方括号更改为括号。最终,文本被强制转换为所需类型的数组。
SELECT TRANSLATE('[1]'::jsonb::text, '[]','{}')::INT[];
,如果您喜欢text []数组
SELECT TRANSLATE('[1]'::jsonb::text, '[]','{}')::TEXT[];
评论
SELECT TRANSLATE('{“ name”:“ foo”,“ tags”:[“ foo”,“ bar”]}'::: jsonb :: text,'[]','{}'):: INT [] ;错误:格式不正确的数组文字:“ {” name“:” foo“,” tags“:{” foo“,” bar“}}”“我认为您必须添加一些有关此方法的解释。
– dezso
16 Dec 5'在9:20
问题是如何将JSON array(!)转换为pg数组。假设我有包含id和jsonb列的表。 JSONb列包含json数组。然后
–FiscalCliff
16 Dec 5'在19:12
TRANSLATE(jsonb :: jsonb :: text,'[]','{}'):: INT []将json数组转换为pg数组。
–FiscalCliff
16 Dec 5'在19:20
SELECT translation('[“ foo”,“ bar”]'::: jsonb :: text,'[]','{}'):: INT [];错误:整数:“ foo”的输入语法无效。
– dezso
16 Dec 6'在10:31
考虑对这些数组使用text []
–FiscalCliff
16 Dec 6'在11:27
#5 楼
这几个函数取自该问题的答案,它们是我正在使用的函数,它们在运行中效果很好。CREATE OR REPLACE FUNCTION json_array_casttext(json) RETURNS text[] AS $f$
SELECT array_agg(x) || ARRAY[]::text[] FROM json_array_elements_text() t(x);
$f$ LANGUAGE sql IMMUTABLE;
CREATE OR REPLACE FUNCTION jsonb_array_casttext(jsonb) RETURNS text[] AS $f$
SELECT array_agg(x) || ARRAY[]::text[] FROM jsonb_array_elements_text() t(x);
$f$ LANGUAGE sql IMMUTABLE;
CREATE OR REPLACE FUNCTION json_array_castint(json) RETURNS int[] AS $f$
SELECT array_agg(x)::int[] || ARRAY[]::int[] FROM json_array_elements_text() t(x);
$f$ LANGUAGE sql IMMUTABLE;
CREATE OR REPLACE FUNCTION jsonb_array_castint(jsonb) RETURNS int[] AS $f$
SELECT array_agg(x)::int[] || ARRAY[]::int[] FROM jsonb_array_elements_text() t(x);
$f$ LANGUAGE sql IMMUTABLE;
在每个函数中,通过与一个空值连接数组,它们处理了一个让我费尽脑筋的情况,因为如果您尝试从
json
/ jsonb
投射一个空数组,如果没有它,您将一无所获,而不是像空数组({}
)期望。我敢肯定他们有一些优化,但是为了简单起见,只剩下它们了。
评论
您正在寻找json_extract_path_text(your_column,'tags')吗?@a_horse_with_no_name:剩下的问题:数组元素仍被引用为JSON格式。文本提取不正确...