简体   繁体   English

从层次结构中动态提取属性列 JSON - PostgreSQL

[英]Extract attribute columns from hierarchical JSON dynamically - PostgreSQL

This query selects a hierarchical JSON as a table in PostgreSQL. The name of the columns could be hard code as done here, but that's not feasible when working with hundreds of columns.此查询选择分层结构 JSON 作为 PostgreSQL 中的表。列的名称可以是硬编码,但在处理数百列时这是不可行的。

JSON sample: JSON 样本:

{
  "NODES": [
    {
      "DESC_D": "fam",
      "SEQ": "1",
      "ID": "2304500",
      "NODES": [
        {
          "DESC_D": "test 1",
          "SEQ": "2.1",
          "ID": "5214",
          "NODES": [
            {
              "DESC_D": "test 1.1",
              "SEQ": "3.1",
              "ID": "999"
            }
          ]
        },
        {
          "DESC_D": "test 2",
          "SEQ": "2.2",
          "ID": "74542"
        }
      ]
    }
  ]
}

Query sample:查询样本:

SELECT DISTINCT
       child->>'SEQ' AS seq
     , child->>'DESC_D' AS desc_d
     , child->>'ID' AS id
     , parent->>'ID' AS parent_id
     , child->>'NODES' AS nodes
  FROM jsonb_path_query('{"NODES":[{"DESC_D":"fam","SEQ":"1","ID":"2304500","NODES":[{"DESC_D":"test 1","SEQ":"2.1","ID":"5214","NODES":[{"DESC_D":"test 1.1","SEQ":"3.1","ID":"999"}]},{"DESC_D":"test 2","SEQ":"2.2","ID":"74542"}]}]}'
                       , '$.** ? (@.NODES.type() == "array")') AS parent
 CROSS JOIN LATERAL jsonb_array_elements(parent->'NODES') AS child
 ORDER BY seq

I want to get the same output of this code, but without hard coding:我想获得此代码的相同 output,但没有硬编码:

'SEQ' AS seq

'DESC_D' AS desc_d

'ID' AS id

'NODES' AS nodes

I need them dynamically written depending on the JSON我需要根据 JSON 动态编写它们

Found this solution but didn't work for me because my JSON is hierarchical (or maybe I didn't implement it the right way).找到了这个解决方案,但对我不起作用,因为我的 JSON 是分层的(或者我没有以正确的方式实现它)。

The proposed solution relies on a composite type which must be dynamically created with the following procedure:建议的解决方案依赖于必须使用以下过程动态创建的复合类型:

CREATE OR REPLACE PROCEDURE composite_type (j jsonb) LANGUAGE plpgsql AS
$$
DECLARE
  _columns text ;
BEGIN
  SELECT string_agg(DISTINCT v.key || ' text', ',')
    INTO _columns
  FROM jsonb_path_query( j
                       , '$.** ? (@.NODES.type() == "array")') AS parent
 CROSS JOIN LATERAL jsonb_array_elements(parent->'NODES') AS child
 CROSS JOIN LATERAL jsonb_each_text(child) AS v(key, value) ;
 DROP TYPE IF EXISTS composite_type ;
 EXECUTE 'CREATE TYPE composite_type AS (' || _columns || ')' ;
END ;
$$ ;

First create the composite type:首先创建复合类型:

CALL composite_type ('{"NODES":[{"DESC_D":"fam","SEQ":"1","ID":"2304500","NODES":[{"DESC_D":"test 1","SEQ":"2.1","ID":"5214","NODES":[{"DESC_D":"test 1.1","SEQ":"3.1","ID":"999"}]},{"DESC_D":"test 2","SEQ":"2.2","ID":"74542"}]}]}' :: jsonb) ;

And then the query:然后查询:

SELECT parent->>'ID' AS parent_id
     , (jsonb_populate_record(null :: composite_type, jsonb_object_agg(lower(v.key), v.value))).*
  FROM jsonb_path_query('{"NODES":[{"DESC_D":"fam","SEQ":"1","ID":"2304500","NODES":[{"DESC_D":"test 1","SEQ":"2.1","ID":"5214","NODES":[{"DESC_D":"test 1.1","SEQ":"3.1","ID":"999"}]},{"DESC_D":"test 2","SEQ":"2.2","ID":"74542"}]}]}'
                       , '$.** ? (@.NODES.type() == "array")') AS parent
 CROSS JOIN LATERAL jsonb_array_elements(parent->'NODES') AS child
 CROSS JOIN LATERAL jsonb_each_text(child) AS v(key, value)
 GROUP BY parent, child
 ORDER BY seq

provides the expected result:提供预期的结果:

parent_id parent_id desc_d desc_d id ID nodes节点 seq序号
null null fam秘境 2304500 2304500 [{"ID": "5214", "SEQ": "2.1", "NODES": [{"ID": "999", "SEQ": "3.1", "DESC_D": "test 1.1"}], "DESC_D": "test 1"}, {"ID": "74542", "SEQ": "2.2", "DESC_D": "test 2"}] [{“ID”:“5214”,“SEQ”:“2.1”,“节点”:[{“ID”:“999”,“SEQ”:“3.1”,“DESC_D”:“测试 1.1”}] , "DESC_D": "test 1"}, {"ID": "74542", "SEQ": "2.2", "DESC_D": "test 2"}] 1 1个
2304500 2304500 test 1测试 1 5214 5214 [{"ID": "999", "SEQ": "3.1", "DESC_D": "test 1.1"}] [{“ID”:“999”,“SEQ”:“3.1”,“DESC_D”:“测试 1.1”}] 2.1 2.1
2304500 2304500 test 2测试 2 74542 74542 null null 2.2 2.2
5214 5214 test测试 1.1 1.1 999 999 null null

Note that in the query child is broken down by CROSS JOIN LATERAL jsonb_each_text(child) AS v(key, value) and then rebuild with jsonb_object_agg(lower(v.key), v.value) so that v.key are converted into lower cases and correspond to the attribute names of composite_type which are necessarily in lower cases.请注意,在查询中, childCROSS JOIN LATERAL jsonb_each_text(child) AS v(key, value)分解,然后使用jsonb_object_agg(lower(v.key), v.value)进行重建,以便v.key转换为较低的cases 并对应于composite_type的属性名称,这些名称必须是小写的。

see dbfiddledbfiddle

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM