【问题标题】:SQL Server - recursively get into nested JSON array using cross apply or openjson or anythingSQL Server - 使用交叉应用或 openjson 或任何东西递归地进入嵌套 JSON 数组
【发布时间】:2018-07-06 06:56:19
【问题描述】:

我一直在阅读尽可能多的相同问题的问题和答案,但我想我的问题需要更多创造性的方法。

所以我这里有一个 JSON 字符串:

declare @json nvarchar(max) =
'{
    "propertyObjects": [{
        "propertyID": 1
        , "title": "foo"
        , "class": ""
        , "typeid": 150
        , "value": "bar"
        , "children": [{}]
    }, {
        "propertyID": 2
        , "title": "foo"
        , "class": ""
        , "typeid": 128
        , "value": "bar"
        , "children": [{}]
    }, {
        "propertyID": 3
        , "title": "foo"
        , "class": ""
        , "typeid": 128
        , "value": "bar"
        , "children": [{
            "propertyID": 4
            , "title": "foo"
            , "class": ""
            , "typeid": 128
            , "value": "bar"
            , "children": [{}]
        }, {
            "propertyID": 5
            , "title": "foo"
            , "class": ""
            , "typeid": 128
            , "value": "bar"
            , "children": [{}]
        }, {
            "propertyID": 6
            , "title": "foo"
            , "class": ""
            , "typeid": 128
            , "value": "bar"
            , "children": [{
                "propertyID": 7
                , "title": "foo"
                , "class": ""
                , "typeid": 128
                , "value": "bar"
                , "children": [{
                    "propertyID": 8
                    , "title": "foo"
                    , "class": ""
                    , "typeid": 128
                    , "value": "bar"
                    , "children": [{}]
                }]
            }]
        }]
    }]
}'

乍一看很疯狂,但这样想:
有一个名为 propertyObjects 的数组,其中包含父子结构中的多个对象。
在每个级别中,只有一个对象可以是父对象。如您所见,对象 3 里面有孩子。

我想要在表格中列出这些对象,同时为每个对象指定一个 parentID,因此对象 4 的父对象 ID 为 3,对象 3 本身的父对象为 0,因为它基本上位于顶层。

到目前为止,我尝试了一些方法,如 Common Table Expression 来进行递归调用,但我失败了:

;with cte 
as
(
    -- anchor member definition
    select p.propertyID
        , 0 as parentID
        , p.title
        , p.typeid
        , p.[value]
        , p.children
    from openjson(@json, '$.propertyObjects')
    with (
        propertyID int
        , title nvarchar(100)
        , typeid int
        , [value] nvarchar(1000)
        , children nvarchar(max) as JSON
    ) as p

    UNION ALL

    -- recursive member definition
    select 0 as propertyID
        , 0 as parentID
        , '' as title
        , 0 typeid
        , '' as [value]
        , '' as children
    /** child should be bound to parent **/
)
select * from cte

这就是我失败的地方,我不知道如何让它通过孩子递归查找对象。另外,我不知道如何指定每个孩子的 parentID!

propertyID    parentID    title    typeid    value    children
----------------------------------------------------------------------------
1             0           foo      150       bar      [{}]
2             0           foo      128       bar      [{}]
3             0           foo      128       bar      [{ "propertyID" : 4 ...
0             0                    0   

我也尝试过使用交叉应用:

select *
from 
    openjson(@json, '$.propertyObjects')
    with (
        propertyID int
        , title nvarchar(100)
        , typeid int
        , [value] nvarchar(1000)
        , children nvarchar(max) as JSON
    ) as p
cross apply
    openjson(p.children)
    with (
        propertyID int
        , title nvarchar(100)
        , typeid int
        , [value] nvarchar(1000)
        , children nvarchar(max) as JSON
    ) as r

但不是机会,我不知道这些孩子在JSON字符串中会走多远。此外,交叉应用的结果将追加列而不是行,这会导致结果中出现巨大的表格,在这种方法中我什至不能考虑指定 parentID。

这完全是失败的,知道如何让所有孩子排成一排吗?

所需的表

propertyID    parentID    title    typeid    value
--------------------------------------------------
1             0           foo      150       bar
2             0           foo      128       bar
3             0           foo      128       bar
4             3           foo      128       bar
5             3           foo      128       bar
6             3           foo      128       bar
7             6           foo      128       bar
8             7           foo      128       bar

【问题讨论】:

  • 我不确定,但您是否尝试从 JSON 文件导入数据?如果是这样,您可能想探索其他选项,例如使用 PowerShell。我猜你已经读过这个页面:docs.microsoft.com/en-us/sql/relational-databases/json/…,但为了确定,我在这里提到它。
  • ksauter 感谢您的提醒,是的,我已经阅读了所有页面,但是阅读此链接使我想起了使用与我的问题无关的内存优化表,但它会帮助我使用答案一种更优化的方式。答案将比仅使用内置函数复杂得多
  • 必须是存储过程吗?是否可以选择使用 SSIS?
  • SSIS 对我来说不是一个选项,但是可以使用存储过程,任何解决这个问题的方法

标签: sql json sql-server recursion sql-server-2016


【解决方案1】:

其实你已经很接近了——你只需要把CROSS APPLYOPENJSON一起使用:

with cte as (
    select
        p.propertyID,
        0 as parentID,
        p.title,
        p.typeid,
        p.[value],
        p.children
    from openjson(@json, '$.propertyObjects') with (
        propertyID int,
        title nvarchar(100),
        typeid int,
        [value] nvarchar(1000),
        children nvarchar(max) as json
    ) as p

    union all

    select
        p.propertyID,
        c.propertyID,
        p.title,
        p.typeid,
        p.[value],
        p.children  
    from cte as c
        cross apply openjson(c.children) with (
            propertyID int,
            title nvarchar(100),
            typeid int,
            [value] nvarchar(1000),
            children nvarchar(max) as json
        ) as p
    where
        c.children <> '[{}]'
)
select
    c.propertyID,
    c.parentID,
    c.title,
    c.typeid,
    c.value
from cte as c

sql fiddle demo

【讨论】:

  • 哇,我已经很久没有使用 Microsoft SQL Server 了 :)) 但我检查了一下,它正在工作!谢谢你的精彩回答。
【解决方案2】:

据我在工作中了解到的这个主题,几乎不可能深入到未知的嵌套 JSON 级别,同时性能不差。

我所做的是避免嵌套级别,我在一个级别中创建 JSON 字符串并简单地返回所有对象及其内部的键值对,并更改数据库中的数据模型。

拥有有效且合理的数据结构比拥有进行复杂编码的技能更重要。

【讨论】:

    猜你喜欢
    • 2022-12-13
    • 2016-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-28
    相关资源
    最近更新 更多