簡體   English   中英

帶點符號的 Postgres 查詢行構建一個嵌套的 json 作為查詢結果

[英]Postgres query row with dot notaiton to build a nested json as query result

我有一個帶有點符號列的平面表,類似於以下內容:

標題.en 標題.fr 類別名稱.en 類別名稱.fr 類別.acronym.en 類別.acronym.fr
英文標題 法語標題 英文類別名稱 法語類別名稱 英文分類縮寫 法語類別首字母縮略詞

點符號在那里表示嵌套的 object,因此每個點的嵌套級別為 json。據此,我希望能夠從 json(b) 中的表中查詢此結果:

{
  "data": {
    "title": {
      "en": "English Title",
      "fr": "French Title"
    },
    "category": {
      "name": {
        "en": "English Category Name",
        "fr": "French Category Name"
      },
      "acronym": {
        "en": "English Category Acronym",
        "fr": "French Category Acronym"
      }
    }
  }
}

我知道可以使用嵌套的“jsonb_build_object”函數手動執行此操作,但想知道是否可以使用列名中的點符號來簡化它。

讓我們考慮data是表名。 下面的解決方案需要幾個步驟:

1. 從數據庫模式中獲取列名:

SELECT table_name, column_name
  FROM information_schema.columns
 WHERE table_name = 'data'

結果:

表名 列名
數據 標題.en
數據 標題.fr
數據 類別名稱.en
數據 類別名稱.fr
數據 類別.acronym.en
數據 類別.acronym.fr

2. 將點符號列名轉換為目標 jsonb 格式:

SELECT ('{"' || table_name || '": {"' || replace(column_name, '.', '": {"') || '": "' || column_name || '"' || repeat('}', regexp_count(column_name, '\.')+2)) :: jsonb
  FROM information_schema.columns
 WHERE table_name = 'data'

結果:

jsonb
{“數據”:{“標題”:{“en”:“title.en”}}}
{“數據”:{“標題”:{“fr”:“title.fr”}}}
{“數據”:{“類別”:{“名稱”:{“en”:“category.name.en”}}}}
{“數據”:{“類別”:{“名稱”:{“fr”:“category.name.fr”}}}}
{“數據”:{“類別”:{“首字母縮略詞”:{“en”:“category.acronym.en”}}}}
{“數據”:{“類別”:{“首字母縮略詞”:{“fr”:“category.acronym.fr”}}}}

3. 將 jsonb 列名合並在一起以構建最終的 jsonb 模板:

在這一步中,我們需要創建一個特定的aggregate function jsonb_merge_agg

CREATE OR REPLACE FUNCTION jsonb_merge(x jsonb, y jsonb) RETURNS jsonb LANGUAGE sql AS $$
  SELECT jsonb_object_agg
           ( COALESCE(x1->>'key', y1->>'key')
           , CASE
               WHEN x1->>'key' IS NULL THEN y1->'value'
               WHEN y1->>'key' IS NULL THEN x1->'value'
               WHEN jsonb_typeof(x1->'value') = 'object' AND jsonb_typeof(y1->'value') = 'object' THEN jsonb_merge(x1->'value', y1->'value')
               ELSE jsonb_build_array(x1->'value', y1->'value')
             END
           )
    FROM jsonb_path_query(x,'$[*].keyvalue()') AS x1
    FULL OUTER JOIN jsonb_path_query(y,'$[*].keyvalue()') AS y1
      ON y1->>'key' = x1->>'key' ;
$$ ;

CREATE OR REPLACE AGGREGATE jsonb_merge_agg(x jsonb)
(stype = jsonb, sfunc = jsonb_merge) ;

SELECT jsonb_merge_agg(('{"' || table_name || '": {"' || replace(column_name, '.', '": {"') || '": "' || column_name || '"' || repeat('}', regexp_count(column_name, '\.')+2)) :: jsonb) AS jsonb_template
  FROM information_schema.columns
 WHERE table_name = 'data'

結果:

jsonb_模板
{"data": {"title": {"en": "title.en", "fr": "title.fr"}, "category": {"name": {"en": "category.name .en", "fr": "category.name.fr"}, "acronym": {"en": "category.acronym.en", "fr": "category.acronym.fr"}}}}

4. 將 jsonb_template 中的每個列名替換為相應的值:

在這一步中,我們需要在標准replace function 的基礎上創建另一個特定的聚合 function replace_agg

CREATE OR REPLACE FUNCTION replace(x text, y text, old text, new text) RETURNS text LANGUAGE sql AS $$
SELECT replace(COALESCE(x, y), old, new) ; $$ ;

CREATE OR REPLACE AGGREGATE replace_agg(x text, old text, new text)
(stype = text, sfunc = replace) ;

5. 構建最終查詢:

WITH list AS (
SELECT jsonb_merge_agg(('{"' || table_name || '": {"' || replace(column_name, '.', '": {"') || '": "' || column_name || '"' || repeat('}', regexp_count(column_name, '\.')+2)) :: jsonb) AS jsonb_template
  FROM information_schema.columns
 WHERE table_name = 'data'
)
SELECT replace_agg( l.jsonb_template :: text
                  , content->>'key'
                  , content->>'value'
                  ) AS final_result
  FROM list AS l
 CROSS JOIN data AS d
 CROSS JOIN LATERAL jsonb_path_query(to_jsonb(d), '$.keyvalue()') AS content
 GROUP BY d

最后結果:

最后結果
{"data": {"title": {"en": "英文標題", "fr": "法文標題"}, "category": {"name": {"en": "英文類別名稱", "fr": "French Category Name"}, "acronym": {"en": "English Category Acronym", "fr": "French Category Acronym"}}}}

dbfiddle中的所有測試結果

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM