Обновить значение json из значения столбца sql - PullRequest
0 голосов
/ 11 июля 2019

Используя 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 ]

1 голос
/ 12 июля 2019

ОБНОВЛЕНИЕ 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'    

Это похоже на мой оригинальный ответ (см. Ниже). Тем не менее:

  • Теперь у нас есть 2 cross apply заявлений. Первый используется для разделения массива JSON на элементы, поэтому мы получаем ключ (индекс) и значение (объект JSON в виде строки), как описано здесь: https://docs.microsoft.com/en-us/sql/t-sql/functions/openjson-transact-sql?view=sql-server-2017#path
  • Второй выполняет то же, что и исходный CROSS APPLY, воздействуя только на один элемент массива.
  • Мы используем [key], возвращаемый первым cross apply, чтобы нацелиться на элемент в массиве, который мы хотим обновить в нашем операторе JSON_MODIFY.

Примечание: если ваш массив JSON может содержать несколько объектов, требующих обновления, лучшее решение, о котором я могу подумать, - это поместить приведенный выше оператор в цикл; так как 1 обновление будет обновлять только 1 индекс для данного JSON. Вот пример: 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, если вы не знаете:


Что касается сценариев, когда в массиве есть несколько элементов, в идеале мы бы использовали выражение фильтра, например:

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

Я подумаю над этим / есть ли что-нибудь чище, так как в настоящее время ни один не сидит комфортно ...

0 голосов
/ 12 июля 2019

Если я понимаю ваш вопрос, то одним из возможных подходов (если вы используете 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)   
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...