在SQLAzure中从JSON删除节点的已知子集

时间:2018-09-07 20:37:23

标签: sql json sql-server azure-sql-database

从前,有一行数据(大大简化了,实际的json数据为10KB +),因此:

ID, json
1, '{
      "a1.arr": [1,2,3],
      "a1.boo": true,
      "a1.str": "hello",
      "a1.num": 123
    }'

一个过程应该写入另一个记录,该记录主要具有不同的数据:

ID, json
2, '{
      "a1.arr": [1,2,3], //from ID 1
      "a2.arr": [4,5,6], //new (and so are all below)
      "a2.boo": false,
      "a2.str": "goodbye",
      "a2.num": 456
    }'

但是由于一些外部错误,来自ID 1的原始json集最终也以ID 2表示,所以现在该表如下所示:

ID, json
1, '{
      "a1.arr": [1,2,3],
      "a1.boo": true,
      "a1.str": "hello",
      "a1.num": 123
    }'
2, '{
      "a1.arr": [1,2,3],
      "a1.boo": true,    //extraneous
      "a1.str": "hello", //extraneous
      "a1.num": 123,     //extraneous
      "a2.arr": [4,5,6],
      "a2.boo": false,
      "a2.str": "goodbye",
      "a2.num": 456
    }'

我想知道是否有办法从ID 2记录中删除多余的行。

我相信ID 1中的整个JSON字符串在ID 2中表示为连续的块,因此字符串替换可以起作用,但是有可能发生了一些重新排序。但是,对于应该保留的元素有点混乱

还有一些a1。*节点的值被略微更改的机会(我没有做比较),但是我很乐意在评估是否使用节点名而不是它们的值时应删除一个节点。节点之一(a1.arr)应保留在ID 2中。因此结果集应如下所示:

ID, json
1, '{
      "a1.arr": [1,2,3],
      "a1.boo": true,
      "a1.str": "hello",
      "a1.num": 123
    }'
2, '{
      "a1.arr": [1,2,3],
      "a2.arr": [4,5,6],
      "a2.boo": false,
      "a2.str": "goodbye",
      "a2.num": 456
    }'

我已经开始使用https://dba.stackexchange.com/questions/168303/can-sql-server-2016-extract-node-names-from-json来获取要从ID 2中删除的ID 1中的节点名称列表,只是不确定如何将它们从ID 2的JSON中删除-大概是反序列化,减少和重新序列化序列?

1 个答案:

答案 0 :(得分:1)

您可以采用这种方法:

  1. 在具有openjson值的行上获取要替换为id=1的键
  2. 使用cross apply过滤带有id>1的行中的键
  3. 使用STRING_AGGgroup by重建JSON字符串而无需不需要的密钥

此代码应该有效:

declare @tmp table ([id] int, jsonValue nvarchar(max))
declare @source_json nvarchar(max)

insert into @tmp values
 (1, '{"a1.arr":[1,2,3],"a1.boo":true,"a1.str":"hello","a1.num":123}')
,(2, '{"a1.arr":[1,2,3],"a1.boo":true,"a1.str":"hello","a1.num":123,"a2.arr":[4,5,6],"a2.boo":false,"a2.str":"goodbye","a2.num":456}')
,(3, '{"a1.arr":[1,2,3],"a1.boo":true,"a1.str":"hello","a1.num":123,"a3.arr":[4,5,6],"a3.boo":false,"a3.str":"goodbye","a3.num":456}')

--get json string from id=1
select @source_json = jsonValue from @tmp where [id] = 1

--rebuild json string for id > 1 removing keys from id = 1
select t.[id],   
       '{' + STRING_AGG( '"' + g.[key] + '":"' + STRING_ESCAPE(g.[value], 'json')  + '"', ',') + '}' as [json]
from @tmp t cross apply openjson(jsonValue) g
where t.id > 1
    and g.[key] not in (select [key] from openjson(@source_json) where [key] <> 'a1.arr')
group by t.id

结果:

enter image description here