JSON_VALUE SQL Server function not returning all values - sql

I have this nvarchar field with a JSON like this:
{"BOARD":"QC_Reference_Phone","SERIAL":"LGM700c2eee454","VERSION.INCREMENTAL":"1901817521542","CPU_ABI2":"armeabi","HOST":"CLD-BLD3-VM1-16","TIME":"1547801577000","MODEL":"LG-M700","MANUFACTURER":"LGE","USER":"jenkins","CPU_ABI":"armeabi-v7a","BRAND":"lge","DISPLAY":"OPM1.171019.026","FINGERPRINT":"lge/mh_global_com/mh:8.1.0/OPM1.171019.026/1901817521542:user/release-keys","HARDWARE":"mh","PRODUCT":"mh_global_com","BOOTLOADER":"unknown","VERSION.RELEASE":"8.1.0","ID":"OPM1.171019.026","UNKNOWN":"unknown","TYPE":"user","VERSION.SDK.NUMBER":"27","TAGS":"release-keys"}
And so my syntax is:
select JSON_VALUE(DeviceHardwareData,'$.VERSION.SDK.NUMBER') SDKVersion_nbr
FROM MyTable
It will work with all the other values within the JSON field but for "VERSION.SDK.NUMBER".
It returns a NULL Result for every row in my table.
I can actually get the value with the OPENJSON function, but I would like to know why it's specifically not returning the value for that attribute using JSON_Value

It doesn't work as you expect because in JSON Path syntax, the full stop character means "going one level down to a nested element under the following name". In order to extract the value with your path expression, your JSON structure should resemble the following:
"VERSION": {
"SDK": {
"NUMBER": 14
}
}
However, enclosing the element name in doublequotes in the path expression apparently does the trick:
declare #j nvarchar(max) = N'{
"VERSION.SDK.NUMBER": "27",
"VERSION": {
"SDK": {
"NUMBER": 14
}
}
}';
select json_value(#j, '$."VERSION.SDK.NUMBER"') as [TopValue],
json_value(#j, '$.VERSION.SDK.NUMBER') as [NestedValue];

Related

How to extract a field from an array of JSON objects in AWS Athena?

I have the following JSON data structure in a column in AWS Athena:
[
{
"event_type": "application_state_transition",
"data": {
"event_id": "-3368023833341021830"
}
},
{
"event_type": "application_state_transition",
"data": {
"event_id": "5692882176024811076"
}
}
]
I would like to somehow extract the values of event_id field, e.g. in the form of a list:
["-3368023833341021830", "5692882176024811076"]
(Though I don't insist on exactly this as long as I can get my event IDs.)
I wanted to use the JSON_EXTRACT function and thought it uses the very same syntax as jq. In jq, I can easily get what I want using the following query syntax:
.[].data.event_id
However, in AWS Athena this results in an error, as apparently the syntax is not entirely compatible with jq. Is there an alternative way to achieve the result I want?
JSON_EXTRACT supports quite limited set of json paths. Depending on Athena engine version you can either process column by casting it to array of maps and processing this array via array functions:
-- sample data
with dataset(json_col) as (
values ('[
{
"event_type": "application_state_transition",
"data": {
"event_id": "-3368023833341021830"
}
},
{
"event_type": "application_state_transition",
"data": {
"event_id": "5692882176024811076"
}
}
]')
)
-- query
select transform(
cast(json_parse(json_col) as array(map(varchar, json))),
m -> json_extract(m['data'], '$.event_id'))
from dataset;
Output:
_col0
["-3368023833341021830", "5692882176024811076"]
Or for 3rd Athena engine version you can try using Trino's json_query:
-- query
select JSON_QUERY(json_col, 'lax $[*].data.event_id' WITH ARRAY WRAPPER)
from dataset;
Note that return type of two will differ - in first case you will have array(json) and in the second one - just varchar.

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

Oracle SQL JSON_QUERY ignore key field

I have a json with several keys being a number instead of a fixed string. Is there any way I could bypass them in order to access the nested values?
{
"55568509":{
"registers":{
"001":{
"isPlausible":false,
"deviceNumber":"55501223",
"register":"001",
"readingValue":"5295",
"readingDate":"2021-02-25T00:00:00.000Z"
}
}
}
}
My expected output here would be 5295, but since 59668509 can vary from json to json, JSON_QUERY(data, '$."59668509".registers."001".readingValue) would not be an option. I'm not able to use regexp here because this is only a part of the original json, which contains more than this.
UPDATE: full json with multiple occurrences:
This is how my whole json looks like. I would like all the readingValue in brackets, in the example below, my expected output would be [32641, 00964].
WITH test_table ( data ) AS (
SELECT
'{
"session":{
"sessionStartDate":"2021-02-26T12:03:34+0000",
"interactionDate":"2021-02-26T12:04:19+0000",
"sapGuid":"369F01DFXXXXXXXXXX8553F40CE282B3",
"agentId":"USER001",
"channel":"XXX",
"bpNumber":"5551231234",
"contractAccountNumber":"55512312345",
"contactDirection":"",
"contactMethod":"Z08",
"interactionId":"5550848784",
"isResponsibleForPayingBill":"Yes"
},
"payload":{
"agentId":"USER001",
"contractAccountNumber":"55512312345",
"error":{
"55549271":{
"registers":{
"001":{
"isPlausible":false,
"deviceNumber":"55501223",
"register":"001",
"readingValue":"32641",
"readingDate":"2021-02-26T00:00:00.000Z"
}
},
"errors":[
{
"contractNumber":"55501231",
"language":"EN",
"errorCode":"62",
"errorText":"Error Text1",
"isHardError":false
},
{
"contractNumber":"55501232",
"language":"EN",
"errorCode":"62",
"errorText":"Error Text2",
"isHardError":false
}
],
"bpNumber":"5557273667"
},
"55583693":{
"registers":{
"001":{
"isPlausible":false,
"deviceNumber":"555121212",
"register":"001",
"readingValue":"00964",
"readingDate":"2021-02-26T00:00:00.000Z"
}
},
"errors":[
],
"bpNumber":"555123123"
}
}
}
}'
FROM
dual
)
SELECT
JSON_QUERY(data, '$.payload.error.*.registers.*[*].readingValue') AS reading_value
FROM
test_table;
UPDATE 2:
Solved, this would do the trick, upvoting the first comment.
JSON_QUERY(data, '$.payload.error.*.registers.*.readingValue' WITH WRAPPER) AS read_value
As I explained in the comment to your question, if you are getting that result from the JSON you posted, you are not using JSON_QUERY(); you must be using JSON_VALUE(). Either that, or there's something else you didn't share with us.
In any case, let's say you are using JSON_VALUE() with the arguments you showed. You are asking, how can you modify the path so that the top-level attribute name is not hard-coded. That is trivial: use asterisk (*) instead of the hard-coded name. (This would work the same with JSON_QUERY() - it's about JSON paths, not the specific function that uses them.)
with test_table (data) as (
select
'{
"59668509":{
"registers":{
"001":{
"isPlausible":false,
"deviceNumber":"40157471",
"register":"001",
"readingValue":"5295",
"readingDate":"2021-02-25T00:00:00.000Z"
}
}
}
}' from dual
)
select json_value (data, '$.*."registers"."001"."readingValue"'
returning number) as reading_value
from test_table
;
READING_VALUE
-------------
5295
As an aside that is not related to your question in any way: In your JSON you have an object with a single attribute named "registers", whose value is another object with a single attribute "001", and in turn, this object has an attribute named "register" with value "001". Does that make sense to you? It doesn't to me.

How to update multiple nested keys in a json field in single update query?

I'm trying to write a function that updates a json (not jsonb) field (called settings) in a table (called user).
My json object looks like this (however it might not have some of the keys on any nesting level):
{
"audiences": ["val1", "val2"],
"inviteNumber": "123",
"workExperience": [
{
"company": {
"name": "Ace",
"industry": "Accounting",
"revenues": "1M-10M",
"size": "1-10"
},
"title": "Product",
"startDate": {
"month": 1,
"year": 2018
}
}
],
"notifications": {
"emailNotifications": true
},
"jobFunction": "Research & Development",
"phoneNumber": "2134447777",
"areasOfInterest": {
"Recruiting and Staffing": true
}
}
I need to be able to update the "title" and "name" fields of the 0th element inside "workExperience" array.
What I currently have is this:
create or replace function my_function()
returns trigger language plpgsql as $$
declare
companyName character varying(255);
begin
select company.name into companyName from company where id = new.companyid;
update "user" set
email = new.email,
"settings" = jsonb_set(
"settings"::jsonb,
'{workExperience,0}'::TEXT[],
format(
'{"company": {"name": %s}, "title": %s}',
'"' || companyName || '"', '"' || new.title || '"'
)::jsonb
)
where id = new.userid;
return new;
end $$;
However the above implementation rewrites the while workExperience object removing the keys other than company and title.
I've tried looking through this answer on SO, but still wasn't able to implement the updating correctly.
I see that the problem is with how the jsonb_set works and it does just what I tell it to do: set 0th element of "workExperience" array to the object I define inside format function.
Seems like I need to use multiple jsonb_set one inside another, but I can't figure out the way to do it correctly.
How can I update my json field correctly without removing other keys from the object?
jsonb_set() returns the modified JSON value.
You could nest the calls and change the company name first, and use the result of that as the input to another jsonb_set().
"settings" = jsonb_set(jsonb_set("settings"::jsonb, '{workExperience,0,company,name}', to_jsonb(companyname)),
'{workExperience,0,title}', to_jsonb(new.title)
)

Is it possible to use wildcards as an argument for OPENJSON in SQL Server?

I have a nested JSON array consisting of outer keys that are numbers, each of which contain inner arrays that I need to import into a table in SQL Server. The JSON file is setup like so:
{
"121212": {
"name": name of item,
"subject": item subject
},
"343434": {
"name": name of item,
"subject": item subject
}
}
I can use the SQL Server function OPENJSON() to import a single array without issue like so:
DECLARE #arrayVariable VARCHAR(MAX)
SELECT #arrayVariable = BulkColumn FROM OPENROWSET(BULK 'array.json', SINGLE_BLOB) JSON
INSERT INTO ArrayTable (arrayName, arraySubject)
SELECT * FROM OPENJSON(#arrayVariable, '$."121212"')
WITH (
arrayName VARCHAR(MAX) '$.name',
arraySubject VARCHAR(MAX) '$.subject'
)
The above code successfully imports array 121212 into the ArrayTable. However, I would like to know if there is a solution that can utilize wildcards as an argument for OPENJSON in order to import in all numeric array keys from the JSON array, that way they don't have to be imported individually. I have tried using wildcards but none of the formatting options I've tried have worked so far. For example:
OPENJSON(#arrayVariable, '$."[0-9]%"')
What would be the best way to import all of the numerically titled JSON arrays using OPENJSON()?
Try this
DECLARE #arrayVariable VARCHAR(MAX) = N'{
"121212": {
"name": "name of item1",
"subject": "item subject1"
},
"343434": {
"name": "name of item2",
"subject": "item subject2"
}
}'
SELECT v.arrayName, v.arraySubject
FROM OPENJSON(#arrayVariable) AS r
CROSS APPLY OPENJSON(r.value)
WITH (
arrayName VARCHAR(MAX) '$.name',
arraySubject VARCHAR(MAX) '$.subject'
) AS v
WHERE r.[key] LIKE '[0-9]%'