我有一列data
,其中包含一个json
大致像这样的文档:
{
"name": "foo",
"tags": ["foo", "bar"]
}
我想将嵌套tags
数组转换为串联字符串(foo, bar
)。array_to_string()
从理论上讲,这很容易实现。但是,此功能不适用于json
数组。所以我想知道如何将该json
数组转换为Postgres array
?
我有一列data
,其中包含一个json
大致像这样的文档:
{
"name": "foo",
"tags": ["foo", "bar"]
}
我想将嵌套tags
数组转换为串联字符串(foo, bar
)。array_to_string()
从理论上讲,这很容易实现。但是,此功能不适用于json
数组。所以我想知道如何将该json
数组转换为Postgres array
?
Answers:
显然是受本文启发的,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;
简短语法:
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;
有关:
相关子查询中的ARRAY构造函数:
SELECT tbl_id, ARRAY(SELECT json_array_elements_text(t.data->'tags')) AS txt_arr
FROM tbl t;
有关:
细微的差别:null
元素保留在实际数组中。在上面的查询中,产生text
字符串(不能包含null
值)是不可能的。的真实表示是一个数组。
为了使重复使用更加简单,请将逻辑封装在一个函数中:
CREATE OR REPLACE FUNCTION json_arr2text_arr(_js json)
RETURNS text[] LANGUAGE sql IMMUTABLE AS
'SELECT ARRAY(SELECT json_array_elements_text(_js))';
将其设为SQL函数,以便可以在较大的查询中内联。
设置它IMMUTABLE
(因为是这样)是为了避免在较大的查询中重复评估,并允许它在索引表达式中使用。
呼叫:
SELECT tbl_id, json_arr2text_arr(data->'tags')
FROM tbl;
使用功能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;
ARRAY构造函数,仍然使用带引号的字符串:
SELECT tbl_id, ARRAY(SELECT json_array_elements(t.data->'tags')) AS quoted_txt_arr
FROM tbl t;
请注意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;
从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;
字符串形式的相关子查询:
SELECT tbl_id, (SELECT string_agg(trim(value::text, '"'), ', ')
FROM json_array_elements(t.data->'tags')) AS list
FROM tbl t;
ARRAY构造函数:
SELECT tbl_id, ARRAY(SELECT trim(value::text, '"')
FROM json_array_elements(t.data->'tags')) AS txt_arr
FROM tbl t;
原始(已过时)SQL Fiddle。
db <>在这里拨弄。
有关:
我们需要a json_array_elements_text(json)
的孪生子,json_array_elements(json)
以text
从JSON数组返回正确的值。但这似乎是所提供的JSON函数库中缺少的。或其他一些text
从标JSON
量值中提取值的函数。我似乎也想念那个。
因此,我即兴创作了trim()
,但对于非平凡的案例,这将失败...
to_jsonb()
array-> jsonb转换。
SELECT ARRAY(SELECT json_array_elements_text(_js))
真的保证数组的排序被保留?从理论上讲,是否不允许优化器更改从json_array_elements_text出来的行的顺序?
PG 9.4以上
可接受的答案肯定是您所需要的,但是为了简单起见,我在这里使用了一个帮助程序:
CREATE OR REPLACE FUNCTION jsonb_array_to_text_array(
p_input jsonb
) RETURNS TEXT[] AS $BODY$
DECLARE v_output text[];
BEGIN
SELECT array_agg(ary)::text[]
INTO v_output
FROM jsonb_array_elements_text(p_input) AS ary;
RETURN v_output;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
然后做:
SELECT jsonb_array_to_text_array('["a", "b", "c"]'::jsonb);
在PostgreSQL邮件列表上问了这个问题,我想出了一种通过JSON字段提取运算符将JSON文本转换为PostgreSQL文本类型的方法:
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
☃
基本上,它将值转换为单元素数组,然后要求第一个元素。
另一种方法是使用此运算符来一次提取所有字段。但是对于大型数组,这可能会更慢,因为它需要解析每个数组元素的整个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
☃
我已经测试了一些选项。这是我最喜欢的查询。假设我们有一个包含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[]; ERROR: malformed array literal: "{"name": "foo", "tags": {"foo", "bar"}}"
我认为您必须添加一些有关应该如何工作的解释。
SELECT translate('["foo", "bar"]'::jsonb::text, '[]','{}')::INT[]; ERROR: invalid input syntax for integer: "foo"
它不是那么防弹……
这几个功能取自该问题的答案,它们是我正在使用的并且运行良好
CREATE OR REPLACE FUNCTION json_array_casttext(json) RETURNS text[] AS $f$
SELECT array_agg(x) || ARRAY[]::text[] FROM json_array_elements_text($1) 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($1) 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($1) 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($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;
在它们的每一个中,通过与一个空数组连接,它们处理了一个让我绞尽脑汁的情况,因为如果您尝试从json
/ 抛弃一个空数组,jsonb
您将一无所获,而不是返回{}
如您所料,清空数组()。我敢肯定它们有一些优化,但是为了简单起见,只保留了它们。
json_extract_path_text(your_column, 'tags')
你在找什么?