Updating a JSON field replaces whole document? - sql

In sql server 2016 I am expecting a document to have 3000+ fields in a JSON column. Can I update one field in the document without replacing to whole document. How can I do this?

You could use JSON_MODIFY function:
Updates the value of a property in a JSON string and returns the
updated JSON string.
JSON_MODIFY ( expression , path , newValue )
Something like:
UPDATE table_name
SET json_column = JSON_MODIFY(json_column, '$.name', 'new_name')
WHERE id = 1;

Related

How to manipulate String count in JSON in SQL Server

In one use case I need to write a Query which return 1 if string count is >=4 or else return 0.
Below is the JSON which contains string.
Below is the query, which is returning a single row.
Select * from [Frs_def_businessobjectlayouts] where Definition like '%Open In Parent%' AND name like
'Task.ResponsiveAnalyst'
Note - Definition is the column which contains JSON data
Could some one help me out here!!.
First, you need to query the JSON and store in a temp variable and declare the word you want to search in another variable and perform the below operation. You will get the number of times it occurred in the JSON as output.
DECLARE #string VARCHAR(MAX)="Query your JSON from the table and assign to this variable"
DECLARE #tosearch VARCHAR(MAX)='Open In Parent'
SELECT (DATALENGTH(#string)-DATALENGTH(REPLACE(#string,#tosearch,'')))/DATALENGTH(#tosearch)
AS OccurrenceCount

Prevent double-escaped JSON in FOR JSON output in SQL

I have a small problem in my case because in my case a column can contain text 'John' directly or text as array '["John","Smith"]' both. So how can I prevent double-escaped JSON in FOR JSON output? I think I am doing something wrong here. Please check my example:
Create table #jsonTest(NameList varchar(max))
insert into #jsonTest(NameList)
select '["John","Smith"]'
Now if I want its output it will give correct output from this (without escape character):
select JSON_QUERY(NameList) NameList from #jsonTest for json auto
Output:
[{"NameList":["John","Smith"]}]
Simple text example:
truncate table #jsonTest
insert into #jsonTest(NameList)
Select 'John'
Now for this I have to change my select query for the correct output because JSON_QUERY, as mentioned, it only returns objects and arrays. So i've changed it to this:
select case when ISJSON(NameList) = 1 then JSON_QUERY(NameList) else NameList end NameList from #jsonTest for json auto
Output:
[{"NameList":"John"}]
Now It will give correct output for now but if I insert previous data again and try upper select query
truncate table #jsonTest
insert into #jsonTest(NameList)
select '["John","Smith"]'
select case when ISJSON(NameList) = 1 then JSON_QUERY(NameList) else NameList end NameList from #jsonTest for json auto
Output:
[{"NameList":"[\"John\",\"Smith\"]"}]
then it is giving escape characters in output. What is wrong in the code?
This behaviour is explained in the documentation - If the source data contains special characters, the FOR JSON clause escapes them in the JSON output with '\'. Of course, as you already know, when JSON_QUERY() is used with FOR JSON AUTO, FOR JSON doesn't escape special characters in the JSON_QUERY return value.
Your problem is the fact, that your data is not always a JSON. So, one possible approach is to generate a statement with duplicate column names (NameList). By default FOR JSON AUTO does not include NULL values in the output, so the result is the expected JSON. Just note, that you must not use INCLUDE_NULL_VALUES in the statement or the final JSON will contain duplicate keys.
Table:
CREATE TABLE #jsonTest(NameList varchar(max))
insert into #jsonTest(NameList)
select '["John","Smith"]'
insert into #jsonTest(NameList)
Select 'John'
Statement:
SELECT
JSON_QUERY(CASE WHEN ISJSON(NameList) = 1 THEN JSON_QUERY(NameList) END) AS NameList,
CASE WHEN ISJSON(NameList) = 0 THEN NameList END AS NameList
FROM #jsonTest
FOR JSON AUTO
Result:
[{"NameList":["John","Smith"]},{"NameList":"John"}]

Update an existing JSON value inside a JSON Array in SQL

I want to update an existing JSON value inside a JSON array. I can append a new JSON to the JSON array using JSON_MODIFY. Suppose i have a JSON like :
[{"id":"101","name":"John"}, {"id":"102","name":"peter"}]
But i want to update only the json with id=102.
Is it possible using JSON_MODIFY()?
EDIT:
Actual data
{"Details":{"SId":{"Type":"string","Value":"1234"},"BookList":{"Type":"List","Value":[{"id": "101", "name": "Book1"},{"id": "102", "name": "Book2"}]},"SName":{"Type":"string","Value":"john"}}}
You could use CTE to parse it and combine path in UPDATE part:
WITH cte AS (
SELECT *
FROM t
CROSS APPLY OPENJSON(c) s
WHERE i = 1
AND JSON_VALUE(s.value, '$.id')=102
)
UPDATE cte
SET c = JSON_MODIFY(c, '$[' + cte.[key] + '].name', 'Joe');
DBFiddle Demo
Output:
-- Before
[{"id":"101","name":"John"}, {"id":"102","name":"peter"}]
-- After
[{"id":"101","name":"John"}, {"id":"102","name":"Joe"}]
This will work on SQL Server 2017+ or SQL Azure DB otherwise you will get error. More info about path literal
Updating JSON Data (Postgresql)
If the column in your table contains json data and you want to update this data, you can use the following structure:
UPDATE table_name SET column_name = '{"key" : value}'::jsonb
WHERE column_name::jsonb #> '{“new_key” : new_value}'::jsonb;
Note: Usually #> is used as the "contains" operator.
The best way is to generate the statement like this:
In this way you, won't get the error of "Cannot resolve the collation conflict between Latin1_General_BIN and SQL_Latin1_General_CP1_CI_AS"
Also, you won't get this error "The argument 2 of the JSON_MODIFY must be a string literal"
WITH cte AS (
SELECT
t.PrimaryKey,
JSON_VALUE([value], '$.id') as id,
t.JsonColumn,
o.*
,('UPDATE MyTable set JsonColumn = JSON_MODIFY(JsonColumn, ''$['+[key]+'].id'', ''NewVALUE'') WHERE PrimaryKey = '''+t.PrimaryKey COLLATE SQL_Latin1_General_CP1_CI_AS+ '''') as statement
FROM MyTable t
CROSS APPLY OPENJSON(JSON_QUERY(JsonColumn, '$')) o WHERE JSON_VALUE(o.value, '$.Id')= 1
)
select * from cte;

Update PostgreSQL hstore field with sql variable

I have table files which has hstore column details. In my sql statement I insert data to it:
UPDATE files SET details = 'new_users=>new_users_count'::hstore where id = v_file_id;
but I want to update this hstore field not with string but with variable that is available in my sql statement. How can I do this?
PL/pgSQL can't detect variables inside a string literal. You need to use the "constructor" method of the hstore type to pass a variable:
UPDATE files
SET details = hstore('new_users', p_new_user_count)
where id = v_file_id;
If p_new_user_count is defined as a number (rather than a varchar or text) you need to cast this to a text value:
UPDATE files
SET details = hstore('new_users', p_new_user_count::text)
where id = v_file_id;
Edit after the question was changed:
To do this for multiple variables you can either concatenate two hstore values:
details = hstore('new_users', p_new_user_count::text)||hstore('post_count', p_post_count::text)
or use arrays:
details = hstore(array['new_users','post_count'], array[p_user_count, p_post_count]::text[]);
This is all documented in the manual: http://www.postgresql.org/docs/current/static/hstore.html
In case if you need to replace only new_users in the hstore then
UPDATE files
SET details = details || hstore('new_users', p_new_user_count::text)
where id = v_file_id;
It will replace new_users if already present in the column or add a new entry.

How to replace a string in a SQL Server Table Column

I have a table (SQL Sever) which references paths (UNC or otherwise), but now the path is going to change.
In the path column, I have many records and I need to change just a portion of the path, but not the entire path. And I need to change the same string to the new one, in every record.
How can I do this with a simple update?
It's this easy:
update my_table
set path = replace(path, 'oldstring', 'newstring')
UPDATE [table]
SET [column] = REPLACE([column], '/foo/', '/bar/')
I tried the above but it did not yield the correct result. The following one does:
update table
set path = replace(path, 'oldstring', 'newstring') where path = 'oldstring'
UPDATE CustomReports_Ta
SET vchFilter = REPLACE(CAST(vchFilter AS nvarchar(max)), '\\Ingl-report\Templates', 'C:\Customer_Templates')
where CAST(vchFilter AS nvarchar(max)) LIKE '%\\Ingl-report\Templates%'
Without the CAST function I got an error
Argument data type ntext is invalid for argument 1 of replace function.
You can use this query
update table_name set column_name = replace (column_name , 'oldstring' ,'newstring') where column_name like 'oldstring%'
all answers are great but I just want to give you a good example
select replace('this value from table', 'table', 'table but updated')
this SQL statement will replace the existence of the word "table"
(second parameter) inside the given statement(first parameter) with the third parameter
the initial value is this value from table but after executing replace function it will be this value from table but updated
and here is a real example
UPDATE publication
SET doi = replace(doi, '10.7440/perifrasis', '10.25025/perifrasis')
WHERE doi like '10.7440/perifrasis%'
for example if we have this value
10.7440/perifrasis.2010.1.issue-1
it will become
10.25025/perifrasis.2010.1.issue-1
hope this gives you better visualization
select replace(ImagePath, '~/', '../') as NewImagePath from tblMyTable
where "ImagePath" is my column Name. "NewImagePath" is temporery
column Name insted of "ImagePath" "~/" is my current string.(old
string) "../" is my requried string.(new string)
"tblMyTable" is my table in database.
you need to replace path with the help of replace function.
update table_name set column_name = replace(column_name, 'oldstring', 'newstring')
here column_name refers to that column which you want to change.
Hope it will work.
If target column type is other than varchar/nvarchar like text, we need to cast the column value as string and then convert it as:
update URL_TABLE
set Parameters = REPLACE ( cast(Parameters as varchar(max)), 'india', 'bharat')
where URL_ID='150721_013359670'
You also can replace large text for email template at run time, here is an simple example for that.
DECLARE #xml NVARCHAR(MAX)
SET #xml = CAST((SELECT [column] AS 'td','',
,[StartDate] AS 'td'
FROM [table]
FOR XML PATH('tr'), ELEMENTS ) AS NVARCHAR(MAX))
select REPLACE((EmailTemplate), '[#xml]', #xml) as Newtemplate
FROM [dbo].[template] where id = 1