【问题标题】:How to convert JSON Array of Arrays to columns and rows如何将 JSON 数组转换为列和行
【发布时间】:2015-11-10 04:47:32
【问题描述】:

我从 API 中提取 JSON 格式的数据,格式类似于下面的示例数据。基本上每个“行”都是一个值数组。 API 文档预先定义了列及其类型。所以我知道 col1 是一个 varchar,而 col2 是一个 int。

CREATE TEMP TABLE dat (data json);
INSERT INTO dat
VALUES ('{"COLUMNS":["col1","col2"],"DATA":[["a","1"],["b","2"]]}');

我想在 PostgreSQL 9.3 中对其进行转换,最终得到:

col1 | col2
------------
  a  |  1
  b  |  2

使用json_array_elements我可以到达:

SELECT json_array_elements(data->'DATA') 
FROM dat

json_array_elements
json
---------
["a","1"]
["b","2"]

但是我不知道如何将 JSON 数组转换为 PostgreSQL 数组,这样我就可以执行unnest(ARRAY['a','1']) 之类的操作

【问题讨论】:

  • 多么糟糕的 JSON 文档。
  • 我真诚地感谢您的认可。
  • 好像他们听说过json,但是完全不懂。结构化文档?不,我们只是将关系的表示塞入其中。

标签: arrays json postgresql-9.3


【解决方案1】:

未知列的一般情况

得到类似的结果

col1 | col2
------------
  a  |  1
  b  |  2

会需要一堆动态SQL,因为你事先不知道列的类型,也不知道列名。

您可以使用以下内容解压缩 json:

SELECT
  json_array_element_text(colnames, colno) AS colname,
  json_array_element_text(colvalues, colno) AS colvalue,
  rn,
  idx,
  colno
FROM (
  SELECT
    data -> 'COLUMNS' AS colnames,
    d AS colvalues,
    rn,
    row_number() OVER () AS idx
  FROM (
    SELECT data, row_number() OVER () AS rn FROM dat
  ) numbered
  cross join json_array_elements(numbered.data -> 'DATA') d
) elements
cross join generate_series(0, json_array_length(colnames) - 1) colno;

产生如下结果集:

 colname | colvalue | rn | idx | colno 
---------+----------+----+-----+-------
 col1    | a        |  1 |   1 |     0
 col2    | 1        |  1 |   1 |     1
 col1    | b        |  1 |   2 |     0
 col2    | 2        |  1 |   2 |     1
(4 rows)

然后,您可以将其用作 tablefunc 模块中交叉表函数的输入,例如:

SELECT * FROM crosstab('
SELECT
  to_char(rn,''00000000'')||''_''||to_char(idx,''00000000'') AS rowid,
  json_array_element_text(colnames, colno) AS colname,
  json_array_element_text(colvalues, colno) AS colvalue
FROM (
  SELECT
    data -> ''COLUMNS'' AS colnames,
    d AS colvalues,
    rn,
    row_number() OVER () AS idx
  FROM (
    SELECT data, row_number() OVER () AS rn FROM dat
  ) numbered
  cross join json_array_elements(numbered.data -> ''DATA'') d
) elements
cross join generate_series(0, json_array_length(colnames) - 1) colno;
') results(rowid text, col1 text, col2 text);

制作:

        rowid        | col1 | col2 
---------------------+------+------
  00000001_ 00000001 | a    | 1
  00000001_ 00000002 | b    | 2
(2 rows)

此处不保留列名。

如果您使用的是 9.4,则可以避免使用 row_number() 调用并使用 WITH ORDINALITY,使其更干净。

使用固定的已知列进行简化

由于您显然事先知道列数及其类型,因此可以大大简化查询。

SELECT
  col1, col2
FROM (
  SELECT
    rn,
    row_number() OVER () AS idx,
    elem ->> 0 AS col1,
    elem ->> 1 :: integer AS col2
  FROM (
    SELECT data, row_number() OVER () AS rn FROM dat
  ) numbered
  cross join json_array_elements(numbered.data -> 'DATA') elem
  ORDER BY 1, 2
) x;

结果:

 col1 | col2 
------+------
 a    |    1
 b    |    2
(2 rows)

使用 9.4 WITH ORDINALITY

如果您使用的是 9.4,则可以使用 WITH ORDINALITY 使其更简洁:

SELECT
  col1, col2
FROM (
  SELECT
    elem ->> 0 AS col1,
    elem ->> 1 :: integer AS col2
  FROM
    dat
  CROSS JOIN
    json_array_elements(dat.data -> 'DATA') WITH ORDINALITY AS elements(elem, idx)
  ORDER BY idx
) x;

【讨论】:

  • 列类型在 API 文档中预先指定,现在编辑我的问题。
  • @raphael 所以你知道列类型,因此知道列数?对于一般情况,解决它之前了解这一点很有用。
  • 所以你真正需要的只是json_array_element_text?见上文。
  • 该功能在 postgresql 9.3 中不可用
  • @raphael 啊,对,是的。 9.3 的 json 支持有点疏忽。您必须使用运算符形式->>。请参阅the manual for 9.3 和上面的编辑。
【解决方案2】:

这段代码对我来说很好,也许它对某人有用。

select to_json(array_agg(t))
 from (
  select text, pronunciation,
   (
     select array_to_json(array_agg(row_to_json(d)))
    from (
      select part_of_speech, body
       from definitions
       where word_id=words.id
       order by position asc
     ) d
   ) as definitions
  from words
  where text = 'autumn'
) t

学分: https://hashrocket.com/blog/posts/faster-json-generation-with-postgresql

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-11
    • 1970-01-01
    • 1970-01-01
    • 2015-08-06
    • 2015-08-26
    • 2017-04-09
    • 2021-01-16
    相关资源
    最近更新 更多