繁体   English   中英

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

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

此查询选择分层结构 JSON 作为 PostgreSQL 中的表。列的名称可以是硬编码,但在处理数百列时这是不可行的。

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"
        }
      ]
    }
  ]
}

查询样本:

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

我想获得此代码的相同 output,但没有硬编码:

'SEQ' AS seq

'DESC_D' AS desc_d

'ID' AS id

'NODES' AS nodes

我需要根据 JSON 动态编写它们

找到了这个解决方案,但对我不起作用,因为我的 JSON 是分层的(或者我没有以正确的方式实现它)。

建议的解决方案依赖于必须使用以下过程动态创建的复合类型:

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 ;
$$ ;

首先创建复合类型:

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) ;

然后查询:

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

提供预期的结果:

parent_id desc_d ID 节点 序号
null 秘境 2304500 [{“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个
2304500 测试 1 5214 [{“ID”:“999”,“SEQ”:“3.1”,“DESC_D”:“测试 1.1”}] 2.1
2304500 测试 2 74542 null 2.2
5214 测试 1.1 999 null

请注意,在查询中, childCROSS JOIN LATERAL jsonb_each_text(child) AS v(key, value)分解,然后使用jsonb_object_agg(lower(v.key), v.value)进行重建,以便v.key转换为较低的cases 并对应于composite_type的属性名称,这些名称必须是小写的。

dbfiddle

暂无
暂无

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

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