在 SQL 服务器中查询复杂的 JSON - 过滤对象数组
Query complex JSON in SQL Server - filter array of objects
我在将 Msft Sql 服务器中的 JSON 功能组合在一起时遇到问题。我有一个存储复杂 JSON 结构的 table,需要提取对象数组的一个子集。
例如,我制作了一个简单的脚本,它创建了一个 table 并用一些记录填充它:
CREATE TABLE JsonData ( CompanyId int IDENTITY(1,1) NOT NULL, Name varchar(50) NOT NULL, Json varchar(max) NOT NULL)
INSERT INTO JsonData (Name, Json) VALUES ('Company A', '{"Sector":"Food/Bev","EmployeeCount":105,"Address":{"Address1":"88 Oak Ave","Address2":"","City":"Madison","State":"WI","Zip":"11223"},"Vehicles":[{"Make":"Toyota","Model":"Camry","Year":2013,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2017-04-01"},{"Desc":"Oil change","PerformedOn":"2017-08-01"}]},{"Make":"Ford","Model":"F150","Year":2010,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2015-01-01"}]},{"Make":"Honda","Model":"Odyssey","Year":2010,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2013-01-01"},{"Desc":"Oil change","PerformedOn":"2014-01-01"}]}]}');
INSERT INTO JsonData (Name, Json) VALUES ('Company B', '{"Sector":"Plastics","EmployeeCount":853,"Address":{"Address1":"100 Main St","Address2":"","City":"Anchorage","State":"AK","Zip":"56432"},"Vehicles":[{"Make":"Ford","Model":"F150","Year":2003,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2017-01-01"},{"Desc":"Tire rotation","PerformedOn":"2017-01-01"},{"Desc":"Brake inspection","PerformedOn":"2017-02-01"}]},{"Make":"Ford","Model":"F150","Year":2008,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2017-01-01"}]},{"Make":"Volkswagen","Model":"Jetta","Year":2010,"Maintenance":[]}]}');
INSERT INTO JsonData (Name, Json) VALUES ('Company C', '{"Sector":"Plastics","EmployeeCount":50,"Address":{"Address1":"99 Pine St","Address2":"","City":"Dallas","State":"TX","Zip":"33443"},"Vehicles":[{"Make":"Pontiac","Model":"Fiero","Year":1998,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2010-04-01"},{"Desc":"Oil change","PerformedOn":"2000-08-01"}]},{"Make":"Chevy","Model":"Silverado","Year":2008,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2010-01-01"}]},{"Make":"Honda","Model":"Odyssey","Year":2014,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2015-04-01"},{"Desc":"Oil change","PerformedOn":"2015-09-01"}]}]}');
我正在尝试获取 B 公司的 Fords 车辆列表。我的想法是获取公司 B 的记录,然后解析 json 以获取 Make = 'Ford' 的 Vehicles 数组。这个脚本有效,但它真的很笨拙。
在我看来,这应该全部汇总到一个声明中。
DECLARE @vehicJson varchar(max);
SELECT @vehicJson = '{ "Vehicles": ' + JSON_QUERY(json, '$.Vehicles') + '}' FROM JsonData WHERE Name = 'Company B';
SELECT @vehicJson;
SELECT * FROM OPENJSON(@vehicJson, '$.Vehicles') WHERE JSON_VALUE(value, '$.Make') = 'Ford';
我看到的第一个问题是 JSON_QUERY 函数 returns 我的对象数组作为一个字符串,但它不是纯粹的 JSON。我手动为该字符串添加前缀和后缀,使其成为真正的 JSON 格式。
我尝试使用 For JSON PATH 输出 true JSON,但这不允许我将输出分配给变量。
下一个问题是 OPENJSON 正在处理一个字符串变量,它遵循我找到的所有 Msft 示例。我发现这些示例很奇怪,因为我假设大多数实际实现会将 JSON 存储在某种 table 中。
免责声明:我在上面的示例中使用的 JSON 结构比我必须使用的格式简单得多。我正在考虑将结构分解为更简单的组件,然后在 Select 查询中构建最终的完整结构。性能也可能决定这种复杂结构的分解。我的目的是创建有助于 "denormalize" 将 JSON 转换为更传统的 SQL 数据格式以进行调试和过滤的视图。
查询显然会根据您的结构而改变,但我正在使用您的示例。
OPENJSON
可以将您的 JSON 切碎成关系形式,这样您就可以更轻松地过滤和 select。您可以使用可选的 WITH
子句:
SELECT j.*, j2.*
FROM JsonData j
CROSS APPLY OPENJSON(Json, '$.Vehicles') WITH (
Make VARCHAR(10),
Model VARCHAR(10),
Year INT
) j2
WHERE j.Name = 'Company B'
AND j2.Make = 'Ford'
;
您可以在不使用 JSON_QUERY
或 JSON_VALUE
的情况下走得更远。上面可以像你说的那样在视图中使用,所以 Json 东西完全隐藏了。
字符串变量的情况可以通过将您的数据交叉应用到我在上面的代码中显示的 OPENJSON
函数来轻松解决。
这会让您更接近您需要去的地方吗?
我在将 Msft Sql 服务器中的 JSON 功能组合在一起时遇到问题。我有一个存储复杂 JSON 结构的 table,需要提取对象数组的一个子集。
例如,我制作了一个简单的脚本,它创建了一个 table 并用一些记录填充它:
CREATE TABLE JsonData ( CompanyId int IDENTITY(1,1) NOT NULL, Name varchar(50) NOT NULL, Json varchar(max) NOT NULL)
INSERT INTO JsonData (Name, Json) VALUES ('Company A', '{"Sector":"Food/Bev","EmployeeCount":105,"Address":{"Address1":"88 Oak Ave","Address2":"","City":"Madison","State":"WI","Zip":"11223"},"Vehicles":[{"Make":"Toyota","Model":"Camry","Year":2013,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2017-04-01"},{"Desc":"Oil change","PerformedOn":"2017-08-01"}]},{"Make":"Ford","Model":"F150","Year":2010,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2015-01-01"}]},{"Make":"Honda","Model":"Odyssey","Year":2010,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2013-01-01"},{"Desc":"Oil change","PerformedOn":"2014-01-01"}]}]}');
INSERT INTO JsonData (Name, Json) VALUES ('Company B', '{"Sector":"Plastics","EmployeeCount":853,"Address":{"Address1":"100 Main St","Address2":"","City":"Anchorage","State":"AK","Zip":"56432"},"Vehicles":[{"Make":"Ford","Model":"F150","Year":2003,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2017-01-01"},{"Desc":"Tire rotation","PerformedOn":"2017-01-01"},{"Desc":"Brake inspection","PerformedOn":"2017-02-01"}]},{"Make":"Ford","Model":"F150","Year":2008,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2017-01-01"}]},{"Make":"Volkswagen","Model":"Jetta","Year":2010,"Maintenance":[]}]}');
INSERT INTO JsonData (Name, Json) VALUES ('Company C', '{"Sector":"Plastics","EmployeeCount":50,"Address":{"Address1":"99 Pine St","Address2":"","City":"Dallas","State":"TX","Zip":"33443"},"Vehicles":[{"Make":"Pontiac","Model":"Fiero","Year":1998,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2010-04-01"},{"Desc":"Oil change","PerformedOn":"2000-08-01"}]},{"Make":"Chevy","Model":"Silverado","Year":2008,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2010-01-01"}]},{"Make":"Honda","Model":"Odyssey","Year":2014,"Maintenance":[{"Desc":"Oil change","PerformedOn":"2015-04-01"},{"Desc":"Oil change","PerformedOn":"2015-09-01"}]}]}');
我正在尝试获取 B 公司的 Fords 车辆列表。我的想法是获取公司 B 的记录,然后解析 json 以获取 Make = 'Ford' 的 Vehicles 数组。这个脚本有效,但它真的很笨拙。
在我看来,这应该全部汇总到一个声明中。
DECLARE @vehicJson varchar(max);
SELECT @vehicJson = '{ "Vehicles": ' + JSON_QUERY(json, '$.Vehicles') + '}' FROM JsonData WHERE Name = 'Company B';
SELECT @vehicJson;
SELECT * FROM OPENJSON(@vehicJson, '$.Vehicles') WHERE JSON_VALUE(value, '$.Make') = 'Ford';
我看到的第一个问题是 JSON_QUERY 函数 returns 我的对象数组作为一个字符串,但它不是纯粹的 JSON。我手动为该字符串添加前缀和后缀,使其成为真正的 JSON 格式。
我尝试使用 For JSON PATH 输出 true JSON,但这不允许我将输出分配给变量。
下一个问题是 OPENJSON 正在处理一个字符串变量,它遵循我找到的所有 Msft 示例。我发现这些示例很奇怪,因为我假设大多数实际实现会将 JSON 存储在某种 table 中。
免责声明:我在上面的示例中使用的 JSON 结构比我必须使用的格式简单得多。我正在考虑将结构分解为更简单的组件,然后在 Select 查询中构建最终的完整结构。性能也可能决定这种复杂结构的分解。我的目的是创建有助于 "denormalize" 将 JSON 转换为更传统的 SQL 数据格式以进行调试和过滤的视图。
查询显然会根据您的结构而改变,但我正在使用您的示例。
OPENJSON
可以将您的 JSON 切碎成关系形式,这样您就可以更轻松地过滤和 select。您可以使用可选的 WITH
子句:
SELECT j.*, j2.*
FROM JsonData j
CROSS APPLY OPENJSON(Json, '$.Vehicles') WITH (
Make VARCHAR(10),
Model VARCHAR(10),
Year INT
) j2
WHERE j.Name = 'Company B'
AND j2.Make = 'Ford'
;
您可以在不使用 JSON_QUERY
或 JSON_VALUE
的情况下走得更远。上面可以像你说的那样在视图中使用,所以 Json 东西完全隐藏了。
字符串变量的情况可以通过将您的数据交叉应用到我在上面的代码中显示的 OPENJSON
函数来轻松解决。
这会让您更接近您需要去的地方吗?