如何将 JSON Array of Arrays 转换为列和行

How to convert JSON Array of Arrays to columns and rows

我正在从 JSON 中的 API 中提取数据,其格式类似于下面的示例数据。基本上每个 "row" 都是一个值数组。 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'])

的操作

未知列的一般情况

获得类似

的结果
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;

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

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