OpenJson 解析和部分更新 json 列列

OpenJson to parse and partially update a json column column

我有这个table:

CREATE TABLE [dbo].[Device]
(
    [Id] [bigint] IDENTITY(1,1) NOT NULL,
    [DeviceStatus] [int] NOT NULL,
    [Type] [nvarchar](64) NOT NULL,
    [Serial] [nvarchar](64) NOT NULL,
    [Group] [nvarchar](max) NULL,
    [Name] [nvarchar](max) NULL,
    [IP] [nvarchar](max) NULL,
    [Description] [nvarchar](max) NULL,
    [JsonConfig] [nvarchar](max) NULL,
    [JsonStatus] [nvarchar](max) NULL,
    [RSSI] [int] NOT NULL,
    [DateCreated] [datetime2](7) NOT NULL,
    [DateUpdated] [datetime2](7) NOT NULL,
    [DateLastSeen] [datetime2](7) NOT NULL,
    [BatteryVoltage] [int] NOT NULL,
    [IsBatteryPowered] [bit] NOT NULL,
    [Uptime] [int] NOT NULL,
    [Memory] [int] NOT NULL,
    [Version] [nvarchar](max) NULL
)

JsonConfig 列有此数据

declare @json nvarchar(max)
set @json = '
[
  {
    "item": 0,
    "type": "switch",
    "name": "item 1",
    "status": {}
  },
  {
    "item": 1,
    "type": "switch",
    "name": "item 2",
    "status": {}
  },
  {
    "item": 2,
    "type": "switch",
    "name": "item 3",
    "status": {}
  },
  {
    "item": 3,
    "type": "switch",
    "name": "item 4",
    "status": {}
  },
  {
    "item": 4,
    "type": "switch",
    "name": "item 5",
    "status": {}
  },
  {
    "item": 5,
    "type": "switch",
    "name": "item 6",
    "status": {}
  },
  {
    "item": 6,
    "type": "switch",
    "name": "item 7",
    "status": {}
  },
  {
    "item": 7,
    "type": "switch",
    "name": "item 8",
    "status": {}
  }
]';

我收到了这样的 json 文档:

declare @jsonStat nvarchar(max)
set @jsonStat = '{
  "serial": "locker-7C9EBD6074F8",
  "type": "locker",
  "ver": "0.1",
  "ip": "192.168.1.133",
  "uptime": 79,
  "mem": 210888,
  "rssi": -36,
  "resources": [
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false,
      "valueA" : 1
    },
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false,
      "valueB" : "test B"
    },
    {
      "busy": true,
      "enabled": true,
      "duration": 5,
      "timer": true
    },
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false
    },
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false
    },
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false
    },
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false
    },
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false
    }
  ]
}';

我想update/replace“状态”与新文档中包含的任何信息在同一数组位置(预先不知道状态中包含的字段名称),例如:

   {
    "item": 0,
    "type": "switch",
    "name": "item 1",
    "status": {}
  },

变成:

   {
    "item": 0,
    "type": "switch",
    "name": "item 1",
    "status": {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false,
      "valueA" : 1
    }
  },

这是我到目前为止所做的,但不知道如何提取每个 resource[] 项目以将其字段插入项目 [].status

ALTER PROCEDURE sp_process_device_stat
    (@json nvarchar(MAX))
AS 
BEGIN
    UPDATE device
    SET [type] = Item.[type],
        [version] = Item.ver,  
        [ip] = Item.[ip], 
        [uptime] = Item.uptime, 
        [memory] = Item.mem, 
        [rssi] = Item.rssi, 
        [jsonStatus] = Item.resources,
        [DateLastSeen] = GETDATE()
    FROM 
        OPENJSON(@json)
        WITH
            ([serial] nvarchar(100),
             [type] nvarchar(100),
             [ver] nvarchar(100),
             [ip] nvarchar(100),
             [uptime] nvarchar(100),
             [mem] nvarchar(100),
             [rssi] nvarchar(100),
             [resources] nvarchar(max) as JSON) as Item
    JOIN 
        [Device] device ON Item.serial = device.serial;
END

你可以试试这个:

--声明你的两个 json 字符串

declare @json nvarchar(max)
set @json = '
[
  {
    "item": 0,
    "type": "switch",
    "name": "item 1",
    "status": {}
  },
  {
    "item": 1,
    "type": "switch",
    "name": "item 2",
    "status": {}
  },
  {
    "item": 2,
    "type": "switch",
    "name": "item 3",
    "status": {}
  },
  {
    "item": 3,
    "type": "switch",
    "name": "item 4",
    "status": {}
  },
  {
    "item": 4,
    "type": "switch",
    "name": "item 5",
    "status": {}
  },
  {
    "item": 5,
    "type": "switch",
    "name": "item 6",
    "status": {}
  },
  {
    "item": 6,
    "type": "switch",
    "name": "item 7",
    "status": {}
  },
  {
    "item": 7,
    "type": "switch",
    "name": "item 8",
    "status": {}
  }
]';

declare @jsonStat nvarchar(max)
set @jsonStat = '{
  "serial": "locker-7C9EBD6074F8",
  "type": "locker",
  "ver": "0.1",
  "ip": "192.168.1.133",
  "uptime": 79,
  "mem": 210888,
  "rssi": -36,
  "resources": [
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false,
      "valueA" : 1
    },
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false,
      "valueB" : "test B"
    },
    {
      "busy": true,
      "enabled": true,
      "duration": 5,
      "timer": true
    },
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false
    },
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false
    },
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false
    },
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false
    },
    {
      "busy": false,
      "enabled": true,
      "duration": 0,
      "timer": false
    }
  ]
}';

--查询将使用游标(因为JSON_MODIFY() 只允许单个更改)

DECLARE @insP INT;
DECLARE @stat NVARCHAR(MAX);

DECLARE cur CURSOR FOR SELECT B.[key] AS InsertPosition
                             ,B.[value] AS statusToInsert
                       FROM        OPENJSON(@jsonStat) WITH([resources] NVARCHAR(MAX) AS JSON) A
                       OUTER APPLY OPENJSON(A.resources) B;
OPEN CUR;
FETCH NEXT FROM cur INTO @insP,@stat;
WHILE @@FETCH_STATUS = 0
BEGIN
    --PRINT CONCAT(@insP ,' ->', @stat);
    SET @json = JSON_MODIFY(@json,CONCAT(N'$[',@insP,'].status'),JSON_QUERY(@stat));
    FETCH NEXT FROM cur INTO @insP,@stat;
END
CLOSE CUR;
DEALLOCATE CUR;

PRINT @json

简而言之:

  • 我们使用 WITH 子句从 @jsonStat 读取 resources 以告诉引擎该片段本身是 JSON 部分。
  • 我们使用另一个 APPLY OPENJSON 检索项目的位置和值。
  • 对于这个查询,我们使用游标向下遍历
  • 在游标内我们可以使用位置和内容分别执行JSON_MODIFY()一次
  • 最后PRINT说明成功

提示:如果您事先知道 JSON 的字段(并且预计不会对此进行任何更改),您可能会将其解构为 table 并在其中重建 JSON走。为此,您将使用 WITH 子句来获取表格结果中的所有字段,并在末尾使用 FOR JSON PATH 构建 JSON。