【问题标题】:Flattening Postgres nested JSONB column展平 Postgres 嵌套 JSONB 列
【发布时间】:2017-07-13 04:49:05
【问题描述】:

我正在寻找如何展平嵌套在 JSONB 列中的数据。 例如,假设我们有 user_id(int) 和兄弟姐妹(JSONB) 的表 users

像这样的行:

id | JSONB
---------------------
1  | {"brother": {"first_name":"Sam", "last_name":"Smith"}, "sister": {"first_name":"Sally", "last_name":"Smith"}
2  | {"sister": {"first_name":"Jill"}}

我正在寻找一个会返回如下响应的查询:

id | sibling   | first_name | last_name
-------------------------------------
1  | "brother" | "Sam"      | "Smith"
1  | "sister"  | "Sally"    | "Smith"
2  | "sister"  | "Jill"     | null

【问题讨论】:

  • postgres 版本?
  • @VaoTsun 版本 9.4.9
  • json 键 first_name 和 last_name 是常量吗?..
  • @VaoTsun 不,键可能会改变
  • @JacobMurphy 如果键可以更改,即first_name 等,那么这是变成固定列的不好选择。你能定义一组你想变成列的键,其余的保留为 JSON 吗?

标签: postgresql jsonb


【解决方案1】:

我开发到这个在psql 中使用它。 为了检查代码,我创建了小视图t1:

CREATE VIEW t1 AS (
       SELECT 1 AS id, '{"brother": {"first_name":"Sam", "last_name":"Smith"}, "sister": {"first_name":"Sally", "last_name":"Smith"}}'::jsonb AS jsonb
 UNION SELECT 2, '{"sister": {"first_name":"Jill", "last_name":"Johnson"}}'
 UNION SELECT 3, '{"sister": {"first_name":"Jill", "x_name":"Johnson"}}'
);

第一个任务是找到可能的键列表:

WITH fields AS (
     SELECT DISTINCT jff.key
       FROM t1,
            jsonb_each(jsonb) AS jf,
            jsonb_each(jf.value) AS jff
)
SELECT * FROM fields;

结果是:

    key     
------------
 first_name
 last_name
 x_name

下一步是生成查询:

SELECT 'SELECT id, jf.key as sibling, ' || (
    WITH fields AS (
         SELECT DISTINCT jff.key
           FROM t1,
                jsonb_each(jsonb) AS jf,
                jsonb_each(jf.value) AS jff
    )
    SELECT string_agg('jf.value->>''' || key || ''' as "' || key || '"', ',' ORDER BY key)
      FROM fields
)
|| ' FROM t1, jsonb_each(jsonb) AS jf ORDER BY 1, 2, 3;' AS cmd;

返回:

                                                                                  cmd                                                                                   
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 SELECT id, jf.key as sibling,jf.value->>'first_name' as "first_name",jf.value->>'last_name' as "last_name",jf.value->>'x_name' as "x_name" FROM t1, jsonb_each(jsonb) AS jf ORDER BY 1, 2, 3;
(1 row)

要将结果设置为 psql 变量,我使用 gset:

\gset

之后可以调用查询:

:cmd

 id | sibling | first_name | last_name | x_name  
----+---------+------------+-----------+---------
  1 | brother | Sam        | Smith     | 
  1 | sister  | Sally      | Smith     | 
  2 | sister  | Jill       | Johnson   | 
  3 | sister  | Jill       |           | Johnson
(4 rows)

要从外部语言运行它,您可以创建 postgres 函数而不是返回 SQL 命令:

CREATE OR REPLACE FUNCTION build_query(IN tname text, OUT cmd text)  AS $sql$
BEGIN 
    EXECUTE $cmd$
            SELECT 'SELECT id, jf.key as sibling, ' || (
                    WITH fields AS (
                        SELECT DISTINCT jff.key
                          FROM t1,
                               jsonb_each(jsonb) AS jf,
                               jsonb_each(jf.value) AS jff
                    )
                    SELECT string_agg('jf.value->>''' || key || ''' as "' || key || '"', ',' ORDER BY key)
                      FROM fields
                )
        || ' FROM $cmd$ || quote_ident(tname) || $cmd$ , jsonb_each(jsonb) AS jf ORDER BY 1, 2, 3;'$cmd$ INTO cmd;
    RETURN;
END;
$sql$ LANGUAGE plpgsql;

SELECT * FROM build_query('t1');
                                                                                               cmd                                                                                               
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 SELECT id, jf.key as sibling, jf.value->>'first_name' as "first_name",jf.value->>'last_name' as "last_name",jf.value->>'x_name' as "x_name" FROM t1 , jsonb_each(jsonb) AS jf ORDER BY 1, 2, 3;
(1 row)

【讨论】:

  • 很好,但这不是递归的,并且只选择 0+1 级 json 节点,或者如果一行错过 0+1 级 json 节点,则挂起:
猜你喜欢
  • 1970-01-01
  • 2017-01-28
  • 2023-04-07
  • 1970-01-01
  • 2021-02-18
  • 1970-01-01
  • 1970-01-01
  • 2016-08-10
  • 2017-02-23
相关资源
最近更新 更多