【问题标题】:how to filter data by Json string column in specific field如何按特定字段中的 Json 字符串列过滤数据
【发布时间】:2021-10-19 12:39:07
【问题描述】:

我有一个 Ms Sql 表。在此表中,我将数据以 json 格式存储在名为“DynamicData”的列中。我想要做的是在这张桌子上过滤。但是要根据我名为“dynamicData”的列中json数据的不同字段进行过滤。

例如;

身份证 |标题 |动态数据 -------------------------------------------------- -------------- ------------------ 1 |标题1 | { "Id": 1, "Score": 5, "Message": "Msg1", 类型: ["a", "b", "c"]}
2 |标题2 | { "Id": 2, "Score": 3, "Message": "Msg2", 类型: ["z", "k", "c"]}
3 |标题3 | { "Id": 3, "Score": 1, "Message": "Msg3", 类型: ["a", "b", "c"]}
4 |标题4 | { "Id": 4, "Score": 4, "Message": "Msg4", 类型: ["h", "n", "f"]}

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

例如;

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

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

【问题讨论】:

  • 什么版本的sql server?
  • 同上 Tim 的问题 ^^^ 这将影响您可用的选项。

标签: json tsql open-json


【解决方案1】:

如果你是 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"]

要获得“类型”,我们将使用另一个 CROSS APPLY,因为“类型”是一个数组:

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

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

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

【讨论】:

    【解决方案2】:

    如果您不能使用 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);
    

    返回:

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

    【讨论】:

      猜你喜欢
      • 2017-03-03
      • 2018-02-16
      • 1970-01-01
      • 2021-06-16
      • 1970-01-01
      • 2019-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多