MSSQL Query JSON displays Null value - sql

I have a table PublicRelations with a column called Students in a SQL Server database called Subjects.
[
{ "Label": "Name", "ColumnValue": "Trudie" },
{ "Label": "Class", "ColumnValue": "PublicRelations" },
{ "Label": "Room", "ColumnValue": "8049" },
{ "Label": "HttpPath", "ColumnValue": "https://www.google.com/" }
]
I only get NULL when I run the below query using the Json_value. I'd like to get it to display the value from the array. I believe this may have to do with the 4000 character limit?
SELECT [StuduentID],
[Students],
--JSON_VALUE([Students],'$.ColumnValue') AS Name --Only returns NULL
FROM [Subjects].[dbo].[PublicRelations] c
CROSS APPLY OPENJSON(c.Students)
WITH ( Name int '$.Name',
Value nvarchar(255) '$.ColmunValue'
) AS jsonValues
WHERE jsonValues.ColumnValue = 'Trudie'
The query works and I can find what I need, but again, I only get NULL when I want to display that part of the JSON column in my results.

The statement is wrong and you has the following issues (as #MartinSmith already mentioned):
Syntax error - '$.ColmunValue' should be '$.ColumnValue'.
Wrong schema definition (the WITH clause) - I can't see Name key in the input JSON.
Wrong use of JSON_VALUE() - this function extracts scalar value from a JSON string, so JSON_VALUE([Students],'$.ColumnValue') returns NULL with this JSON input in lax mode.
You may try with the following statement (based on the statement in the question):
Table:
CREATE TABLE PublicRelations (
StudentID int,
Students nvarchar(1000))
INSERT INTO PublicRelations (StudentID, Students)
VALUES (1, N'[
{ "Label": "Name", "ColumnValue": "Trudie" },
{ "Label": "Class", "ColumnValue": "PublicRelations" },
{ "Label": "Room", "ColumnValue": "8049" },
{ "Label": "HttpPath", "ColumnValue": "https://www.google.com/" }
]')
Statement:
SELECT p.StudentID, j.*
FROM [PublicRelations] p
CROSS APPLY OPENJSON(p.Students) WITH (
Name nvarchar(50) '$.Label',
Value nvarchar(255) '$.ColumnValue'
) j
WHERE EXISTS (
SELECT 1
FROM OPENJSON(p.Students) WITH (Value nvarchar(255) '$.ColumnValue')
WHERE Value = N'Trudie'
) AND (j.Name IN ('Name', 'Class', 'Room'))
Result:
StudentID Name Value
1 Name Trudie
1 Class PublicRelations
1 Room 8049

Related

Extract object and scalar value via single function

I have to write a query in SQL to extract a JSON path's value. Now the issue is that path's ultimate value can either be a JSON object or scalar value. But in SQL Server, to get the object and scalar value JSON_QUERY and JSON_VALUE functions should be used respectively. My issue is that I want to write the same query which returns both types of values. Is there any way in which this can be achieved?
You can use OPENJSON function to get the values for a given path $.foo.bar like so:
CREATE TABLE t(
id INT IDENTITY NOT NULL PRIMARY KEY,
json VARCHAR(MAX)
);
INSERT INTO t(json) VALUES
('{ "foo": { "bar": 123 } }'),
('{ "foo": { "bar": "x" } }'),
('{ "foo": { "bar": [1] } }'),
('{ "foo": { "bar": { "baz": 1 } } }');
SELECT *
FROM t
CROSS APPLY OPENJSON(json, '$.foo')
WHERE [key] = 'bar';
The result will contain these important columns:
key: name of the key below $.foo which could be used inside WHERE clause
value: the value for the key having datatype = NVARCHAR(MAX)
type: the type of the value having datatype = INT (see documentation)
Demo on db<>fiddle

Joining tables with filter condition on multiple columns in Oracle DBMS

{
"description": "test",
"id": "1",
"name": "test",
"prod": [
{
"id": "1",
"name": "name",
"re": [
{
"name": "name1",
"value": "1"
},
{
"name": "name2",
"value": "1"
},
{
"name": "name3",
"value": "0"
},
{
"name": "name4",
"value": "0"
}
]
}
]
}
Here is the best I can do with your JSON input and your sample output.
Note that your document has a unique "id" and "name" ("1" and "test" in your example). Then it has an array named "productSpecificationRelationship". Each element of this array is an object with its own "id" - in the query, I show this id with the column name PSR_ID (PSR for Product Specification Relationship). Also, each object in this first-level array contains a sub-array (second level), with objects with "name" ("name" again!) and "value" keys. (This looks very much like an entity-attribute-value model - very poor practice.) In the intermediate step in my query (before pivoting), I call these RC_NAME and RC_VALUE (RC for Relationship Characteristic).
In your sample output you have more than one value in the ID and NAME columns. I don't see how that is possible; perhaps from unpacking more than one document? The JSON document you shared with us has "id" and "name" as top-level attributes.
In the output, I understand (or rather, assume, since I didn't understand too much from your question) that you should also include the PSR_ID - there is only one in your document, with value "10499", but in principle there may be more than one, and the output will have one row per such id.
Also, I assume the "name" values are limited to the four you mentioned (or, if there can be more, you are only interested in those four in the output).
With all that said, here is the query. Note that I called the table ES for simplicity. Also, you will see that I had to go to nested path twice (since your document includes an array of arrays, and I wanted to pick up the PSR_ID from the outer array and the tokens from the nested arrays).
TABLE SETUP
create table es (payloadentityspecification clob
check (payloadentityspecification is json) );
insert into es (payloadentityspecification) values (
'{
"description": "test",
"id": "1",
"name": "test",
"productSpecificationRelationship": [
{
"id": "10499",
"relationshipType": "channelRelation",
"relationshipCharacteristic": [
{
"name": "out_of_home",
"value": "1"
},
{
"name": "out_of_home_ios",
"value": "1"
},
{
"name": "out_of_home_android",
"value": "0"
},
{
"name": "out_of_home_web",
"value": "0"
}
]
}
]
}');
commit;
QUERY
with
prep (id, name, psr_id, rc_name, rc_value) as (
select id, name, psr_id, rc_name, rc_value
from es,
json_table(payloadentityspecification, '$'
columns (
id varchar2(10) path '$.id',
name varchar2(40) path '$.name',
nested path '$.productSpecificationRelationship[*]'
columns (
psr_id varchar2(10) path '$.id',
nested path '$.relationshipCharacteristic[*]'
columns (
rc_name varchar2(50) path '$.name',
rc_value varchar2(50) path '$.value'
)
)
)
)
)
select id, name, psr_id, ooh, ooh_android, ooh_ios, ooh_web
from prep
pivot ( min(case rc_value when '1' then 'TRUE'
when '0' then 'FALSE' else 'UNDEFINED' end)
for rc_name in ( 'out_of_home' as ooh,
'out_of_home_android' as ooh_android,
'out_of_home_ios' as ooh_ios,
'out_of_home_web' as ooh_web
)
)
;
OUTPUT
ID NAME PSR_ID OOH OOH_ANDROID OOH_IOS OOH_WEB
-- ---- ------ ----------- ----------- ----------- -----------
1 test 10499 TRUE FALSE TRUE FALSE
Conditional aggregation might be used in order to pivot the result set after extracting the values by using JSON_TABLE() and JSON_VALUE() functions such as
SELECT JSON_VALUE(payloadentityspecification, '$.name') AS channel_map_name,
MAX(CASE WHEN name = 'out_of_home' THEN
DECODE(value,1,'TRUE',0,'FALSE','UNDEFINED')
END) AS ooh,
MAX(CASE WHEN name = 'out_of_home_android' THEN
DECODE(value,1,'TRUE',0,'FALSE','UNDEFINED')
END) AS ooh_android,
MAX(CASE WHEN name = 'out_of_home_ios' THEN
DECODE(value,1,'TRUE',0,'FALSE','UNDEFINED')
END) AS ooh_ios,
MAX(CASE WHEN name = 'out_of_home_web' THEN
DECODE(value,1,'TRUE',0,'FALSE','UNDEFINED')
END) AS ooh_web
FROM EntitySpecification ES,
JSON_TABLE (payloadentityspecification, '$.productSpecificationRelationship[*]'
COLUMNS ( NESTED PATH '$.relationshipCharacteristic[*]'
COLUMNS (
description VARCHAR2(250) PATH '$.description',
name VARCHAR2(250) PATH '$.name',
value VARCHAR2(250) PATH '$.value'
)
)) jt
WHERE payloadentityspecification IS JSON
GROUP BY JSON_VALUE(payloadentityspecification, '$.name')
Demo

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

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

Parse JSON structure in SQL

I have written a code that reads information from JSON lines to table in SQL:
declare #pJSON varchar(max) = '
{
"School": "MiddleSchool",
"Password": "SchoolPassword",
"Attributes": [
{
"Type": "Exam",
"Value": "1"
},
{
"Type": "Class",
"Value": "11b"
},
{
"Type": "Math",
"Value": [
{
"ExamDate": "2019-01-01",
"Points": 100,
"Grade": 10,
"Notes": "Good"
}
]
}
]
} '
select ExamDate, Points, Grade, Notes
from OPENJSON(#pJSON, N'$.Attributes[2].Value')
cross apply openjson ([Value])
with
(
ExamDate date,
Points int,
Grade int,
Notes varchar(max)
) as [value]
Code works fine, but I really hate N'$.Attributes[2].Value' part. Exam information can be in the first, second, third place, so [2] doesn't really work for me. Do you have any suggestions for me, how I can improve this code? Thank you!
You could use JSON_QUERY:
select ExamDate, Points, Grade, Notes
from OPENJSON(JSON_QUERY(#pJSON, N'$.Attributes'))
with
(
ExamDate date N'$.Value[0].ExamDate', -- here 0 because Value is array too
Points int N'$.Value[0].Points',
Grade int N'$.Value[0].Grade',
Notes varchar(max) N'$.Value[0].Notes'
) as [value]
WHERE ExamDate IS NOT NULL;
db<>fiddle demo
EDIT:
In original question there was only one exam in array. If array could contain more than code should be adjusted:
SELECT s2.[key]
,ExamValue = JSON_VALUE(s2.value, '$.ExamDate')
,Points = JSON_VALUE(s2.value, '$.Points')
,Grade = JSON_VALUE(s2.value, '$.Grade')
,Notes = JSON_VALUE(s2.value, '$.Notes')
FROM OPENJSON(JSON_QUERY(#pJSON, N'$.Attributes')) s
CROSS APPLY OPENJSON(JSON_QUERY(s.value, N'$.Value')) s2;
-- or
SELECT [value].*
FROM OPENJSON(JSON_QUERY(#pJSON, N'$.Attributes'))
CROSS APPLY OPENJSON(JSON_QUERY(value, N'$.Value'))
with
(
ExamDate date N'$.ExamDate',
Points int N'$.Points',
Grade int N'$.Grade',
Notes varchar(max) N'$.Notes'
) as [value];
db<>fiddle demo

Bigquery: Append to a nested record

I'm currently checking out Bigquery, and I want to know if it's possible to add new data to a nested table.
For example, if I have a table like this:
[
{
"name": "name",
"type": "STRING"
},
{
"name": "phone",
"type": "RECORD",
"mode": "REPEATED",
"fields": [
{
"name": "number",
"type": "STRING"
},
{
"name": "type",
"type": "STRING"
}
]
}
]
And then I insert a phone number for the contact John Doe.
INSERT into socialdata.phones_examples (name, phone) VALUES("Jonh Doe", [("555555", "Home")]);
Is there an option to later add another number to the contact ? To get something like this:
I know I can update the whole field, but I want to know if there is way to append to the nested table new values.
When you insert data into BigQuery, the granularity is the level of rows, not elements of the arrays contained within rows. You would want to use a query like this, where you update the relevant row and append to the array:
UPDATE socialdata.phones_examples
SET phone = ARRAY_CONCAT(phone, [("555555", "Home")])
WHERE name = "Jonh Doe"
if you need to update multiple records for some users - you can use below
#standardSQL
UPDATE `socialdata.phones_examples` t
SET phone = ARRAY_CONCAT(phone, [new_phone])
FROM (
SELECT 'John Doe' name, STRUCT<number STRING, type STRING>('123-456-7892', 'work') new_phone UNION ALL
SELECT 'Abc Xyz' , STRUCT('123-456-7893', 'work') new_phone
) u
WHERE t.name = u.name
or if those updates are available in some table (for example socialdata.phones_updates):
#standardSQL
UPDATE `socialdata.phones_examples` t
SET phone = ARRAY_CONCAT(phone, [new_phone])
FROM `socialdata.phones_updates` u
WHERE t.name = u.name