Search code examples
sqljsonsql-servernvarchar

SQL-SERVER: JSON_MODIFY on json column with "contains" field match


I'm trying to figure out how to modify a json stored inside a column of my table (SQL SERVER).

I want to remove a property of the JSON only if it matches with a specific string.

Imagine we have a record in our table with this JSON inside a column named "profile" (nvarchar(max)) :

{ 
  name: "goofy",
  class_cod_x: "1345",
}

Then another record presents this JSON:

{ 
  name: "donald",
  class_cod_y: "1457",
}

Now we want to search and update all the JSON inside our records that contains a property that startsWith "class_cod".

I want to remove all the "class_cod.." properties from that column.

I can imagine a query like this:

UPDATE myTable SET profile = JSON_MODIFY( profile , "startsWith(class_cod)" , null)

Is this possible? In that case, which is the correct syntax? Thanks in advance!

UPDATE Expected output:

{ 
  name: "goofy",
}

and

{ 
  name: "donald",
}

"class_cod" field completely removed.

UPDATE 2

  • SQL SERVER VERSION: Microsoft SQL Server 2016 (RTM) - 13.0.1601.5 (X64)

  • "name" and "class_cod.." are not the only possible field inside json column. That JSON is based on an enum of possible configurations.

  • Is it possible that a JSON contains multiple "class_cod.." fields. (extremely rare but possible)

    {
      name: mario,
      class_cod_10: 22,
      class_cod_20: 31,
    }
    

Solution

  • I don't think that you can use wildcards for path parameter in JSON_MODIFY() and if the JSON in the profile column has more than one class_cod_* key, you may try to generate and execute a dynamic statement. The final statement includes one UPDATE statement for each distinct class_cod_* key in all JSON objects from all rows in the table. Note, that you need to use lax mode (it's the default) to specify that the property referenced by path does not have to exist:

    Table:

    CREATE TABLE Data (profile nvarchar(max));
    -- Rows with one `class_cod_*` key
    INSERT INTO Data (profile) VALUES (N'{"name":"goofy","class_cod_x":"1345"}')
    INSERT INTO Data (profile) VALUES (N'{"name":"donald","class_cod_y":"1457"}')
    -- Row without `class_cod_*` key
    INSERT INTO Data (profile) VALUES (N'{"name":"tom"}')
    -- Row with one `class_cod_*` key and other keys
    INSERT INTO Data (profile) VALUES (N'{"name":"goofy","class_cod_x":"1345","age":10}')
    -- Row with two `class_cod_*` keys
    INSERT INTO Data (profile) VALUES (N'{"name":"jerry", "class_cod_x":"1345", "class_cod_y":"1345"}')
    

    Statement:

    DECLARE @stm nvarchar(max)
    SELECT @stm = (
       SELECT CONCAT(
          CONCAT('UPDATE Data SET profile = JSON_MODIFY(profile, ''lax $."', t.[key], '"'', null)'),
          '; '
       )   
       FROM (
          SELECT DISTINCT j.[key]
          FROM Data d
          CROSS APPLY OPENJSON(d.profile) j
          WHERE j.[key] LIKE N'class_cod_%' 
       ) t
       FOR XML PATH('')
    )
    PRINT @stm
    EXEC sp_executesql @stm
    

    Result:

    profile
    {"name":"goofy"}
    {"name":"donald"}
    {"name":"tom"}
    {"name":"goofy","age":10}
    {"name":"jerry"}