Check for unique JSON keys in SQL Server - json

In SQL Server, how can I check if a string is valid JSON and the keys are all unique?
According to the T-SQL documentation, the regular ISJSON method does not check that the keys on the same level are unique.

You can use a Recursive Common Table Expression to do this. EG something like:
DECLARE #json NVARCHAR(4000) = N'{
"StringValue":"John",
"IntValue":45,
"TrueValue":true,
"FalseValue":false,
"NullValue":null,
"ArrayValue":["a","r","r","a","y"],
"ObjectValue":{"obj":"ect"}
}';
with q as
(
select [key] path, [key], value, type, 0 level
from openjson(#json)
union all
select concat(q.path,'\',n.[key]), n.[key], n.value, n.type, q.level + 1
from q
cross apply openjson(q.value) n
where q.type in (4,5)
)
select *, count(*) over (partition by path) pathCount
from q

Related

Extract feelds as key value from a json object in mariadb

Hello I want to extract the different field values of a json object as key value pairs, but I'm not able to do that.
I tried this
SELECT JSON_EXTRACT(chapters, '$[*].Id', '$[*].Name') AS rec
FROM `Novels`
WHERE 1
but it result looks like this
["1","first Name","2","second name"]
any idea on how to convert it to something like this
{"1":"first Name","2":"second name"}
Thanks in advance!
Depending on the result, the concerned value of the chapters column should be
'[ {"Id":"1","Name":"first name"}, {"Id":"2","Name":"second name"} ]'
JSON_EXTRACT() can be applied for each element of the array in order to determine Id values as keys part, and Name values as values part.
And then, JSON_UNQUOTE() can be applied to get rid of double-quotes while generating rows for each individual array elements. JSON_OBJECTAGG is used to aggregate all those extracted objects at the last step provided that MariaDB version is 10.5+:
WITH n AS
(
SELECT #i := #i + 1 AS rn,
JSON_UNQUOTE(JSON_EXTRACT(chapters, CONCAT('$[',#i-1,'].Id'))) AS js_id,
JSON_UNQUOTE(JSON_EXTRACT(chapters, CONCAT('$[',#i-1,'].Name'))) AS js_name
FROM information_schema.tables
CROSS JOIN ( SELECT #i := 0, chapters FROM `Novels` ) n
WHERE #i < JSON_LENGTH(JSON_EXTRACT(chapters, '$[*]'))
)
SELECT JSON_OBJECTAGG(js_id,js_name) AS Result
FROM n
A Workaround might be given for DB version prior to 10.5 as
SELECT CONCAT('{',
GROUP_CONCAT(
REPLACE(
REPLACE( JSON_OBJECT(js_id,js_name) , '}', '')
, '{', '')
)
, '}') AS Result
FROM n
Demo
One option uses json_table() to unnest the array to rows (available in MySQL 8 only) then aggregation:
select
t.*,
(
select json_objectagg('id', x.id, 'name', x.name)
from json_table(
t.chapter,
'$[*]'
columns (
id int path '$.Id',
name varchar(50) path '$.Name'
)
) as x
) as obj
from mytable t

Parse JSON in Microsoft SQL

I have the following JSON,
And I have no clue as to how to parse "Value" (When you run it in SQL you get 3 columns key, value and type)
Any ideas?
DECLARE #json NVARCHAR(MAX);
SET #json = N'[
{"salesDate":"2020-03-02","storeSales":[
{"storeId":"104","sales":[
{"productId":"20002","salesVolume":0.700,"salesQuantity":2,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}},
{"productId":"74301","salesVolume":0.750,"salesQuantity":1,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}},
{"productId":"642401","salesVolume":0.750,"salesQuantity":1,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}},
{"productId":"784001","salesVolume":2.100,"salesQuantity":3,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}},
{"productId":"1013801","salesVolume":1.500,"salesQuantity":2,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}},
{"productId":"1202801","salesVolume":0.750,"salesQuantity":1,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}},
{"productId":"1209901","salesVolume":0.700,"salesQuantity":1,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}},
{"productId":"1282201","salesVolume":0.750,"salesQuantity":1,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}},
{"productId":"3317301","salesVolume":1.500,"salesQuantity":2,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}},
{"productId":"4801301","salesVolume":0.700,"salesQuantity":1,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}},
{"productId":"5780106","salesVolume":6.000,"salesQuantity":2,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}},
{"productId":"7964902","salesVolume":0.375,"salesQuantity":1,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}},
{"productId":"10785001","salesVolume":0.750,"salesQuantity":1,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}},
{"productId":"11037501","salesVolume":1.500,"salesQuantity":2,"lastChanged":{"date":"2020-03-03","time":"07:28:06"}}]}]}]';
SELECT *
FROM OPENJSON(#json)
Assuming you wanted to get the sales data try this (although I am not 100% sure if this query offers the best performance)
SELECT [SalesDate], [StoreId], [ProductId], [SalesVolume], [SalesQuantity]
FROM OPENJSON(#json) WITH (
[SalesDate] DATETIME '$.salesDate',
[StoreSales] NVARCHAR(MAX) '$.storeSales' AS JSON
)
OUTER APPLY OPENJSON(StoreSales) WITH (
[StoreId] INT '$.storeId',
[Sales] NVARCHAR(MAX) '$.sales' AS JSON
)
OUTER APPLY OPENJSON(Sales) WITH (
[ProductId] INT '$.productId',
[SalesVolume] DECIMAL(18, 4) '$.salesVolume',
[SalesQuantity] INT '$.salesQuantity'
)
Value is the first object in your JSON array, which is itself another JSON object. SO you will have to read it using other SQL Server JSON utilities. For example:
SELECT JSON_VALUE(Value, '$.salesDate')
FROM OPENJSON(#json)

parsing json values in sql server

I want to parse the below json values.
DECLARE #json NVARCHAR(MAX)
SET #json =
N'[{"ID":1,"Roles":[1,2]},{"ID":2"Roles":[1,2,3]},{"ID":3,"Roles":[1,2,3,4]}]'
i want to display below output
Required Output
ID ROLES
1 1
1 2
2 1
2 2
2 3
3 1
3 2
3 3
3 4
I want query for this.
An OPENJSON with a WITH unrolls the outer layer, then a regular one can be used to unroll the arrays. (The alias isn't strictly necessary, I just think it's clearer, and it becomes necessary if you have to peel even more layers.)
SELECT ID, R.[Value] AS [Role]
FROM OPENJSON(#json) WITH (
ID INT,
[Roles] NVARCHAR(MAX) AS JSON
)
CROSS APPLY OPENJSON([Roles]) R
Try this:
DECLARE #json NVARCHAR(MAX)
SET #json =
N'[{"ID":1,"Roles":[1,2]},{"ID":2, "Roles":[1,2,3]},{"ID":3,"Roles":[1,2,3,4]}]'
SELECT pvt.[ID]
,roles.value
FROM
(
SELECT js.[key] as [key_id]
,roles.[key]
,roles.value
FROM OPENJSON(#json) js
CROSS APPLY OPENJSON(js.value) roles
) DS
PIVOT
(
MAX([value]) FOR [key] IN ([ID], [Roles])
) PVT
CROSS APPLY OPENJSON(pvt.roles) roles
or just this:
SELECT JSON_VALUE(js.value, '$.ID')
,ds.[value]
FROM OPENJSON(#json) js
CROSS APPLY OPENJSON(JSON_QUERY(js.value, '$.Roles')) ds;

How to get Key Value as resultset from Oracle JSON column using JSON_TABLE

I have googled aplenty, and can't seem to find a simple solution to my simple use case.
I have a json column in an Oracle 12C database (actually a varchar with json constraint of course), and in that column I store a representation of a Map like this
{
"a":9.0847,
"b":859.947
}
In plsql I would like to return a result set of that looks like this
key val
a 9.0847
b 859.947
I have tinkered with seemingly infinite variations of this below, and all the examples are too contrived for my use case.
select b.* from mytable a,json_table(myJsonCol,'$'
columns ( value varchar2(500) path '$.myjsonkey')) b
but this only returns a list of values, without the corresponding keys.
The json data is ALWAYS string-double key vals.
thanks
EDIT
To add a bit more context, i use json_each in postgres to do this now, and i'm looking for a similar method in Oracle.
Here's a generic solution that I tried on 18c and 21c, which uses PL/SQL APIs from within a SQL WITH function to produce the desired output (you can, of course, also store the function):
with
function json_keys(j varchar2) return clob as
jo json_object_t;
k json_key_list;
r clob;
begin
jo := json_object_t(j);
k := jo.get_keys();
select coalesce(
json_arrayagg(column_value returning clob),
json_array(returning clob)
)
into r
from table (k);
return r;
end;
select o, json_keys(o)
from (
select '{}' as o from dual union all
select '{"a":1}' from dual union all
select '{"a":1,"b":2}' from dual
) t;
Resulting in:
|O |JSON_KEYS(O)|
|-------------|------------|
|{} |[] |
|{"a":1} |["a"] |
|{"a":1,"b":2}|["a","b"] |
Try this:
declare
jo JSON_OBJECT_T;
i NUMBER;
keys JSON_KEY_LIST;
CURSOR c_json IS
SELECT myJsonCol FROM mytable;
begin
FOR rec IN c_json
LOOP
jo := JSON_OBJECT_T.parse(rec.myJsonCol);
keys := jo.get_keys;
dbms_output.put_line('KEY VAL');
FOR i in 1..keys.COUNT
LOOP
dbms_output.put_line(keys(i) || ' ' || jo.get_Number(keys(i)));
END LOOP;
END LOOP;
END;
/
Your JSON value is a single tuple, so you could use UNPIVOT to turn it into a table of key/value pairs:
with mydata as (
select '{
"a":9.0847,
"b":859.947
}' myjsoncol
from dual
), q as (
select json_value(mydata.myjsoncol, '$.a') ca
,json_value(mydata.myjsoncol, '$.b') cb
from mydata
) select * from q
unpivot (val for key in (ca as 'a', cb as 'b'));
KEY VAL
=== =======
a 9.0847
b 859.947
Live SQL: https://livesql.oracle.com/apex/livesql/s/d31n9re90y6cpghi4i3m9hfoh
For Oracle 11g version Json manupulation is not supported. So we must use basic functions : SUBSTR / INSTR / SUBSTR
Check solution on another thread : Manipulating JSON data with SQL in Oracle

passing comma deligma string to IN clause in sql server

SQL statement:
(select top 1 [egrp_name] from [Enotify Group] where [egrp_id] in (a.grp_id) )
e value of a.grp_id is '0,1145' and i am getting error
Conversion failed when converting the varchar value '0,1145' to data type int.
Can anybody tell me how can i change '0,1145' to 0,1145 in above case, so my query does work and also if their is any other way to do this
You can use a split-string function to change your comma delimited string into a table.
select top(1) [egrp_name]
from [Enotify Group]
where [egrp_id] in (
select Value
from dbo.SplitInts(a.grp_id)
);
One version of a split-string function that you can use if you like:
create function dbo.SplitInts(#Values nvarchar(max)) returns table with schemabinding
as
return
(
select T2.X.value(N'.', N'int') as Value
from (select cast(N'<?X '+replace(#Values, N',', N'?><?X ') + N'?>' as xml).query(N'.')) as T1(X)
cross apply T1.X.nodes(N'/processing-instruction("X")') as T2(X)
);
Or you can use like.
select top(1) [egrp_name]
from [Enotify Group]
where ','+a.grp_id +',' like '%,'+cast(egrp_id as varchar(11))+',%' ;