【问题标题】:Query complex JSON in SQL Server - filter array of objects在 SQL Server 中查询复杂的 JSON - 过滤对象数组
【发布时间】:2018-02-27 15:03:31
【问题描述】:

我在将 Msft Sql Server 中的 JSON 函数组合在一起时遇到问题。我有一个存储复杂 JSON 结构的表,需要提取对象数组的子集。

作为一个例子,我做了一个简单的脚本来创建一个表并用一些记录填充它:

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 公司的福特汽车清单。我的想法是获取公司 B 的记录,然后解析 json 以获取 Make = 'Ford' 的车辆数组。这个脚本有效,但它真的很笨重。

在我看来,这一切都应该集中在一个语句中。

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 函数将我的对象数组作为字符串返回,但它不是纯 JSON。我手动为该字符串添加前缀和后缀,使其成为真正的 JSON 格式。
我尝试使用 For JSON PATH 输出真正的 JSON,但这不允许我将输出分配给变量。

下一个问题是 OPENJSON 正在处理一个字符串变量,它遵循我找到的所有 Msft 示例。我觉得这些例子很奇怪,因为我假设大多数实际实现会将 JSON 存储在某种表中。

免责声明:我在上面的示例中使用的 JSON 结构比我必须使用的格式简单得多。我正在考虑将结构分解为更简单的组件,然后在 Select 查询中构建最终的完整结构。性能也可能决定这种复杂结构的分解。我的意图是创建有助于将 JSON“非规范化”为更传统的 SQL 数据格式以进行调试和过滤的视图。

【问题讨论】:

    标签: sql-server sql-server-2016 sql-server-json


    【解决方案1】:

    查询显然会根据您的结构而改变,但我正在抽取您的样本。

    OPENJSON 可以将您的 JSON 分解为关系形式,以便您更轻松地过滤和选择。你可以使用可选的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_QUERYJSON_VALUE 的情况下走得更远。而且上面可以用在你说的视图中,所以Json的东西是完全隐藏的。

    字符串变量的大小写可以很容易地解决,方法是将您的数据交叉应用到OPENJSON 函数,如我在上面的代码中所示。

    这会让你更接近你需要去的地方吗?

    【讨论】:

    • 我忘了使用 CROSS APPLY。我得再研究一下,谢谢。我了解 OPENJSON 将 JSON 转换为关系数据的潜力,但在这种情况下,这对我没有用处。我需要返回的数据是格式化为有效 JSON 的数组 JSON 对象。所以也许我的要求可能不是最初设计的功能。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-05
    • 1970-01-01
    • 2021-12-12
    • 2013-08-05
    相关资源
    最近更新 更多