【问题标题】:Recursive JSONB postgres递归 JSONB postgres
【发布时间】:2018-05-30 07:02:58
【问题描述】:

我正在尝试在 Postgres 中构建一个同时支持数组和对象的递归 CTE,以返回键值对列表,但似乎无法找到一个好的示例。这是我当前的代码。

with recursive jsonRecurse as
(
select
j.key as Path
,j.key
,j.value
from jsonb_each(to_jsonb('{
    "key1": {
        "key2": [
            {
                "key3": "test3",
                "key4": "test4"
            }
        ]
    },
    "key5": [
        {
            "key6":
            [
                {
                    "key7": "test7"
                }
            ]
        }
    ]
}'::jsonb)) j

union all

select
jr.path || '.' || jr2.Key
,jr2.key
,jr2.value
from jsonRecurse jr
       left join lateral jsonb_each(jr.value) jr2 on true
where jsonb_typeof(jr.value) = 'object'
)

select
*
from jsonRecurse;

正如您所见,只要我点击一个数组而不是一个对象,代码就会停止递归。我尝试过使用 case 语句,并将对 jsonb_each 或 jsonb_array_element 的函数调用放在 case 语句中,但我收到一个错误,告诉我改用横向连接。

【问题讨论】:

    标签: postgresql jsonb recursive-cte


    【解决方案1】:

    我已使用此示例表使查询更具可读性:

    create table my_table(id serial primary key, jdata jsonb);
    insert into my_table (jdata) values
    ('{
        "key1": {
            "key2": [
                {
                    "key3": "test3",
                    "key4": "test4"
                }
            ]
        },
        "key5": [
            {
                "key6":
                [
                    {
                        "key7": "test7"
                    }
                ]
            }
        ]
    }');
    

    您必须有条件地同时加入jsonb_each(value)jsonb_array_elements(value),具体取决于value 的类型:

    with recursive extract_all as
    (
        select 
            key as path, 
            value
        from my_table
        cross join lateral jsonb_each(jdata)
    union all
        select
            path || '.' || coalesce(obj_key, (arr_key- 1)::text),
            coalesce(obj_value, arr_value)
        from extract_all
        left join lateral 
            jsonb_each(case jsonb_typeof(value) when 'object' then value end) 
            as o(obj_key, obj_value) 
            on jsonb_typeof(value) = 'object'
        left join lateral 
            jsonb_array_elements(case jsonb_typeof(value) when 'array' then value end) 
            with ordinality as a(arr_value, arr_key)
            on jsonb_typeof(value) = 'array'
        where obj_key is not null or arr_key is not null
    )
    select *
    from extract_all;
    

    输出:

            path        |                     value                      
    --------------------+------------------------------------------------
     key1               | {"key2": [{"key3": "test3", "key4": "test4"}]}
     key5               | [{"key6": [{"key7": "test7"}]}]
     key1.key2          | [{"key3": "test3", "key4": "test4"}]
     key5.0             | {"key6": [{"key7": "test7"}]}
     key1.key2.0        | {"key3": "test3", "key4": "test4"}
     key5.0.key6        | [{"key7": "test7"}]
     key1.key2.0.key3   | "test3"
     key1.key2.0.key4   | "test4"
     key5.0.key6.0      | {"key7": "test7"}
     key5.0.key6.0.key7 | "test7"
    (10 rows)
    

    json 数组的元素没有键,我们应该使用它们的索引来构建路径。因此函数jsonb_array_elements() 应该按序号调用。根据the documentation(参见7.2.1.4. 表函数):

    如果指定了 WITH ORDINALITY 子句,则会在函数结果列中添加一个 bigint 类型的附加列。此列对函数结果集的行进行编号,从 1 开始。

    函数调用

    jsonb_array_elements(case jsonb_typeof(value) when 'array' then value end) 
    with ordinality as a(arr_value, arr_key)
    

    返回对 (value, ordinality) 别名为 (arr_value, arr_key)

    【讨论】:

    • 啊哈,我认为条件连接是前进的方向,但我不知道如何让它发挥作用。 lateral join 函数调用中的 case 语句似乎是我所缺少的魔法。请问with ordinality语句在做什么?
    猜你喜欢
    • 2023-04-08
    • 1970-01-01
    • 2013-01-07
    • 2022-01-19
    • 2021-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-03
    相关资源
    最近更新 更多