如何按特定字段中的 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
我有一位女士 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