SQL Server - 使用cross apply或openjson或任何

时间:2018-01-26 21:24:41

标签: sql json sql-server recursion 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": [{}]
                }]
            }]
        }]
    }]
}'
乍一看是疯了,但想到这一点:
这里有一个名为 propertyObjects 的数组,它包含父子结构中的多个对象。
在每个级别中,只有一个对象可以是父对象。如你所见,对象3里面有孩子。

我想要的是在我们为每个对象指定父ID时在表中列出这些对象,因此对象4具有ID为3的父对象,对象3本身为父0,因为它基本上位于顶部水平。

到目前为止,我尝试了一些方法,如公用表表达式来进行递归调用,但我失败了:

;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

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

就我在工作中学习这个主题而言,这几乎不可能潜入未知的嵌套JSON级别,同时性能不佳。

我所做的是避免嵌套级别,我在一个级别创建了JSON字符串,只是返回其中包含键值对的所有对象,并且还更改了数据库中的数据模型。

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