【问题标题】:Postgresql: denormalize JSONB with nested key/values and arraysPostgresql:使用嵌套的键/值和数组对 JSONB 进行非规范化
【发布时间】:2020-10-22 16:28:16
【问题描述】:

如果需要将嵌套的 JSONB 结构非规范化为平面表,如果我正在混合键/值和数组,是否可以在单个 SELECT 中实际完成,如下所示?我有一个包含两列的表 - repositorydata 一个示例 data 列值如下所示:

{
    "branches": {
        "master": {
            "type1": {
                "files": [{
                    "filename": "a.txt",
                    "contents": [{
                        "identifier": "foo",
                        "value": "bar"
                    }]
                }]
            }
        }
    }
}

我的目标是有一个扁平化这个结构的查询,例如转换为以下 CSV 伪代码:

repository,branch,type,filename,identifier,value
a/b,master,type1,a.txt,foo,bar

我已使 JSON 示例尽可能简单,但最终会得到大约一百万行。

虽然对单个 data JSON 列的需求是固定的,但它的格式可以重构,如果它可以使非规范化更容易或更有效。例如与其将分支和类型从它们的唯一标识符中分离出来,不如将它们转换为带有标识符的数组。

【问题讨论】:

  • repository 列在输出中来自哪里?顺便说一句:您实际上是在规范化您拥有的非规范化数据。
  • 你能修复 JSON 吗?这是无效的,因为{ 大括号的数量与} 大括号的数量不匹配。并且 contents 数组未正确关闭。 contents 真的是一个数组吗?如果是的话,如果那里有多个标识符和值怎么办?它们应该在输出中显示为行还是列?

标签: postgresql jsonb


【解决方案1】:

假设contents 数组的元素应以行和有效的 JSON 结构形式返回,如下所示:

{
  "branches": {
    "master": {
      "type1": {
        "files": [{"filename": "a.txt","contents": [{"identifier": "foo","value": "bar"}, {"identifier": "a2", "value": "a value"}]},
                  {"filename": "b.txt","contents": [{"identifier": "id2","value": "something else"}]}]
      }
    }
  }
}  

那么你可以使用这个:

select b.branch, 
       t.type, 
       f.file ->> 'filename' as filename, 
       c.content ->> 'identifier' as identifier, 
       c.content ->> 'value' as value
from the_table tt
  cross join jsonb_each(tt.the_column -> 'branches') as b(branch, branch_value)
  cross join jsonb_each(b.branch_value) as t(type, type_value)
  cross join jsonb_array_elements(t.type_value -> 'files') as f(file)
  cross join jsonb_array_elements(f.file -> 'contents') as c(content)

使用上面更正后的 JSON,返回:

branch | type  | filename | identifier | value         
-------+-------+----------+------------+---------------
master | type1 | a.txt    | foo        | bar           
master | type1 | a.txt    | a2         | a value       
master | type1 | b.txt    | id2        | something else

如果contents 实际上不是一个数组,那么像这样:

{
  "branches": {
    "master": {
      "type1": {
        "files": [{"filename": "a.txt","contents": {"identifier": "foo","value": "bar"}},
                  {"filename": "b.txt","contents": {"identifier": "id2","value": "something else"}}]
      }
    }
  }
}

那么你可以使用这个查询:

select b.branch, t.type, f.file ->> 'filename' as filename, f.file #>> '{contents, identifier}' as identifier, f.file #>> '{contents,value}' as value
from the_table tt
  cross join jsonb_each(tt.the_column -> 'branches') as b(branch, branch_value)
  cross join jsonb_each(b.branch_value) as t(type, type_value)
  cross join jsonb_array_elements(t.type_value -> 'files') as f(file)

返回:

branch | type  | filename | identifier | value         
-------+-------+----------+------------+---------------
master | type1 | a.txt    | foo        | bar           
master | type1 | b.txt    | id2        | something else

【讨论】:

    猜你喜欢
    • 2017-05-02
    • 1970-01-01
    • 2019-12-17
    • 1970-01-01
    • 2020-05-06
    • 2018-11-26
    • 1970-01-01
    • 2017-02-23
    • 2013-06-30
    相关资源
    最近更新 更多