SQL Server - 使用交叉应用或 openjson 或任何东西递归地进入嵌套的 JSON 数组

Bri*_* SP 4 sql sql-server recursion json sql-server-2016

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

所以我这里有一个 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": [{}]
                }]
            }]
        }]
    }]
}'
Run Code Online (Sandbox Code Playgroud)

乍一看很疯狂,但可以这样想:
有一个名为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
Run Code Online (Sandbox Code Playgroud)

这就是我失败的地方,我不知道如何让它通过孩子递归地找到对象。另外,我不知道如何指定每个孩子的 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   
Run Code Online (Sandbox Code Playgroud)

我也尝试使用交叉应用:

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
Run Code Online (Sandbox Code Playgroud)

但不是机会,我不知道这些孩子会在 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
Run Code Online (Sandbox Code Playgroud)

Rom*_*kar 5

您实际上非常接近 - 您只需要与以下CROSS APPLY一起使用OPENJSON

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
Run Code Online (Sandbox Code Playgroud)

sql fiddle demo