如何将json数组转换为文本数组?

dl5txlt9  于 2023-01-06  发布在  其他
关注(0)|答案(6)|浏览(475)

此变通方法无效

CREATE FUNCTION json_array_castext(json) RETURNS text[] AS $f$
  SELECT array_agg(x::text) FROM json_array_elements($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;

-- Problem:
SELECT 'hello'='hello';  -- true...
SELECT  (json_array_castext('["hello","world"]'))[1] = 'hello'; -- false!

那么,如何获取真实的的文本数组呢?
PS:对于所谓的“一等公民”JSONb,同样的问题。
编辑:@OtoShavadze回答得很好(评论解决了!),一个 * 清单PostgreSQL开发人员 *:为什么x::text不是强制类型转换?(使用9.5.6页)以及为什么它不生成警告或错误?

wfauudbj

wfauudbj1#

尝试使用json_array_elements_text代替json_array_elements,并且不需要显式转换为文本(x::text),因此可以用途:

CREATE or replace FUNCTION json_array_castext(json) RETURNS text[] AS $f$
    SELECT array_agg(x) FROM json_array_elements_text($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;

对于您的其他问题
为什么x::text不是强制类型转换?
这是强制类型转换,因此它不会给出任何错误,但是当将json字符串转换为如下文本时:::text,postgres将引号添加到值中。
出于测试目的,让我们再次将函数更改为原始函数(如问题中所示),然后尝试:

SELECT  
(json_array_castext('["hello","world"]'))[1] = 'hello',
(json_array_castext('["hello","world"]'))[1],
'hello'

如您所见,(json_array_castext('["hello","world"]'))[1]给出"hello"而不是hello,这就是为什么在比较这些值时得到false

ocebsuys

ocebsuys2#

CREATE or replace FUNCTION json_to_array(json) RETURNS text[] AS $f$
  SELECT coalesce(array_agg(x), 
    CASE WHEN $1 is null THEN null ELSE ARRAY[]::text[] END)
  FROM json_array_elements_text($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;

测试用例:

  • select json_to_array('["abc"]') =〉单元素数组
  • select json_to_array('[]') =〉空数组
  • select json_to_array(null) =〉空
s5a0g9ez

s5a0g9ez3#

对于PostgreSQL的这种丑陋行为,有一个丑陋的强制转换解决方案,操作符#>>'{}'

CREATE or replace FUNCTION json_array_castext(json) RETURNS text[] AS $f$
    SELECT array_agg(x#>>'{}') FROM json_array_elements($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;

SELECT  (json_array_castext('["hello","world"]'))[1] = 'hello'; -- true!

(编辑)2020年,第12页性能检查

我们期望专用函数json_array_elements_text()比用户定义的强制转换好...但是,好多少?2倍?20倍...还是只有百分之几?
有时我们不能使用它,所以,有一些性能损失?
准备测试:

CREATE TABLE j_array_test AS -- JSON
  SELECT   array_to_json(array[x,10000,2222222,33333333,99999,y]) AS j
  FROM generate_series(1, 1900) t1(x), generate_series(1, 1900) t2(y);

CREATE TABLE jb_array_test AS --JSONb
  SELECT   to_jsonb(array[x,10000,2222222,33333333,99999,y]) AS j
  FROM generate_series(1, 1900) t1(x), generate_series(1, 1900) t2(y);

CREATE FUNCTION ...

函数名称:

  • j操作转换(json) 使用array_agg(x#>>'{}') FROM json_array_elements($1)
  • jb操作转换(jsonb) 使用array_agg(x#>>'{}') FROM jsonb_array_elements($1)
  • j函数类型转换(json) 使用array_agg(x) FROM json_array_elements_text($1)
  • jb函数类型转换(jsonb) 使用array_agg(x) FROM jsonb_array_elements_text($1)
    **结果:**所有结果几乎相同,报告的差异仅在数十亿(~3610000)次函数调用后才可察觉。对于数千次调用,它们性能相同(!)。
EXPLAIN ANALYZE select j_op_cast(j) from j_array_test; -- ~35000
EXPLAIN ANALYZE select j_func_cast(j) from j_array_test;  -- ~28000
-- Conclusion: about average time json_array_elements_text is ~22%  faster.
-- calculated as 200*(35000.-28000)/(28000+35000)

EXPLAIN ANALYZE select jb_op_cast(j) from jb_array_test; -- ~45000
EXPLAIN ANALYZE select jb_func_cast(j) from jb_array_test;  -- ~37000
-- Conclusion: about average time json_array_elements_text is ~20%  faster.
-- calculated as 200*(45000.-37000)/(45000+37000)

对于JSON和JSONb,性能差异大约为20%,因此通常(例如报告或微服务输出)可以忽略不计。
正如预期的那样,JSON转换为文本比JSONB转换快,因为JSON内部是文本,而JSONB不是。
PS:在Ubuntu 20 LTS上使用PostgreSQL 12.4,虚拟机。

voase2hg

voase2hg4#

Oto的answer是一个救星,但它确实有一个边界情况让我绞尽脑汁。由于强制类型转换的损耗特性,它可以完美地工作,除非您有一个空的json数组。在这种情况下,您可能希望返回一个空数组,但实际上它什么也不返回。作为一种解决方法,如果你只是将返回值与一个空数组连接起来,那么在实际返回的情况下不会有任何影响,但是当你得到一个空数组时,就应该做正确的事情。下面是实现该解决方案的更新后的SQL函数(适用于jsonjsonb)。

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;

有一些像这样的特性指出了将文档数据库集成到成熟的关系数据库中的困难,但是Postgres在处理大多数这些特性方面做得非常出色。

nszi6y05

nszi6y055#

在我的例子中,它帮助了结果反映3种状态,空,空文本数组和非空文本数组取决于输入。

CREATE OR REPLACE FUNCTION json_array_text_array(JSON)
  RETURNS TEXT [] AS $$
DECLARE
  result TEXT [];
BEGIN
  IF $1 ISNULL
  THEN
    result := NULL;
  ELSEIF json_array_length($1) = 0
    THEN
      result := ARRAY [] :: TEXT [];
  ELSE
    SELECT array_agg(x) FROM json_array_elements_text($1) t(x) INTO result;
  END IF;
  RETURN result;
END;
$$
LANGUAGE plpgsql
IMMUTABLE
STRICT;
whitzsjs

whitzsjs6#

Postgres 9.6或更高版本的最佳转换函数如下:

CREATE OR REPLACE FUNCTION jsonb_array_to_text_array(_js jsonb)
  RETURNS text[]
  LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS
'SELECT ARRAY(SELECT jsonb_array_elements_text(_js))';

ARRAY构造函数比array_agg()便宜。请参见:

将其标记为PARALLEL SAFE也很重要。
请参见dba.SE上前面的答案中的分步说明:

相关问题