2

我有一个以 Json 格式存储数据的 SQL 表。我正在使用下面的示例数据来理解这个问题。每种文档类型都有自己的 JSON 结构。

DocumentID  DocumentTypeID  Status    JsonData  
----------------------------------------------------------------------------  
1             2             Active    {"FirstName":"Foo","LastName":"Bar","States":"[OK]"}  
2             2             Active    {"FirstName":"James","LastName":"Smith","States":"[TX,NY]"}
3             3             Active    {"Make":"Ford","Model":"Focus","Year":"[2020]"}  
4             3             Active    {"Make":"Tesla","Model":"X","Year":"[2012,2015,2019]"}  

然后我有另一个 JSON 需要在 Where 条件下使用

@Condition =   '{"FirstName": "James",LastName:"Smith","States":[TX]}'

我也会有DocumentTypeID作为参数

因此,在普通 sql 中,如果我对属性名称进行硬编码,则 SQL 看起来像

 SELECT * FROM Documents d
 WHERE 
 d.DocumentTypeID = @DocumentTypeID AND
 JSON_VALUE(d.JsonData,'$.FirstName') = JSON_VALUE(@Condition,'$.FirstName') AND
 JSON_VALUE(d.JsonData,'$.LastName') = JSON_VALUE(@Condition,'$.LastName') AND
 JSON_QUERY(d.JsonData,'$.States') = JSON_QUERY(@Condition,'$.States') -- This line is wrong. I have 
                                                                       -- to check if one array is  
                                                                       -- subset of another array

给定
中的属性名称,并将与给定的 DocumentTypeID 完全匹配。JsonDataCondition

我已经有另一个存储 DocumentType 及其属性的 SQL 表。如果有帮助,我可以为每个可在上述查询中使用的属性存储 json 路径,以动态构造 where 条件

DocumentTypeID      PropertyName             JsonPath         DataType
---------------------------------------------------------------------------------
2                    FirstName                $.FirstName       String
2                    LastName                 $.LastName        String
2                    States                   $.States          Array
3                    Make                     $.Make            String
3                    Model                    $.Model           String
3                    Year                     $.Year            Array

ISSUE
对于每种文档类型,@condition 将具有不同的 JSON 结构。我如何构建动态 where 条件?这在 SQL 中甚至可能吗?

我正在使用 C#.NET,所以我想在 C# 中构建 SQL 查询并执行 SQL 查询。但在我走那条路之前,我想检查一下它是否可以在 TSQL 中做到这一点

4

1 回答 1

2

不幸的是,JSON 支持仅在 2016 版本中添加到 SQL Server 中,并且仍有改进的空间。处理包含数组的 JSON 数据非常麻烦,涉及OPENJSON获取数据,另一个OPENJSON涉及获取数组数据。
一个基于 SQL 的解决方案是可能的 - 但我写的 - 很麻烦。

首先,创建并填充示例表(在以后的问题中保存我们这一步):

DECLARE @Documents AS TABLE (
    [DocumentID] int, 
    [DocumentTypeID] int, 
    [Status] varchar(6), 
    [JsonData] varchar(100)
);

INSERT INTO @Documents ([DocumentID], [DocumentTypeID], [Status], [JsonData]) VALUES
(1, 2, 'Active', '{"FirstName":"Foo","LastName":"Bar","States":["OK"]}'),
(2, 2, 'Active', '{"FirstName":"James","LastName":"Smith","States":["TX","NY"]}'),
(2, 2, 'Active', '{"FirstName":"James","LastName":"Smith","States":["OK", "NY"]}'),
(2, 2, 'Active', '{"FirstName":"James","LastName":"Smith","States":["OH", "OK"]}'),
(3, 3, 'Active', '{"Make":"Ford","Model":"Focus","Year":[2020]}'),
(4, 3, 'Active', '{"Make":"Tesla","Model":"X","Year":[2012,2015,2019]}');

注意我在示例数据中添加了几行,以验证条件是否正常工作。
另外,作为旁注 - 问题中的一些 JSON 数据格式不正确 - 我必须解决这个问题。

然后,声明搜索参数(注意:我仍然认为发送 JSON 字符串作为搜索条件有潜在风险):

DECLARE @DocumentTypeID int = 2,
        @Condition varchar(100) = '{"FirstName": "James","LastName":"Smith","States":["TX", "OH"]}';

(注意:我添加了另一个状态 - 再次确保条件正常工作。)

然后,我使用了一个公用表表达式openjsoncross apply将 json 条件转换为表格数据,并将该 cte 加入到表中:

WITH CTE AS
(
SELECT FirstName, LastName, [State]
FROM OPENJSON(@Condition)
    WITH (
        FirstName varchar(10) '$.FirstName',
        LastName varchar(10) '$.LastName',
        States nvarchar(max) '$.States' AS JSON
    ) 
    CROSS APPLY OPENJSON(States)
    WITH (
        [State] varchar(2) '$'
    )
)

SELECT [DocumentID], [DocumentTypeID], [Status], [JsonData]
FROM @Documents 
CROSS APPLY 
    OPENJSON([JsonData])
    WITH(
        -- Since we already have to use OPENJSON, no point of also using JSON_VALUE...
        FirstName varchar(10) '$.FirstName',
        LastName varchar(10) '$.LastName',
        States nvarchar(max) '$.States' AS JSON
    ) As JD
    CROSS APPLY OPENJSON(States)
    WITH(
        [State] varchar(2) '$'
    ) As JDS
JOIN CTE 
    ON JD.FirstName = CTE.FirstName
    AND JD.LastName = CTE.LastName
    AND JDS.[State] = CTE.[State]
WHERE DocumentTypeID = @DocumentTypeID 

结果:

DocumentID  DocumentTypeID  Status  JsonData
2           2               Active  {"FirstName":"James","LastName":"Smith","States":["TX","NY"]}
2           2               Active  {"FirstName":"James","LastName":"Smith","States":["OH", "OK"]}
于 2020-05-24T09:52:13.810 回答