3

考虑这个 JSON:

{
    "Name": "Alice",
    "Relations": [
        {
            "RelationId": 1,
            "Value": "one"
        },
        {
            "RelationId": 2,
            "Value": "two"
        }
    ]
}

我将此 JSON 传递给一个存储过程,在该存储过程中对其进行解析并插入名称:

-- parse JSON
WITH [source]
AS (SELECT *
    FROM
        OPENJSON(@json)
        WITH
        (
            [Name] VARCHAR(50),
            [Relations] VARCHAR(MAX)
        ) -- end json WITH
) -- end WITH [source] AS

-- insert Name
INSERT INTO dbo.names
(
    [Name]
)
SELECT [s].[Name]
FROM [source] s;

接下来,我想插入关系,所以首先我OPENJSON必须[Relations]

WITH [relationsSource]
AS (SELECT *
    FROM
        -- now, here is the problem: the CTE (common table expression)
        -- named [source] isn't available anymore
        OPENJSON(<how to access [source].[Relations] here?)
        WITH
        (
            [RelationId] INT,
            [Value] VARCHAR(50)
        ) -- end json WITH
) -- end WITH [relationsSource]

我知道我可以做类似的事情OPENJSON(@json, '$Relations')。但这将@json再次解析整个以搜索$Relations路径,而不是仅解析先前提取的[source].[Relations].

有什么解决方案可以让我使用类似的东西

OPENJSON([source].[Relations]) -- pass only the Relations subset of @json

这样OPENJSON就不必@json再次解析完整了吗?

4

2 回答 2

0

尝试这个:

DECLARE @Json NVARCHAR(MAX) = N'
{
    "Name": "Alice",
    "Relations": [
        {
            "RelationId": 1,
            "Value": "one"
        },
        {
            "RelationId": 2,
            "Value": "two"
        }
    ]
}
'

SELECT persons.Id PersonId
    , persons.Name
    , relations.RelationId
    , relations.Value
INTO #JsonToFlatTable
FROM (
    -- sub-query to retrieve the root person.Name and the array of relations
    SELECT *
    , Row_Number() OVER (ORDER BY Name) Id -- Add a fake ID or use some kind of mapping with an existing table. 
    FROM OPENJSON(@json, N'lax $') 
    WITH (
        [Name] VARCHAR(255) N'lax $.Name'
        , Relations nvarchar(MAX) N'$.Relations' AS JSON  
    )
) persons
-- Use openjson on the subset of relations to retrieve the RelationId and Value
CROSS APPLY OPENJSON(persons.Relations, N'lax $')
    WITH(
        RelationId INT N'lax $.RelationId'
        , Value VARCHAR(255) N'lax $.Value'
    ) relations 

-- Maybe set IDENTITY_INSERT ON
INSERT INTO Person(Id, Name)
SELECT DISTINCT PersonId
    , Name
FROM #JsonToFlatTable
-- Maybe set IDENTITY_INSERT OFF

INSERT INTO Relation(PersonId, RelationId, Value)
SELECT PersonId
    , RelationId
    , Value
FROM #JsonToFlatTable

输出

个人身份 姓名 关系 ID 价值
1 爱丽丝 1
1 爱丽丝 2

您可以使用该子句指定模式。WITH您将只解析第二次子集关系OPENJSON

AS JSON在线对于Relations nvarchar(MAX) N'$.Relations' AS JSON此工作至关重要。如果AS JSON未指定,您将得到NULL而不是数组。

从上面的微软文档:

请注意 AS JSON 子句如何导致值作为 JSON 对象返回,而不是 col5 和 array_element 中的标量值。

如果数组大小是动态的,则不可能一次性解析所有内容。如果它是固定大小,您可以为每个已知索引创建列,但这不可维护。当数组大小发生变化时,您需要更改代码。

然后将提取的数据放入相应的表中,您可以使用临时表并填充相应的表。您可能需要Identity_Insert。或者,您可以使用输出子句从 person 表中检索生成的 ID。

于 2020-12-31T06:06:21.760 回答
0

你可以试试这样的。该JSON_VALUE函数选择“名称”的单个实例。tvf 指定“关系”对象的OPENJSON路径并提供列定义。

declare @json nvarchar(max) = N'
{
    "Name": "Alice",
    "Relations": [
        {
            "RelationId": 1,
            "Value": "one"
        },
        {
            "RelationId": 2,
            "Value": "two"
        }
    ]
}'


select json_value(@json, '$.Name') as [Name], j.*
from openjson(@json, '$.Relations') 
              with (RelationId          int,
                    [Value]             nvarchar(4000)) j;

输出

Name    RelationId  Value
Alice   1           one
Alice   2           two
于 2020-12-31T14:11:37.747 回答