如何按特定字段中的 Json 字符串列过滤数据

how to filter data by Json string column in specific field

我有一位女士 Sql Table。在此 table 中,我将数据以 json 格式存储在名为“DynamicData”的列中。我想做的是过滤这个table。但是要根据我名为“dynamicData”的列中 json 数据的不同字段进行过滤。

例如;

Id | Title | DynamicData
---------------------------------------------------------------- ------------------------------
1 | Title1 | { "Id": 1, "Score": 5, "Message": "Msg1", Types: ["a", "b", "c"]}
2 | Title2 | { "Id": 2, "Score": 3, "Message": "Msg2", Types: ["z", "k", "c"]}
3 | Title3 | { "Id": 3, "Score": 1, "Message": "Msg3", Types: ["a", "b", "c"]}
4 | Title4 | { "Id": 4, "Score": 4, "Message": "Msg4", Types: ["h", "n", "f"]}

正是我想要的;根据这个json数据中的字段进行不同的查询。

例如;

检索“分数”字段为“5”的记录。 带上“Score”字段“5”和“Message”字段“Msg1”的记录。 获取“Score”字段为“5”或“Types”字段为“h”或“n”的记录。

可能会有与这些类似的不同且更复杂的查询。我搜索但找不到我想要的东西。您对我如何进行此类查询有什么建议或示例应用程序吗?

如果您是 SQL Server 2016+,您可以使用 OPENJSON()

下面是一个如何过滤分数的示例:

DECLARE @testdata TABLE
    (
        [Id] INT
      , [Title] NVARCHAR(20)
      , [DynamicData] NVARCHAR(MAX)
    );


INSERT INTO @testdata (
                          [Id]
                        , [Title]
                        , [DynamicData]
                      )
VALUES ( 1, 'Title1', '{ "Id": 1, "Score": 5, "Message": "Msg1", "Types": ["a", "b", "c"]}' )
     , ( 2, 'Title2', '{ "Id": 2, "Score": 3, "Message": "Msg2", "Types": ["z", "k", "c"]}' )
     , ( 3, 'Title3', '{ "Id": 3, "Score": 1, "Message": "Msg3", "Types": ["a", "b", "c"]}' )
     , ( 4, 'Title4', '{ "Id": 4, "Score": 4, "Message": "Msg4", "Types": ["h", "n", "f"]}' );


SELECT *
FROM   @testdata [td]
CROSS APPLY OPENJSON([td].[DynamicData])
           WITH (
                    [Id] INT
                  , [Score] INT
                  , [Message] NVARCHAR(20)
                  , [Types] NVARCHAR(MAX) AS JSON
                ) AS [dd]
WHERE  [dd].[Score] = 5

为您提供以下结果:

Id          Title                DynamicData                                                         Id          Score       Message  Types
----------- -------------------- ------------------------------------------------------------------- ----------- ----------- -------  -----
1           Title1               { "Id": 1, "Score": 5, "Message": "Msg1", "Types": ["a", "b", "c"]} 1           5           Msg1     ["a", "b", "c"]

为了获得“类型”,我们将使用花药交叉应用,因为“类型”是一个数组:

SELECT *
FROM   @testdata [td]
CROSS APPLY OPENJSON([td].[DynamicData])
           WITH (
                    [Id] INT
                  , [Score] INT
                  , [Message] NVARCHAR(20)
                  , [Types] NVARCHAR(MAX) AS JSON
                ) AS [dd]
CROSS APPLY OPENJSON([Types]) dt --Add another cross apply for types since it is an array
WHERE  [dd].[Score] = 5
OR dt.[Value] IN ('h','n') --Then we can filter on it

给出结果:

Id          Title                DynamicData                                                            Id          Score       Message   Types            key  value   type
----------- -------------------- ---------------------------------------------------------------------- ----------- ----------- --------- ---------------- ---- ------- ----
1           Title1               { "Id": 1, "Score": 5, "Message": "Msg1", "Types": ["a", "b", "c"]}    1           5           Msg1      ["a", "b", "c"]  0    a       1
1           Title1               { "Id": 1, "Score": 5, "Message": "Msg1", "Types": ["a", "b", "c"]}    1           5           Msg1      ["a", "b", "c"]  1    b       1
1           Title1               { "Id": 1, "Score": 5, "Message": "Msg1", "Types": ["a", "b", "c"]}    1           5           Msg1      ["a", "b", "c"]  2    c       1
4           Title4               { "Id": 4, "Score": 4, "Message": "Msg4", "Types": ["h", "n", "f"]}    4           4           Msg4      ["h", "n", "f"]  0    h       1
4           Title4               { "Id": 4, "Score": 4, "Message": "Msg4", "Types": ["h", "n", "f"]}    4           4           Msg4      ["h", "n", "f"]  1    n       1

您看到有重复项,在“类型”上交叉应用,该数组中的每个元素现在都是它自己的行,显示在“值”列中。

您必须根据自己的具体要求进行调整,但这应该会让您朝着正确的方向前进。

如果您不能使用 JSON,总有 Ngrams8k

DECLARE @table TABLE (ID INT IDENTITY, Title VARCHAR(20), DynamicData VARCHAR(1000));

INSERT @table(Title,DynamicData) VALUES
('Title1', '{"Id": 1, "Score": 5, "Message": "Msg1", Types: ["a", "b", "c"]}'),
('Title2', '{"Id": 2, "Score": 3, "Message": "Msg2", Types: ["z", "k", "c"]}'),
('Title3', '{"Id": 3, "Score": 1, "Message": "Msg3", Types: ["a", "b", "c"]}'),
('Title4', '{"Id": 4, "Score": 4, "Message": "Msg4", Types: ["h", "n", "f"]}');

DECLARE @score INT = 5, @type CHAR(1) = 'b'

SELECT      t.*
FROM        @table AS t
CROSS APPLY PerfLib.samd.ngrams8K(t.DynamicData,7) AS ng
CROSS APPLY (VALUES(SUBSTRING(t.DynamicData,ng.Position+8,8000))) AS pfx(Txt)
CROSS APPLY (VALUES(CHARINDEX('[',t.DynamicData),
                    CHARINDEX(']',t.DynamicData))) AS tp(Lft,Rgt)
CROSS APPLY (VALUES(REPLACE(SUBSTRING(
                    t.DynamicData,tp.Lft+1,tp.Rgt-tp.Lft-1),'"',''))) AS tl(List)
CROSS APPLY (VALUES(SUBSTRING(pfx.Txt,1,CHARINDEX(',',pfx.Txt)-1)))   AS f(Score)
WHERE       ng.Token = '"Score"'
  AND       f.Score = @score
  AND       EXISTS (SELECT 1 FROM STRING_SPLIT(tl.List,',') AS split
                    WHERE LTRIM(split.[value]) = @type);

Returns:

ID  Title  DynamicData                                                      Position
--- ------ ---------------------------------------------------------------- --------
1   Title1 {"Id": 1, "Score": 5, "Message": "Msg1", Types: ["a", "b", "c"]} 11