簡體   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