如何将JSON数组数组转换为列和行

rap*_*ael 8 arrays json postgresql-9.3

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

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

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

col1 | col2
------------
  a  |  1
  b  |  2
Run Code Online (Sandbox Code Playgroud)

使用json_array_elements我可以到达:

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

json_array_elements
json
---------
["a","1"]
["b","2"]
Run Code Online (Sandbox Code Playgroud)

但后来我无法弄清楚如何将JSON数组转换为PostgreSQL数组,以便我可以执行类似的操作 unnest(ARRAY['a','1'])

Cra*_*ger 9

未知列的一般情况

得到一个像这样的结果

col1 | col2
------------
  a  |  1
  b  |  2
Run Code Online (Sandbox Code Playgroud)

将需要一堆动态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;
Run Code Online (Sandbox Code Playgroud)

生成如下结果集:

 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)
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用此作为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);
Run Code Online (Sandbox Code Playgroud)

生产:

        rowid        | col1 | col2 
---------------------+------+------
  00000001_ 00000001 | a    | 1
  00000001_ 00000002 | b    | 2
(2 rows)
Run Code Online (Sandbox Code Playgroud)

列名称不会保留在此处.

如果你在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;
Run Code Online (Sandbox Code Playgroud)

结果:

 col1 | col2 
------+------
 a    |    1
 b    |    2
(2 rows)
Run Code Online (Sandbox Code Playgroud)

使用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;
Run Code Online (Sandbox Code Playgroud)