【问题标题】:How to parse JSON column using TSQL如何使用 TSQL 解析 JSON 列
【发布时间】:2019-07-23 22:11:32
【问题描述】:

我正在尝试使用 T-SQL 将特定的有效 JSON 字符串从列解析为其各个值。

我查看了很多样本​​,尤其是这个Parse JSON in TSQL,但仍然不完全在那里。谁能建议一个有效的 T-SQL 语句来完成这项工作?

MessageDetail 列中带有 json 的示例 cte:

select Id, MessageDetail from cte_example

Id  MessageDetail
1   {"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "sysadmins": {"sysadmin": {"Member": "DummyAdmin", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-22T18:10:55.023", "Removed": "2019-07-22T19:21:15.867"}}}

我正在尝试使用的 T-SQL:

select
    json_value(b.value, '$.Member') as Member
    ,json_value(b.value, '$.IsDisabled') as IsDisabled
    ,json_value(b.value, '$.IsNTGroup') as IsNTGroup
    ,json_value(b.value, '$.Added') as Added
    ,json_value(b.value, '$.Removed') as Removed
from
    cte_example a
    outer apply openjson(json_query(a.MessageDetail, '$.sysadmins.sysadmin')) b

这会导致以下错误:

消息 13609,第 16 级,状态 2,第 17 行 JSON 文本格式不正确。在位置 0 发现意外字符“D”。

由于 JSON 查询 $.sysadmins.sysadmin 是有效的,我感到很困惑。我做错了什么?

注意:当它尝试解析以下内容时,我的查询工作正常

{"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "sysadmins": {"sysadmin": [{"Member": "sa", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "testuser", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "abc\\User1", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "NT SERVICE\\SQLWriter", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "NT SERVICE\\Winmgmt", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "NT Service\\MSSQLSERVER", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "NT SERVICE\\SQLSERVERAGENT", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "DummyAdmin", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-22T02:10:07.833", "Removed": "2019-07-22T03:00:02.177"}, {"Member": "domain1\\testservice", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T04:18:51.900"}, {"Member": "##MS_PolicyEventProcessingLogin##", "IsDisabled": "1", "IsNTGroup": "0", "Added": "2019-07-22T04:07:48.497"}]}}

【问题讨论】:

  • 您的 JSON 示例是不同的结构。您的第一个 sysadmin 不是阵列,但您列出的第二个 sysadmin 是阵列?是哪一个,因为tsql代码会不一样?
  • 您的第一个错误的原因是因为在这种情况下,由于 sysadmin 不是数组,因此您在 b.value 中获得的值不是有效的 JSON,因此 json_value 会引发错误。您的第二个示例 sysadmin 是一个数组,因此您在 b.value 中获得了一个有效的 json 并且 json_value 可以遍历它。
  • 是的,我现在看到了。我想现在我意识到我不能用同一个查询来解析两者。我很可能需要看看我是否也可以将“奇异”JSON 字符串做成一个数组?
  • 顺便说一句:“JSON”是相当明显的笨拙转换的 XML,现在不再需要 xsi 属性。 SQL Server 还对存储和解析 XML 提供了出色的支持,因此这里可能有机会减少转换步骤。

标签: json sql-server tsql


【解决方案1】:

解决方案 1:

如果你想使用JSON_VALUE,你需要在这种情况下使用OPENJSON,在列定义中使用显式模式和AS JSON选项。这里的path 参数是$.sysadmins

表:

CREATE TABLE cte_example (
   Id int,
   MessageDetail nvarchar(max)
)
INSERT INTO cte_example
   (Id, MessageDetail)
VALUES   
   (1, N'{"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "sysadmins": {"sysadmin": {"Member": "DummyAdmin", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-22T18:10:55.023", "Removed": "2019-07-22T19:21:15.867"}}}')

声明:

SELECT 
   JSON_VALUE(b.sysadmin, '$.Member') AS Member,
   JSON_VALUE(b.sysadmin, '$.IsDisabled') AS IsDisabled,
   JSON_VALUE(b.sysadmin, '$.IsNTGroup') AS IsNTGroup,
   JSON_VALUE(b.sysadmin, '$.Added') AS Added,
   JSON_VALUE(b.sysadmin, '$.Removed') AS Removed
FROM cte_example a
OUTER APPLY OPENJSON(a.MessageDetail, '$.sysadmins') WITH (
   sysadmin nvarchar(max) '$.sysadmin' AS JSON
) b

输出:

-------------------------------------------------------------------
Member      IsDisabled  IsNTGroup   Added                   Removed
-------------------------------------------------------------------
DummyAdmin  0           0           2019-07-22T18:10:55.023 2019-07-22T19:21:15.867

解决方案 2:

您可以尝试不使用JSON_VALUE 的另一种方法,再次使用具有显式架构定义的OPENJSON。这里的path 参数是$.sysadmins.sysadmin

SELECT b.*
FROM cte_example a
OUTER APPLY OPENJSON(a.MessageDetail, '$.sysadmins.sysadmin') WITH (
   Member nvarchar(10) '$.Member', 
   IsDisabled nvarchar(1) '$.IsDisabled', 
   IsNTGroup nvarchar(1) '$.IsNTGroup', 
   Added nvarchar(23) '$.Added', 
   Removed nvarchar(23) '$.Removed'
) b

关于您的错误的解释:

附声明:

select
    b.*
from
    cte_example a
    outer apply openjson(json_query(a.MessageDetail, '$.sysadmins.sysadmin')) b

你的结果是:

---------------------
key value   type 
---------------------
Member      DummyAdmin              1
IsDisabled  0                       1
IsNTGroup   0                       1
Added       2019-07-22T18:10:55.023 1
Removed     2019-07-22T19:21:15.867 1

value 列中的值不是JSON 格式,json_value(b.value, '$.Member') 返回错误。

【讨论】:

    【解决方案2】:

    使用OPENJSON() 和 WITH 子句将允许您将这两种情况或 sysadmin 作为数组处理。

    这是一个包含两者的工作示例:

    DECLARE @testData TABLE
        (
            [id] INT
          , [MessageDetail] NVARCHAR(MAX)
        );
    
    INSERT INTO @testData (
                              [id]
                            , [MessageDetail]
                          )
    VALUES (1 , '{"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "sysadmins": {"sysadmin": [{"Member": "sa", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "testuser", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "abc\\User1", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "NT SERVICE\\SQLWriter", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "NT SERVICE\\Winmgmt", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "NT Service\\MSSQLSERVER", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "NT SERVICE\\SQLSERVERAGENT", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T03:01:49.677"}, {"Member": "DummyAdmin", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-22T02:10:07.833", "Removed": "2019-07-22T03:00:02.177"}, {"Member": "domain1\\testservice", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-19T04:18:51.900"}, {"Member": "##MS_PolicyEventProcessingLogin##", "IsDisabled": "1", "IsNTGroup": "0", "Added": "2019-07-22T04:07:48.497"}]}}' )
           ,(2 , '{"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "sysadmins": {"sysadmin": {"Member": "DummyAdmin", "IsDisabled": "0", "IsNTGroup": "0", "Added": "2019-07-22T18:10:55.023", "Removed": "2019-07-22T19:21:15.867"}}}')
    
    
    SELECT [b].*
    FROM   @testData [a]
    CROSS APPLY
           OPENJSON([a].[MessageDetail], '$.sysadmins.sysadmin')
               WITH (
                        [member] NVARCHAR(100) '$.Member'
                      , [IsDiabled] NVARCHAR(100) '$.IsDisabled'
                      , [IsNTGroup] NVARCHAR(100) '$.IsNTGroup'
                      , [Added] DATETIME '$.Added'
                      , [Removed] DATETIME '$.Removed'
                    ) [b];
    

    【讨论】:

    • @TimMylott 这只是一个评论,仅此而已,但它与我的回答中的Solution 2 方法相同。我无法理解这种重复答案的原因。
    • @Zhorov 我最初发帖时没有看到答案,我们一定同时在处理它。不是想偷你的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-03
    • 1970-01-01
    相关资源
    最近更新 更多