【问题标题】:Flatten nested JSON structure in PostgreSQL在 PostgreSQL 中展平嵌套的 JSON 结构
【发布时间】:2019-01-21 20:49:54
【问题描述】:

我正在尝试编写一个 Postgres 查询,它将以特定格式输出我的 json 数据。

JSON 数据结构

{
    user_id: 123,
    data: {
        skills: {
            "skill_1": {
                "title": "skill_1",
                "rating": 4,
                "description": 'description text'
            },
            "skill_2": {
                "title": "skill_2",
                "rating": 2,
                "description": 'description text'
            },
            "skill_3": {
                "title": "skill_3",
                "rating": 5,
                "description": 'description text'
            },
            ...
        }
    }
}

这就是我最终需要对数据进行格式化的方式:

[
    {
        user_id: 123,
        skill_1: 4, 
        skill_2: 2, 
        skill_3: 5, 
                    ... 
    },
    {
        user_id: 456,
        skill_1: 1, 
        skill_2: 3, 
        skill_3: 4, 
                    ... 
    }
]

到目前为止,我正在处理一个如下所示的查询:

SELECT
    user_id,
    data#>>'{skills, "skill_1",  rating}' AS "skill_1",
    data#>>'{skills, "skill_2",  rating}' AS "skill_2",
    data#>>'{skills, "skill_3",  rating}' AS "skill_3"
FROM some_table

必须有更好的方法来编写我的查询。有 400+ 行和 70+ 技能。我上面的查询有点疯狂。任何指导或帮助将不胜感激。

注意事项:

  1. 用户根据 70 多项技能对自己进行评分
  2. 每个技能对象都有相同的结构
  3. 每个用户都根据完全相同的技能给自己打分

【问题讨论】:

  • 在您的数据结构中,我缺少 user_id 字段。
  • 更新:它们与数据对象处于同一级别。

标签: json postgresql object nested


【解决方案1】:

db<>fiddle

我将您的测试数据扩展为(注意所有用户周围的数组):

[{
    "user_id": 123,
    "data": {
        "skills": {
            "skill_1": {
                "title": "skill_1",
                "rating": 4,
                "description": "description text"
            },
            "skill_2": {
                "title": "skill_2",
                "rating": 2,
                "description": "description text"
            },
            "skill_3": {
                "title": "skill_3",
                "rating": 5,
                "description": "description text"
            }
        }
    }
},
{
    "user_id": 456,
    "data": {
        "skills": {
            "skill_1": {
                "title": "skill_1",
                "rating": 1,
                "description": "description text"
            },
            "skill_2": {
                "title": "skill_2",
                "rating": 3,
                "description": "description text"
            },
            "skill_3": {
                "title": "skill_3",
                "rating": 4,
                "description": "description text"
            }
        }
    }
}]

查询:

SELECT 
    jsonb_pretty(jsonb_agg(user_id || skills))               -- E
FROM (
    SELECT
        json_build_object('user_id', user_id)::jsonb as user_id,  -- D
        json_object_agg(skill_title, skills -> skill_title -> 'rating')::jsonb as skills
    FROM (
        SELECT 
            user_id,
            json_object_keys(skills) as skill_title,         -- C
            skills
        FROM (
            SELECT
                (datasets -> 'user_id')::text as user_id,
                datasets -> 'data' -> 'skills' as skills     -- B
            FROM (
                SELECT 
                  json_array_elements(json) as datasets      -- A
                FROM (
                  SELECT '/* the JSON data; see db<>fiddle */'::json
                )s
            )s
        )s  
    )s    
    GROUP BY user_id
    ORDER BY user_id
)s

A 使所有数组元素 ({user_id: '42', data: {...}}) 各占一行

B 第一列安全的是user_id。对于无法对 JSON 输出进行分组的 GROUP BY 之后,必须强制转换为文本。对于第二列,提取用户的skills 数据

C 提取技能名称,将其用作 (D.1) 中的键。

D.1 skills -&gt; skill_title -&gt; 'rating' 从每个技能中提取评分值

D.2 json_object_agg 将技能标题和每个对应的评分值聚合到一个 JSON 对象中;按user_id分组

D.3 json_build_object 使 user_id 再次成为 JSON 对象

E.1user_id || skills将两个json对象聚合为一个

E.2 jsonb_agg aggregates这些json对象成一个数组

E.3 jsonb_pretty 使结果看起来很漂亮。

结果:

[{
    "skill_1": 4,
    "skill_2": 2,
    "skill_3": 5,
    "user_id": "123"
},
{
    "skill_1": 1,
    "skill_2": 3,
    "skill_3": 4,
    "skill_4": 42,
    "user_id": "456"
}]

【讨论】:

  • 这真是太棒了。这应该在版本 9.6.6 中工作吗?我应该将其添加到我原来的问题中。对此感到抱歉。
  • 是的,它适用于 Postgres 9.6 sqlfiddle.com/#!17/9eecb/19326 请不要忘记接受和投票。
  • 谢谢,谢谢,谢谢。这正是我所需要的。你一定是个 postgres 向导!!!你的魔法很强大。
  • :D。也许有点:) 不,有趣!这只是阅读文档postgresql.org/docs/current/static/functions-json.html,然后反复试验直到它起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-02
  • 1970-01-01
  • 2014-05-24
  • 1970-01-01
  • 1970-01-01
  • 2019-10-31
  • 2020-10-04
相关资源
最近更新 更多