Update json string column with proper json in Postgres SQL - sql

I have a column which type is JSON but it contains JSON strings like this:
"{\"a\":1,\"b\":2,\"c\":3}"
I want to update the values in the column with proper JSON objects without the quotes and escapes like this:
{"a":1,"b":2,"c":3}
I've tried the following statement even tough it says it does update rows, the columns are still the same.
UPDATE table SET column = to_json(column);
It seems like the to_json function doesn't work since it is a JSON string?
How can I update those values?

You could cast the JSON column as text, remove the unwanted quotes and escapes, and then cast the resulting text as JSON.
update tbl
set js = trim(replace(js::text, '\',''), '"')::json
demo

You can use the #>> operator to extract the string and then convert it back to JSON with ::json:
UPDATE your_table
SET your_column = (your_column #>> '{}')::json;
A fully working demo:
create table your_table (
your_column json
);
insert into your_table (your_column) values ('"{\"a\":1,\"b\":2,\"c\":3}"'::json);
select your_column, your_column #>> '{}'
from your_table ;
your_column
?column?
"{"a":1,"b":2,"c":3}"
{"a":1,"b":2,"c":3}
update your_table
set your_column = (your_column #>> '{}')::json;
select *
from your_table;
your_column
{"a":1,"b":2,"c":3}

Related

SQL to replace all occurrences from table using REGEX_REPLACE

I would like to know how I can replace all occurrences (from any column in a table) of \\\\N with an empty string. I think I should use the REGEX_REPLACE function, but I've only been able to see examples of it used on one column inside Snowflake.
REGEXP_REPLACE( <subject> , <pattern> [ , <replacement> , <position> , <occurrence> , <parameters> ] )
What you're looking for is not possible natively in SQL. You could do
update your_table
set col1=replace(col1,'\\\\N',''),
col2=replace(col2,'\\\\N',''),
col3=replace(col3,'\\\\N',''),
....
I personally prefer the following because I can run the select portion to take a look at my output before making any changes
create or replace table your_table as
select top 0 * --to avoid having to write column names in subsequent select
from your_table
union all
select replace(col1,'\\\\N',''),
replace(col2,'\\\\N',''),
replace(col3,'\\\\N',''),
...
from your_table
You can generate the SQL to operate on each column using the 'show columns' then build a set of SQL statements using the lastqueryID
show columns in table mytable;
select 'update mytable set ' || "column_name" || ' = replace(' || "column_name" || ',''\\\\\\\\N'','''',);' from TABLE(RESULT_SCAN(LAST_QUERY_ID()));
My issue was that Snowflake by default replaced NULL values with \\N that's why I was seeing the exported file from my s3 bucket containing a double escaped newline characters. The issue wasn't a problem with the table itself but after export and setting the file_format option to override the default as empty did the trick.
copy into <#s3..> from <view> header = true max_file_size = 5368709120 Single=True overwrite = true file_format = (TYPE='CSV' COMPRESSION='NONE' DEFAULT_NULL=());

SQL / DB2 Array in Where declared in with

anyone does know a workaround for querying with parameters/variables for usage in where [...] in functions on db2 v11?
what i tried:
DECLARE #list varchar(23) = '1,2,3,4'
SELECT ...FROM tbl WHERE col IN (#list)
WITH test(val) AS (VALUES(ARRAY['5','9']))
SELECT ... FROM table, test WHERE col ANY(val)
both do not work, first one isn't db2 compatible, the second ones does not work cause he cant split the values.
any ideas or examples?
Try this:
SELECT t.*
FROM tbl t
WHERE EXISTS
(
select 1
from xmltable
(
'for $id in tokenize($s, ",") return <i>{string($id)}</i>'
passing '1,2,3,4' as "s"
columns
tok int path '.'
) v
where v.tok = t.col
);
You may use a parameter marker instead of the string constant 1,2,3,4 as for usual string paramter, if you want to provide such a list of integers as a comma separated string at runtime.
Declare local temporary table and insert your value list in that ?

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;

How to write null value to datetime array in PostgreSQL 8.3?

I try to execute query:
INSERT INTO table_name
(
timedate_array_field
)
VALUES
(
'{NULL, NULL}'
)
But I get error "Could not convert string to DateTime: 'null'".
Just add single quotes for the value:
INSERT INTO table_name (timedate_array_field)
VALUES ('{NULL, NULL}')
-> SQLfiddle
BTW, the data type is not called "DateTime" or "timedate" in Postgres, but timestamp.