从sql列值更新json值

时间:2019-07-11 20:08:22

标签: json sql-server

我要使用SQL Server获取列数据并将其复制到json对象列中

我正在使用SQL Server查询列和json数据。我想做的是将下面代码中的ename列中的数据复制到fieldvalue列中。如果我可以使用SQL做到这一点,那就太好了。

SELECT 
    a.id, a.ssn, a.ename, p.CaptionName, p.FieldName, p.FieldType, p.FieldValue
FROM 
    TelecentersDB.dbo.tblUissAssignments as a
CROSS APPLY 
    OPENJSON (details)
        WITH (CaptionName NVARCHAR(100),
              FieldName NVARCHAR(100),
              FieldType NVARCHAR(15),
              FieldValue NVARCHAR(50)) AS P
WHERE
    p.captionname = 'txtEname' 
    AND a.ssn = '000-00-0000'

详细信息列中的我的json字符串

[{"CaptionName":"txtEname","FieldName":null,"FieldType":null,"FieldValue":""}]

我真的不太喜欢sql,这是我要使用的。将数据复制到json对象后,我将删除ename列。

2 个答案:

答案 0 :(得分:1)

更新2019-07-11

这是经过修改的解决方案,适用于JSON中包含多个值的情况:https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=1fde45dfb604b2d5540c56f6c17a822d

update a
set details = JSON_MODIFY(details, '$[' + x.[key] + '].FieldValue', ename)
from dbo.tblUissAssignments a
CROSS APPLY OPENJSON (details, '$') x
CROSS APPLY OPENJSON (x.Value)
    WITH (CaptionName NVARCHAR(100),
        FieldName NVARCHAR(100),
        FieldType NVARCHAR(15),
        FieldValue NVARCHAR(50)) AS P
WHERE a.ssn = '000-00-0000'
and p.CaptionName = 'txtEname'    

这类似于我的原始答案(见下文)。但是:

注意:如果您的JSON数组可能包含多个需要更新的对象,那么我能想到的最好的解决方案是将上述语句放入循环中。因为1次更新只会更新给定JSON上的1个索引。这是一个示例:https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=120d2ac7dd3a024e5e503a5f64b0089e

declare @doWhileTrueFlag bit = 1

while (@doWhileTrueFlag = 1)
begin

    update a
    set details = JSON_MODIFY(details, '$[' + x.[key] + '].FieldValue', ename)
    from dbo.tblUissAssignments a
    CROSS APPLY OPENJSON (details, '$') x
    CROSS APPLY OPENJSON (x.Value)
        WITH (CaptionName NVARCHAR(100),
            FieldName NVARCHAR(100),
            FieldType NVARCHAR(15),
            FieldValue NVARCHAR(50)) AS P
    WHERE a.ssn = '000-00-0000'
    and p.CaptionName = 'txtEname'    
    and p.FieldValue != ename --if it's already got the correct value, don't update it again

    set @doWhileTrueFlag = case when @@RowCount > 0 then 1 else 0 end

end

原始答案

尝试一下:https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=b7b4d075cac6cd46239561ddb992ac90

update a
set details = JSON_MODIFY(details, '$[0].FieldValue', ename)
from dbo.tblUissAssignments a
cross apply
    OPENJSON (details)
        WITH (CaptionName NVARCHAR(100),
              FieldName NVARCHAR(100),
              FieldType NVARCHAR(15),
              FieldValue NVARCHAR(50)) AS P
where a.ssn = '000-00-0000'
and p.captionname = 'txtEname' 

此处提供有关JSON_MODIFY方法的更多信息:https://docs.microsoft.com/en-us/sql/t-sql/functions/json-modify-transact-sql?view=sql-server-2017

微妙的一点是,您正在更新包含json对象的json数组;不是单个对象。为此,您必须在根元素上包括索引。如果您不熟悉,请参阅此帖子以获取有关JsonPath的一些有用信息:https://support.smartbear.com/alertsite/docs/monitors/api/endpoint/jsonpath.html


对于数组中有多个项目的情况,理想情况下,我们将使用过滤器表达式,例如:

update a
set details = JSON_MODIFY(details, '$[?(@.CaptionName == ''txtEname'')].FieldValue', ename)
from dbo.tblUissAssignments a
where a.ssn = '000-00-0000'

遗憾的是,MS SQL尚不支持这些功能(请参见此出色的帖子:https://modern-sql.com/blog/2017-06/whats-new-in-sql-2016

因此,我认为我们需要进行讨厌的修改。我想到了两种这样的方法:

  • 实施循环以遍历所有匹配项
  • 从JSON转换为其他类型,然后再转换回JSON

我会考虑这些/是否有更清洁的东西,因为目前都没有舒适的坐姿...

答案 1 :(得分:0)

如果我理解您的问题,那么一种可能的方法(如果您使用SQL Server 2017+)是对OPENJSON()使用STRING_AGG()和字符串操作:

表格:

CREATE TABLE #Data (
   id int, 
   ssn varchar(12), 
   ename varchar(40),
   details nvarchar(max)
)
INSERT INTO #Data
   (id, ssn, ename, details)
VALUES
   (1, '000-00-0000', 'stackoverflow1', N'[{"CaptionName":"txtEname","FieldName":null,"FieldType":null,"FieldValue":""}, {"CaptionName":"txtEname","FieldName":null,"FieldType":null,"FieldValue":""}]'),
   (2, '000-00-0000', 'stackoverflow2', N'[{"CaptionName":"txtEname","FieldName":null,"FieldType":null,"FieldValue":""}, {"CaptionName":"txtEname","FieldName":null,"FieldType":null,"FieldValue":""}]')

声明:

SELECT
   d.id, d.ssn, d.ename,
   CONCAT(N'[', STRING_AGG(JSON_MODIFY(j.[value], '$.FieldValue', ename), ','), N']') AS details
FROM #Data d
CROSS APPLY OPENJSON (d.details) j
WHERE JSON_VALUE(j.[value], '$.CaptionName') = N'txtEname' AND (d.ssn = '000-00-0000')   
GROUP BY d.id, d.ssn, d.ename

输出:

id  ssn         ename           details
1   000-00-0000 stackoverflow1  [{"CaptionName":"txtEname","FieldName":null,"FieldType":null,"FieldValue":"stackoverflow1"},{"CaptionName":"txtEname","FieldName":null,"FieldType":null,"FieldValue":"stackoverflow1"}]
2   000-00-0000 stackoverflow2  [{"CaptionName":"txtEname","FieldName":null,"FieldType":null,"FieldValue":"stackoverflow2"},{"CaptionName":"txtEname","FieldName":null,"FieldType":null,"FieldValue":"stackoverflow2"}]

对于SQL Server 2016,您可以使用FOR XML PATH进行字符串聚合:

SELECT
   d.id, d.ssn, d.ename,
   CONCAT(N'[', STUFF(s.details, 1, 1, N''), N']') AS details
FROM #Data d
CROSS APPLY (
   SELECT CONCAT(N',', JSON_MODIFY(j.[value], '$.FieldValue', ename))
   FROM #Data
   CROSS APPLY OPENJSON (details) j
   WHERE 
      (JSON_VALUE(j.[value], '$.CaptionName') = N'txtEname') AND
      (ssn = '000-00-0000') AND
      (id = d.id) AND (d.ssn = ssn) AND (d.ename = ename)
   FOR XML PATH('')
) s(details)