How to update a nested array in JSON in mssql

拥有回忆 提交于 2019-12-13 20:18:40

问题


I am using mssql and one column is having json data, I want to update that part of that json which is an array, by passing the id.

{  
   "customerName":"mohan",
   "custId":"e35273d0-c002-11e9-8188-a1525f580dfd",
   "feeds":[  
      {  
         "feedId":"57f221d0-c310-11e9-8af7-cf1cf42fc72e",
         "feedName":"ccsdcdscsdc",
         "format":"Excel",
         "sources":[  
            {  
               "sourceId":69042417,
               "name":"TV 2 Livsstil"
            },
            {  
               "sourceId":69042419,
               "name":"Turk Max"
            }
         ]
      },
      {  
         "feedId":"59bbd360-c312-11e9-8af7-cf1cf42fc72e",
         "feedName":"dfgdfgdfgdfgsdfg",
         "format":"XmlTV",
         "sources":[  
            {  
               "sourceId":69042417,
               "name":"TV 2 Livsstil"
            },
            {  
               "sourceId":69042419,
               "name":"Turk Max"
            }
         ]
      }
   ]
}

suppose if I am going to pass customerId and feedId, it should update the whole feed with the feed which I have passed.

I tried with below query, but no help.

UPDATE
    ExtractsConfiguration.dbo.Customers 
SET
    configJSON = JSON_MODIFY(configJSON,'$.feeds[]',{"feedName":"ccsdcdscsdc"})
WHERE
    CustomerId = '9ee07040-c001-11e9-b29a-55eb3439cd7c' 
    AND json_query(configJSON,'$.feeds[].feedId'='57f221d0-c310-11e9-8af7-cf1cf42fc72e');

回答1:


This, @mohan, is a tricky one and I took it on as a challenge to myself. There is a way to update a nested JSON object's value like you're asking, however, it's not as straight forward as it seems.

Because you're working within an array, you need the array's index in order to update a nested value. In your case you don't know the index within the array, however, you do have a key-value you can reference, in this case, your feedName.

In order to update your value, you first need to "unpack" your JSON so that you can filter for a specific feedName, "ccsdcdscsdc" in your example.

Here is an example that you can run in SSMS that will get you moving in the right direction.

The first thing I created was @Customers TABLE variable to mimic the data structure you showed in your example and inserted your sample data.

DECLARE @Customers TABLE ( CustomerId VARCHAR(50), configJSON VARCHAR(MAX) );
INSERT INTO @Customers ( CustomerID, configJSON ) VALUES ( '9ee07040-c001-11e9-b29a-55eb3439cd7c', '{"customerName":"mohan","custId":"e35273d0-c002-11e9-8188-a1525f580dfd","feeds":[{"feedId":"57f221d0-c310-11e9-8af7-cf1cf42fc72e","feedName":"ccsdcdscsdc","format":"Excel","sources":[{"sourceId":69042417,"name":"TV 2 Livsstil"},{"sourceId":69042419,"name":"Turk Max"}]},{"feedId":"59bbd360-c312-11e9-8af7-cf1cf42fc72e","feedName":"dfgdfgdfgdfgsdfg","format":"XmlTV","sources":[{"sourceId":69042417,"name":"TV 2 Livsstil"},{"sourceId":69042419,"name":"Turk Max"}]}]}' );

Running a SELECT against @Customers returns the following:

+--------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|              CustomerId              |                                                                                                                                                                                                                                    configJSON                                                                                                                                                                                                                                     |
+--------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 9ee07040-c001-11e9-b29a-55eb3439cd7c | {"customerName":"mohan","custId":"e35273d0-c002-11e9-8188-a1525f580dfd","feeds":[{"feedId":"57f221d0-c310-11e9-8af7-cf1cf42fc72e","feedName":"ccsdcdscsdc","format":"Excel","sources":[{"sourceId":69042417,"name":"TV 2 Livsstil"},{"sourceId":69042419,"name":"Turk Max"}]},{"feedId":"59bbd360-c312-11e9-8af7-cf1cf42fc72e","feedName":"dfgdfgdfgdfgsdfg","format":"XmlTV","sources":[{"sourceId":69042417,"name":"TV 2 Livsstil"},{"sourceId":69042419,"name":"Turk Max"}]}]} |
+--------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Next, I matched your rules for the update: Update a nested JSON value that is restricted to a specific CustomerId (9ee07040-c001-11e9-b29a-55eb3439cd7c) and a feedName (ccsdcdscsdc).

Like I mentioned, we need to "unpack" the JSON first because we don't know the specific key (index) value that should be updated. The easiest way to accomplish both tasks (unpack/update) is to use a Common Table Expression (CTE).

So, here's how I did that:

;WITH Config_CTE AS (

    SELECT * FROM @Customers AS Customer
    CROSS APPLY OPENJSON( configJSON, '$.feeds' ) AS Config
    WHERE
        Customer.CustomerId = '9ee07040-c001-11e9-b29a-55eb3439cd7c'
        AND JSON_VALUE( Config.value, '$.feedName' ) = 'ccsdcdscsdc'

)
UPDATE Config_CTE
SET configJSON = JSON_MODIFY( configJSON, '$.feeds[' + Config_CTE.[key] + '].format', 'MS Excel' );

The CTE allows us to "unpack" (I made this word up as it seemed fitting) the JSON contained in configJSON, which then allows us to apply a filter against the feedName.

AND JSON_VALUE( Config.value, '$.feedName' ) = 'ccsdcdscsdc'

You'll also note that we included the CustomerId rule:

Customer.CustomerId = '9ee07040-c001-11e9-b29a-55eb3439cd7c'

Both the CustomerId and feedName could easily be SQL variables.

So, what did this do? If we were to look at Configs_CTE resultset ( by changing the UPDATE... to SELECT * FROM Config_CTE ) we would see:

+--------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------+
|              CustomerId              |                                                                                                                                                                                                                                    configJSON                                                                                                                                                                                                                                     | key |                                                                                            value                                                                                             | type |
+--------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------+
| 9ee07040-c001-11e9-b29a-55eb3439cd7c | {"customerName":"mohan","custId":"e35273d0-c002-11e9-8188-a1525f580dfd","feeds":[{"feedId":"57f221d0-c310-11e9-8af7-cf1cf42fc72e","feedName":"ccsdcdscsdc","format":"Excel","sources":[{"sourceId":69042417,"name":"TV 2 Livsstil"},{"sourceId":69042419,"name":"Turk Max"}]},{"feedId":"59bbd360-c312-11e9-8af7-cf1cf42fc72e","feedName":"dfgdfgdfgdfgsdfg","format":"XmlTV","sources":[{"sourceId":69042417,"name":"TV 2 Livsstil"},{"sourceId":69042419,"name":"Turk Max"}]}]} |   0 | {"feedId":"57f221d0-c310-11e9-8af7-cf1cf42fc72e","feedName":"ccsdcdscsdc","format":"Excel","sources":[{"sourceId":69042417,"name":"TV 2 Livsstil"},{"sourceId":69042419,"name":"Turk Max"}]} |    5 |
+--------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------+

There is a bunch of information here, but what we really care about is the "key" column as this contains the feed index ( in this case 0 ) that we want to update.

With that, was able to complete the request and UPDATE format from "Excel" to "MS Excel" for the "feed" with the feedName of "ccsdcdscsdc".

This guy ( note the use of Config_CTE.[key] ):

UPDATE Config_CTE
SET configJSON = JSON_MODIFY( configJSON, '$.feeds[' + Config_CTE.[key] + '].format', 'MS Excel' );

Did it work? Let's look at the updated table's data.

+--------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|              CustomerId              |                                                                                                                                                                                                                                      configJSON                                                                                                                                                                                                                                      |
+--------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 9ee07040-c001-11e9-b29a-55eb3439cd7c | {"customerName":"mohan","custId":"e35273d0-c002-11e9-8188-a1525f580dfd","feeds":[{"feedId":"57f221d0-c310-11e9-8af7-cf1cf42fc72e","feedName":"ccsdcdscsdc","format":"MS Excel","sources":[{"sourceId":69042417,"name":"TV 2 Livsstil"},{"sourceId":69042419,"name":"Turk Max"}]},{"feedId":"59bbd360-c312-11e9-8af7-cf1cf42fc72e","feedName":"dfgdfgdfgdfgsdfg","format":"XmlTV","sources":[{"sourceId":69042417,"name":"TV 2 Livsstil"},{"sourceId":69042419,"name":"Turk Max"}]}]} |
+--------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Here's the updated JSON "beautified" (pretty sure I didn't make up that one).

{
    "customerName": "mohan",
    "custId": "e35273d0-c002-11e9-8188-a1525f580dfd",
    "feeds": [{
        "feedId": "57f221d0-c310-11e9-8af7-cf1cf42fc72e",
        "feedName": "ccsdcdscsdc",
        "format": "MS Excel",
        "sources": [{
            "sourceId": 69042417,
            "name": "TV 2 Livsstil"
        }, {
            "sourceId": 69042419,
            "name": "Turk Max"
        }]
    }, {
        "feedId": "59bbd360-c312-11e9-8af7-cf1cf42fc72e",
        "feedName": "dfgdfgdfgdfgsdfg",
        "format": "XmlTV",
        "sources": [{
            "sourceId": 69042417,
            "name": "TV 2 Livsstil"
        }, {
            "sourceId": 69042419,
            "name": "Turk Max"
        }]
    }]
}

Well, there you have it, format for feedName "ccsdcdscsdc" has been updated from "Excel" to "MS Excel". I was not clear on what you were trying to update, so I used format for my testing/example.

I hope this gets you moving in the right direction with your task. Happy coding!

Here's the complete example that can be run in SSMS:

-- CREATE A CUSTOMERS TABLE TO MIMIC SCHEMA --
DECLARE @Customers TABLE ( CustomerId VARCHAR(50), configJSON VARCHAR(MAX) );
INSERT INTO @Customers ( CustomerID, configJSON ) VALUES ( '9ee07040-c001-11e9-b29a-55eb3439cd7c', '{"customerName":"mohan","custId":"e35273d0-c002-11e9-8188-a1525f580dfd","feeds":[{"feedId":"57f221d0-c310-11e9-8af7-cf1cf42fc72e","feedName":"ccsdcdscsdc","format":"Excel","sources":[{"sourceId":69042417,"name":"TV 2 Livsstil"},{"sourceId":69042419,"name":"Turk Max"}]},{"feedId":"59bbd360-c312-11e9-8af7-cf1cf42fc72e","feedName":"dfgdfgdfgdfgsdfg","format":"XmlTV","sources":[{"sourceId":69042417,"name":"TV 2 Livsstil"},{"sourceId":69042419,"name":"Turk Max"}]}]}' );

-- SHOW CURRENT DATA --
SELECT * FROM @Customers;

-- UPDATE "format" FROM "Excel" to "MS Excel" FOR feedName: ccsdcdscsdc --
WITH Config_CTE AS (

    SELECT * FROM @Customers AS Customer
    CROSS APPLY OPENJSON( configJSON, '$.feeds' ) AS Config
    WHERE
        Customer.CustomerId = '9ee07040-c001-11e9-b29a-55eb3439cd7c'
        AND JSON_VALUE( Config.value, '$.feedName' ) = 'ccsdcdscsdc'

)
UPDATE Config_CTE
SET configJSON = JSON_MODIFY( configJSON, '$.feeds[' + Config_CTE.[key] + '].format', 'MS Excel' );

-- SHOW UPDATED DATA --
SELECT * FROM @Customers;

EDIT:

i wanted to update the feed with the given feedId with the whole new feed

To replace one "feed" with an entirely new feed, you may do the following:

-- REPLACE AN ENTIRE JSON ARRAY OBJECT  --
DECLARE @MyNewJson NVARCHAR(MAX) = '{"feedId": "this_is_an_entirely_new_node","feedName": "ccsdcdscsdc","format": "NewFormat","sources": [{"sourceId": 1,"name": "New Source 1"},{"sourceId": 2,"name": "New Source 2"}]}';

WITH Config_CTE AS (

    SELECT * FROM @Customers AS Customer
    CROSS APPLY OPENJSON( configJSON, '$.feeds' ) AS Config
    WHERE
        Customer.CustomerId = '9ee07040-c001-11e9-b29a-55eb3439cd7c'
        AND JSON_VALUE( Config.value, '$.feedName' ) = 'ccsdcdscsdc'

)
UPDATE Config_CTE
SET configJSON = JSON_MODIFY( configJSON, '$.feeds[' + Config_CTE.[key] + ']', JSON_QUERY( @MyNewJson ) );

After running this, the feeds now appear as:

{
  "customerName": "mohan",
  "custId": "e35273d0-c002-11e9-8188-a1525f580dfd",
  "feeds": [
    {
      "feedId": "this_is_an_entirely_new_node",
      "feedName": "ccsdcdscsdc",
      "format": "NewFormat",
      "sources": [
        {
          "sourceId": 1,
          "name": "New Source 1"
        },
        {
          "sourceId": 2,
          "name": "New Source 2"
        }
      ]
    },
    {
      "feedId": "59bbd360-c312-11e9-8af7-cf1cf42fc72e",
      "feedName": "dfgdfgdfgdfgsdfg",
      "format": "XmlTV",
      "sources": [
        {
          "sourceId": 69042417,
          "name": "TV 2 Livsstil"
        },
        {
          "sourceId": 69042419,
          "name": "Turk Max"
        }
      ]
    }
  ]
}

Note the use of JSON_QUERY( @MyNewJson ) in the UPDATE. This is important.

From Microsoft's Docs:

JSON_QUERY without its optional second parameter returns only the first argument as a result. Since JSON_QUERY always returns valid JSON, FOR JSON knows that this result does not have to be escaped.

If you were to pass @MyNewJson without the JSON_QUERY your new json would be escaped ( e.g., "customerName" becomes \"customerName\" ) as if it were being stored as plain text. JSON_QUERY will return unescaped, valid JSON which is necessary in your case.

Also note that the only change I made to replace the entire feed vs. a single item value was switching

'$.feeds[' + Config_CTE.[key] + '].format'

to

'$.feeds[' + Config_CTE.[key] + ']'.


来源:https://stackoverflow.com/questions/57591635/how-to-update-a-nested-array-in-json-in-mssql

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!