How to write a select query to get the index value from Json object - sql

I have the below JSON object. I need to write a select query to get the index values of Object JSON array. Kind of getting the sequence value.
{
"Model": [
{
"ModelName": "Test Model",
"Object": [
{
"ID": 1,
"Name": "ABC",
},
{
"ID": 11,
"Name": "ABCD",
},
{
"ID": 15,
"Name": "ABCDE",
},
]
}]}
Expected Output:
Index_Value
1
2
3

If I understand the question correctly and you want to get the index of the items in the Object JSON array, you need to use OPENJSON() with default schema. The result is a table with columns key, value and type and in case of JSON array, the key column holds the index of each item in the array (0-based):
JSON:
DECLARE #json nvarchar(max) = N'{
"Model":[
{
"ModelName":"Test Model",
"Object":[
{
"ID":1,
"Name":"ABC"
},
{
"ID":11,
"Name":"ABCD"
},
{
"ID":15,
"Name":"ABCDE"
}
]
}
]
}'
Statement:
SELECT CONVERT(int, j2.[key]) + 1 AS item_id
FROM OPENJSON (#json, '$.Model') j1
CROSS APPLY OPENJSON(j1.[value], '$.Object') j2
But if you want to get the values of the ID keys in the Object JSON array, the statement is different:
SELECT j2.ID
FROM OPENJSON (#json, '$.Model') j1
CROSS APPLY OPENJSON(j1.[value], '$.Object') WITH (
ID int '$.ID'
) j2
Note, that you need two OPENJSON() calls, because the input JSON has nested array structure. Of course, if Model JSON array has always one item, you may simplify the statement using an appropriate path:
SELECT CONVERT(int, [key]) + 1 AS item_id
FROM OPENJSON (#json, '$.Model[0].Object')
Finally, to get index, ID and Name, you should use the following statement, which assumes, that $.Model JSON array has more than one item and defines ID and Name columns with the appropraite data types:
SELECT
CONVERT(int, j2.[key]) + 1 AS ItemID,
j3.ID, j3.Name
FROM OPENJSON (#json, '$.Model') j1
CROSS APPLY OPENJSON(j1.[value], '$.Object') j2
CROSS APPLY OPENJSON(j2.[value], '$') WITH (
ID int '$.ID',
Name varchar(50) '$.Name'
) j3

DECLARE #json nvarchar(max) = N'{
"Model":[
{
"ModelName":"Test Model",
"Object":[
{
"ID":1,
"Name":"ABC"
},
{
"ID":11,
"Name":"ABCD"
},
{
"ID":15,
"Name":"ABCDE"
}
]
}
]
}'
declare #i int=0;
SELECT
j2.ID, j2.Name
FROM OPENJSON (#json, '$.Model') j1
CROSS APPLY OPENJSON(j1.[value],concat('$.Object[',#i,']')) WITH (
ID i`enter code here`nt '$.ID', Name varchar(100) '$.Name'
) j2
Results:-
ID
Name
11
ABCD

you can select the key columns in select clause no need to mention in with of crossjoin.
SELECT
distinct t.id,
JSON_VALUE(AttsData.[value], '$.address') as address,
JSON_VALUE(AttsData.[value], '$.name') as name,
JSON_VALUE(AttsData.[value], '$.owner_name') as owner_name,
JSON_VALUE(AttsData.[value], '$.project') as project
,CONVERT(int, AttsData.[key]) index_id
FROM mytablewithjsonfeild t
CROSS APPLY OPENJSON (t."jsonfeild",N'$.parentkey') as AttsData
Above query, from the table I have cross joined the JSON field. and in select statement i have taken the specific keys.
and CONVERT(int, AttsData.[key]) to get the index of the elements

Related

SQL OPENJSON get only first element

I'm trying to get data from only the first element in the JSON string because the first element will always be the newest. I have tried a few things with no luck. Maybe someone here can point me in the right direction.
This is the JSON string:
[
{
"wd:Transaction_Log_Entry": [
{
"wd:Transaction_Log_Reference": {
"#wd:Descriptor": "Rescind of End Contract: XXXX (Rescinded)",
"wd:ID": {
"#wd:type": "WID",
"#text": "fb27bafef89b101b5bf865947b420000"
}
},
"wd:Transaction_Log_Data": {
"wd:Transaction_Log_Description": "Rescind of End Contract: XXXX (Rescinded)",
"wd:Transaction_Effective_Moment": "2023-01-18T09:00:00+01:00",
"wd:Transaction_Entry_Moment": "2023-01-19T10:49:00.868+01:00",
"wd:Is_Rescind_Or_Rescinded": "1",
"wd:Is_Correction_Or_Corrected": "0"
}
},
{
"wd:Transaction_Log_Reference": {
"#wd:Descriptor": "End Contract: XXXX (Rescinded)",
"wd:ID": {
"#wd:type": "WID",
"#text": "a4a0fd2c2df8101bd1c6bde5f5710000"
}
},
"wd:Transaction_Log_Data": {
"wd:Transaction_Log_Description": "End Contract: XXXX (Rescinded)",
"wd:Transaction_Effective_Moment": "2023-01-18T09:00:00+01:00",
"wd:Transaction_Entry_Moment": "2023-01-18T12:41:43.867+01:00",
"wd:Is_Rescind_Or_Rescinded": "1",
"wd:Is_Correction_Or_Corrected": "0"
}
}
]
}
]
And my SQL query - I'm selecting the JSON string from a table that has the column name 'Transaction_Log_Entry_Data'
SELECT Effective_Moment, Entry_Moment
FROM [dbo].[Daily_And_Future_Terminations_Transaction_Log_Source]
CROSS APPLY OPENJSON (Transaction_Log_Entry_Data, '$[0]."wd:Transaction_Log_Entry"')
WITH (
Effective_Moment NVARCHAR(50) '$."wd:Transaction_Log_Data"."wd:Transaction_Effective_Moment"',
Entry_Moment NVARCHAR(50) '$."wd:Transaction_Log_Data"."wd:Transaction_Entry_Moment"'
)
And my results is 2 rows and I only want data from the first element:
Best regards
Ole
You have an arry in an array, so you must write.
JSOn is an interesting data structure, but you need a lot of experience with it
SELECT Effective_Moment, Entry_Moment
FROM [Daily_And_Future_Terminations_Transaction_Log_Source]
CROSS APPLY OPENJSON (Transaction_Log_Entry_Data, '$[0]."wd:Transaction_Log_Entry"[0]')
WITH (
Effective_Moment NVARCHAR(50) '$."wd:Transaction_Log_Data"."wd:Transaction_Effective_Moment"',
Entry_Moment NVARCHAR(50) '$."wd:Transaction_Log_Data"."wd:Transaction_Entry_Moment"'
)
Effective_Moment
Entry_Moment
2023-01-18T09:00:00+01:00
2023-01-19T10:49:00.868+01:00
fiddle
SELECT TOP 1 Effective_Moment, Entry_Moment
FROM [Daily_And_Future_Terminations_Transaction_Log_Source]
CROSS APPLY OPENJSON (Transaction_Log_Entry_Data, '$[0]."wd:Transaction_Log_Entry"')
WITH (
Effective_Moment NVARCHAR(50) '$."wd:Transaction_Log_Data"."wd:Transaction_Effective_Moment"',
Entry_Moment NVARCHAR(50) '$."wd:Transaction_Log_Data"."wd:Transaction_Entry_Moment"'
)
ORDEr By Entry_Moment DESC
Effective_Moment
Entry_Moment
2023-01-18T09:00:00+01:00
2023-01-19T10:49:00.868+01:00
fiddle

How to select a JSON array to a table

Is it possible to output a nested array into a table?
I intend to loop over the orders array and capture the line_item id's for the current iteration.
SQL
DECLARE #json NVARCHAR(4000) = N'
{
"orders":[
{
"id":123,
"line_items":[
{
"id":1
},
{
"id":2
}
]
}
]
};'
SELECT
[LineId]
FROM OPENJSON (#json, '$.orders.line_items') WITH (
[LineId] BIGINT '$.id'
);
Desired output
id
1
2
Here is a working option using a CROSS APPLY to extract the array.
SELECT id = JSON_VALUE(B.Value,'$.id')
FROM OPENJSON(#json, '$.orders') A
CROSS APPLY OPENJSON (A.value, '$.line_items') as B
Results
id
1
2

JSON_MODIFY all values without looping

See sample below. How would I use JSON_MODIFY or other way to modify all the "disc" values to "100" without having to update each array item in a loop?
create table #temp_data (json_text nvarchar(max))
insert into #temp_data select
'
"curr":"USD",
"items":[
{
"line":1,
"disc":10,
},
{
"line":2,
"disc":11
},
{
"line":3,
"disc":12,
}
]
}'
select * from #temp_data
We don't actually need to parse the rest of the JSON, we only need the $.items part. So we can APPLY that property with OPENJSON, then reassemble it with JSON_MODIFY:
UPDATE t
SET json_text = JSON_MODIFY(t.json_text, '$.items', v.items)
FROM temp_data t
CROSS APPLY (
SELECT line, disc = 100
FROM OPENJSON(t.json_text, '$.items') WITH (line int) AS items
FOR JSON PATH
) v(items);
In the WITH block, we need to add all properties we are not modifying, then in the inner SELECT we add any columns we want to change.
One way would be to use the traditional SQL DML UPDATE to replace the json_text with a new piece of JSON derived from the existing. Something like this
JSON_MODIFY could be added if that's a requirement.
drop table if exists #temp_data;
go
create table #temp_data (json_text nvarchar(max));
insert into #temp_data(json_text) values(
N'{
"curr":"USD",
"items":[
{
"line":1,
"disc":10
},
{
"line":2,
"disc":11
},
{
"line":3,
"disc":12
}
]
}');
update #temp_data
set json_text=( select json_value(d.json_text, N'strict $.curr') curr,
(select j2.line, j2.disc+100 as disc
from #temp_data td
cross apply openjson(td.json_text)
with(curr nvarchar(20),
items nvarchar(max) as json) j1
cross apply openjson(j1.items)
with(line int,
disc int) j2
for json path) items
from #temp_data d
for json path, without_array_wrapper);
select * from #temp_data;
{
"curr": "USD",
"items": [
{
"line": 1,
"disc": 210
},
{
"line": 2,
"disc": 211
},
{
"line": 3,
"disc": 212
}
]
}
It's not possible to use a wildcard in a JSON_MODIFY() call, so one possible approach are the following steps:
Parse the $.items array from the stored JSON using OPENJSON() and explicit schema including the line key.
Set the new value for the disc key for each item of this array.
Generate the JSON again using FOR JSON PATH.
Update the table with JSON_MODIFY().
Table:
create table #temp_data (json_text nvarchar(max))
insert into #temp_data (json_text)
VALUES (N'
{
"curr":"USD",
"items":[
{"line":1, "disc":10},
{"line":2, "disc":11},
{"line":3, "disc":12}
]
}')
Statement:
UPDATE #temp_data
SET json_text = JSON_MODIFY(
json_text,
'$.items',
(
SELECT line, 100 AS disc
FROM OPENJSON(json_text, '$.items') WITH (line int '$.line')
FOR JSON PATH
)
)
Result:
json_text
{
"curr":"USD",
"items":[{"line":1,"disc":100},{"line":2,"disc":100},{"line":3,"disc":100}]
}

SQL Query to get the json array values by comma separated

I have the below Json object. I need to get the task names by comma (,) separated.
{
"Model": [
{
"ModelName": "Test Model",
"Object": [
{
"ID": 1,
"Name": "ABC",
"Task" : [
{
TaskID : 1222,
Name: "TaskA"
},
{
TaskID : 154,
Name: "TaskB"
}
]
},
{
"ID": 11,
"Name": "ABCD",
"Task" : [
{
TaskID : 222,
Name: "TaskX"
},
{
TaskID : 234,
Name: "TaskY"
}
]
},
]
}]}
The expected Output should be in the below table. I need the task names should be comma separated.
ModelName ObjectID ObjectName TaskName
Test Model 1 ABC TaskA, TaskB
Test Model 11 ABCD TaskX, TaskY
I tried the below query. But I don't know how to group Task names.
SELECT S1.ModelName,
S2.ID AS ObjectID,
S2.Name AS ObjectName,
S3.TaskName
FROM TableA
CROSS APPLY OPENJSON(JsonData)
WITH (Model NVARCHAR(MAX) '$.Model[0]' AS JSON) S1
CROSS APPLY OPENJSON (S1.Model)
WITH (Object NVARCHAR(MAX) '$.Object' AS JSON,
ID INT '$.ID',
Name NVARCHAR(250) '$.Name') S2
CROSS APPLY OPENJSON (S2.Object)
WITH (Task NVARCHAR(MAX) '$.Task' AS JSON ,
TaskName NVARCHAR(MAX) '$.TaskName') S3
Corrected the json to a valid format and here is the SQL query for your desired output. The below query compiles and runs well in Oracle Compiler
SELECT * FROM JSON_TABLE(
'{"Model":[{"ModelName":"Test Model","Object":[{"ID":1,"Name":"ABC","Task":[{"TaskID":1222,"Name":"TaskA"},{"TaskID":154,"Name":"TaskB"}]},{"ID":11,"Name":"ABCD","Task":[{"TaskID":222,"Name":"TaskX"},{"TaskID":234,"Name":"TaskY"}]}]}]}',
'$.Model[*]'
COLUMNS (
-- L_MODEL_ROWNUM FOR ORDINALITY,
L_MODEL_NAME VARCHAR2(10) PATH '$.ModelName',
NESTED PATH '$.Object[*]' COLUMNS (
--L_OBJECT_ROWNUM FOR ORDINALITY,
L_OBJECT_ID NUMBER PATH '$.ID',
L_OBJECT_NAME VARCHAR2(10) PATH '$.Name',
L_TASK_NAME VARCHAR2(100) FORMAT JSON WITH WRAPPER PATH '$.Task[*].Name'
)
)
)
Here is the query which runs on SQL Server 2019,
DECLARE #JSONDATA NVARCHAR(MAX);
SET
#JSONDATA = N'{"Model":[{"ModelName":"Test Model","Object":[{"ID":1,"Name":"ABC","Task":[{"TaskID":1222,"Name":"TaskA"},{"TaskID":154,"Name":"TaskB"}]},{"ID":11,"Name":"ABCD","Task":[{"TaskID":222,"Name":"TaskX"},{"TaskID":234,"Name":"TaskY"}]}]}]}';
WITH TASK AS
(
SELECT
MODEL,
ID,
NAME,
TASK_NAME
FROM
OPENJSON(#JSONDATA) WITH (MODELS NVARCHAR(MAX) '$.Model' AS JSON) CROSS APPLY OPENJSON(MODELS) WITH (MODEL NVARCHAR(80) '$.ModelName', OBJECTS NVARCHAR(MAX) '$.Object' AS JSON) CROSS APPLY OPENJSON(OBJECTS ) WITH (ID INT '$.ID', NAME NVARCHAR(250) '$.Name', TASKS NVARCHAR(MAX) '$.Task' AS JSON ) CROSS APPLY OPENJSON (TASKS) WITH (TASK_NAME NVARCHAR(80) '$.Name')
)
SELECT DISTINCT
MODEL AS MODELNAME,
ID AS OBJECTID,
NAME AS OBJECTNAME,
STUFF((
SELECT
',' + [TASK_NAME]
FROM
TASK T1
WHERE
T1.[ID] = T2.[ID] FOR XML PATH('')), 1, 1, '') AS TASKNAME
FROM
TASK T2;

SQL Query on Json object inside multiple JSON objects

I have the below Json string. I need to write a query to get the TP records. Without providing the index value, we need get the result.
{
"S": [
{
"Name": "Project1",
"SP": [
{
"ID": 1,
"Name": "Test1",
"TP": [
{
"TID": 11,
"TName": "TT1",
},
{
"TID": 12,
"TName": "TT2",
},
]
},
{
"ID": 2,
"Name": "Test2",
"TP": [
{
"TID": 13,
"TName": "TT3",
},
{
"TID": 14,
"TName": "TT4",
},
]
},
]}]}
How do I query to get the TP values.
Expected Result:
TID TName
11 TT1
12 TT2
13 TT3
14 TT4
You can use OPENJSON function containing WITH Clause added as many CROSS APPLY Clause as upto see all sub-arrays :
SELECT S3.TID, S3.TName
FROM tab
CROSS APPLY OPENJSON(JsonData)
WITH ( S nvarchar(max) '$.S' AS JSON) AS S0
CROSS APPLY OPENJSON (S0.S)
WITH (
SP nvarchar(max) '$.SP' AS JSON ) S1
CROSS APPLY OPENJSON (S1.SP)
WITH (
TP nvarchar(max) '$.TP' AS JSON ) S2
CROSS APPLY OPENJSON (S2.TP)
WITH (
TID nvarchar(500) '$.TID',
TName nvarchar(500) '$.TName' ) S3
Demo
Update : If the first array S is fixed to contain only one item, we can reduce one-step as
SELECT S3.TID, S3.TName
FROM tab
CROSS APPLY OPENJSON(JsonData)
WITH (
SP nvarchar(max) '$.S[0].SP' AS JSON ) S1
CROSS APPLY OPENJSON (S1.SP)
WITH (
TP nvarchar(max) '$.TP' AS JSON ) S2
CROSS APPLY OPENJSON (S2.TP)
WITH (
TID nvarchar(500) '$.TID',
TName nvarchar(500) '$.TName' ) S3
Demo