Assume the following SQL Server table structure
dbo.users
(
id int not null,
favoriteBook int null --fk
)
dbo.books
(
id int not null,
title nvarchar(max)
)
Assuming a user exists that has not entered in their favorite book I'd like to use Hasura's GraphQL engine to search for all users and return the title of their favorite book if one exists, or null if it doesn't.
I'd expect something like this to work:
query Users{
users {
id
FavoriteBook {
title
}
}
}
However when using this query the engine returns the following error:
{
"errors": [
{
"extensions": {
"internal": {
"tag": "unsuccessful_return_code",
"contents": [
"odbc_SQLExecDirectW",
-1,
"[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]JSON text is not properly formatted. Unexpected character '.' is found at position 4.[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]JSON text is not properly formatted. Unexpected character '.' is found at position 4."
]
},
"path": "$",
"code": "unexpected"
},
"message": "sql server exception"
}
]
}
The SQL generated by Hasura looks something like this:
select
isnull (
(
select
[t_Users1].[id] as [id]
,json_query ( [or_books1].[json] ) as [FavoriteBook]
from [dbo].[users] as [t_Users1]
outer apply
(
select
isnull ((
select
[t_Books1].[id] as [id]
from [dbo].[books] as [t_Books1]
where
(([t_Books1].[id]) = ([t_Users1].[favoriteBook]))
for json path, include_null_values, without_array_wrapper
)
,'null'
)
) as [or_Booksr1]([json])
order by
(
select null
) /* ORDER BY is required for OFFSET */ offset 0 rows fetch next 1000 rows only
for json path, include_null_values
)
,'[]'
)
;
Ideally the output would look something like this:
{
"data": {
"users": [
{
"id": 254,
"favoriteBook": {
"id": 1,
"title": "book about foo"
}
},
{
"id": 892,
"favoriteBook": null
}
]
}
}
Related
UPDATE tab s
SET s.DATA = json_transform(DATA, REPLACE '$.dataFilterDefn.columns[1]' = (SELECT JSON_ARRAYAGG(JSON FORMAT JSON RETURNING CLOB)
FROM
(SELECT JSON
FROM
(SELECT j.json || ',' || JSON AS json
FROM tab d
CROSS APPLY JSON_TABLE(d.tab, '$.dataFilterDefn.columns[1]' COLUMNS(json CLOB FORMAT JSON PATH '$')) j
WHERE d.UID = 252))));
The data look like this:
"columns":[
{
"label":"Subcategory",
"aggFn":"NONE",
"datasetId":"ADVENTURE_WORKS",
"fieldName":"SUB_CAT",
"id":"FILTER-1"
}
]
My expectation:
"columns":[
{
"label":"Subcategory",
"aggFn":"NONE",
"datasetId":"ADVENTURE_WORKS",
"fieldName":"SUB_CAT",
"id":"FILTER-1"
},
{
"label":"Subcategory",
"aggFn":"NONE",
"datasetId":"ADVENTURE_WORKS",
"fieldName":"SUB_CAT",
"id":"FILTER-1"
}
]
I want the columns data should duplicate.The columns value will be different.It"ll not be the same. I need to update the column values dynamically..It should not be hardcoded. How can I achieve this in json using Oracle version 19c?
Using json_transform you can append items to an array. Using json_query you can extract the first element of the array.
So you can do something like this:
with rws as (
select '{ "columns":[
{
"label":"Subcategory",
"aggFn":"NONE",
"datasetId":"ADVENTURE_WORKS",
"fieldName":"SUB_CAT",
"id":"ART-DATA-FILTER-1"
}
] }' jdata
from dual
)
select json_transform (
jdata,
append '$.columns' = json_query ( jdata, '$.columns[0]' )
returning varchar2 pretty
)
from rws r;
{
"columns" :
[
{
"label" : "Subcategory",
"aggFn" : "NONE",
"datasetId" : "ADVENTURE_WORKS",
"fieldName" : "SUB_CAT",
"id" : "ART-DATA-FILTER-1"
},
{
"label" : "Subcategory",
"aggFn" : "NONE",
"datasetId" : "ADVENTURE_WORKS",
"fieldName" : "SUB_CAT",
"id" : "ART-DATA-FILTER-1"
}
]
}
Note that json_transform was only added in a recent release update, so ensure your 19c version is fully patched and up-to-date.
i have below tables
table1:
Payload(column)
{
"list": "212=1.00,214"
}
table 2 looks like below
i want result like below using UDF instead of using flatten
{
"test13": {
"code": "212",
"desc": "success",
"value": "1.00"
},
"test15": {
"code": "214",
"desc": "Impression",
"value": ""
}
}
You ought to be able to do JavaScript UDTFs (User-Defined Table Functions) https://docs.snowflake.com/en/sql-reference/udf-js-table-functions.html that can take the single row payload and return multiple rows.
So the SQL to do this, I understand you don't want:
with table1 AS (
select parse_json('{"list": "212=1.00,214"}') as payload
), table2 AS (
select parse_json(column1) as payload
,column2 as key
,column3 as value
from values ('{"id":"212"}', 'test13', 'success' ),
('{"id":"214"}', 'test15', 'impression' )
), table1_demunged AS (
select split(f.value,'=')[0] as id
,split(f.value,'=')[1] as val
from table1 t, lateral flatten(input=>split(t.payload:list,',')) f
), tables_joined as (
select t2.key as obj_key
,t1.id as code
,t2.value as desc
,t1.val as value
from table2 t2
join table1_demunged t1 on t2.payload:id = t1.id
), as_objects AS (
select obj_key, object_construct('code', code, 'desc', desc, 'value', coalesce(value,'')) as res
from tables_joined t
)
select object_agg(obj_key, res) object
from as_objects
group by true;
gives the result you do want:
OBJECT
{ "test13": { "code": "212", "desc": "success", "value": "1.00" }, "test15": { "code": "214", "desc": "impression", "value": "" } }
But I do not understand if your are really want a UDF to do all that, given it's a FLATTEN then a JOIN and then some OBJECT_ functions, or if you are just want to avoid the FALTTEN as it "tricky SQL and you want to find it behind a UDF" or perhaps your using some system that cannot parse the => thus you need the flatten hidden behind a UDF, but in that case the UDF cannot do all the joins for you..
It just feels like there is more to the question than has been asked.
I am now importing JSON data into a SQL Server table where the JSON data is stored as a single column entry.
I now have a more complex JSON structure and I am having difficulty with the path
in the OPENJSON statement
I tried creating table results modifying an existing OPENJSON that was pointed out to me.
However this path I can not get the data to appear (the command does complete successfully)
What is incorrect in my path that I am not retrieving the data for **timestamp and value?**
SELECT t.[DATE], j.*
FROM DTReport.Json_synthetic_response_time t
CROSS APPLY OPENJSON(t.log, '$.metrics."builtin:synthetic.browser.visuallyComplete.load".series[2]') WITH (
[Timestamp] bigint '$[0]',
[Value] float '$[1]'
) j
Here is the JSON file (with several data points removed for brevity)
{
"totalCount":1,
"nextPageKey":null,
"metrics":{
"builtin:synthetic.browser.visuallyComplete.load":{
"series":[
{
"dimensions":[
"SYNTHETIC_TEST-434A9DE59A6CAFD6"
],
"values":[
{
"timestamp":1571691600000,
"value":2978.5833333333335
},
{
"timestamp":1571702400000,
"value":3129.6666666666665
},
{
"timestamp":1571713200000,
"value":3040.6666666666665
},
{
"timestamp":1571724000000,
"value":3132.1666666666665
},
{
"timestamp":1572901200000,
"value":2727.2727272727275
}
]
}
]
}
}
}
You need to parse the series JSON array (which in your case has only one item) and then values JSON array with additional APPLY operator and OPENJSON() call.
JSON:
DECLARE #json nvarchar(max) = N'{
"totalCount":1,
"nextPageKey":null,
"metrics":{
"builtin:synthetic.browser.visuallyComplete.load":{
"series":[
{
"dimensions":[
"SYNTHETIC_TEST-434A9DE59A6CAFD6"
],
"values":[
{
"timestamp":1571691600000,
"value":2978.5833333333335
},
{
"timestamp":1571702400000,
"value":3129.6666666666665
},
{
"timestamp":1571713200000,
"value":3040.6666666666665
},
{
"timestamp":1571724000000,
"value":3132.1666666666665
},
{
"timestamp":1572901200000,
"value":2727.2727272727275
}
]
}
]
}
}
}'
Table:
CREATE TABLE JSON_TEST (DATE date, [log] nvarchar(max))
INSERT INTO JSON_TEST (DATE, [log])
VALUES (GETDATE(), #json)
Statement:
SELECT d.[DATE], j.*
FROM JSON_TEST d
CROSS APPLY OPENJSON(d.log, '$.metrics."builtin:synthetic.browser.visuallyComplete.load".series[0].values') WITH (
[Timestamp] bigint '$.timestamp',
[Value] float '$.value'
) j
Result:
DATE Timestamp Value
05/11/2019 00:00:00 1571691600000 2978.58333333333
05/11/2019 00:00:00 1571702400000 3129.66666666667
05/11/2019 00:00:00 1571713200000 3040.66666666667
05/11/2019 00:00:00 1571724000000 3132.16666666667
05/11/2019 00:00:00 1572901200000 2727.27272727273
Notes:
If the series JSON array has more than one item, you'll need to parse it with additional OPENJSON() call.
The column_path in your with clause needs to be a valid JSON Path Expression. I think the following gets you what you asked for with a change from series[2] to series[0].
DECLARE #json NVARCHAR(MAX) = '{
"totalCount":1,
"nextPageKey":null,
"metrics":{
"builtin:synthetic.browser.visuallyComplete.load":{
"series":[
{
"dimensions":[
"SYNTHETIC_TEST-434A9DE59A6CAFD6"
],
"values":[
{
"timestamp":1571691600000,
"value":2978.5833333333335
},
{
"timestamp":1571702400000,
"value":3129.6666666666665
},
{
"timestamp":1571713200000,
"value":3040.6666666666665
},
{
"timestamp":1571724000000,
"value":3132.1666666666665
},
{
"timestamp":1572901200000,
"value":2727.2727272727275
}
]
}
]
}
}
}'
SELECT *
FROM OPENJSON(#json, '$.metrics."builtin:synthetic.browser.visuallyComplete.load".series[0].values')
WITH (
[Timestamp] bigint '$.timestamp',
[Value] float '$.value'
) j
Another option you may find useful is to break up the JSON structure into multiple pieces so you don't have to reference a specific index in the series array:
SELECT v.*
FROM OPENJSON(#json, '$.metrics."builtin:synthetic.browser.visuallyComplete.load"') WITH (Series NVARCHAR(MAX) '$.series' AS JSON) j
CROSS APPLY OPENJSON (j.Series) WITH ([Values] NVARCHAR(MAX) '$.values' AS JSON) s
CROSS APPLY OPENJSON (s.[Values]) WITH ([Timestamp] bigint '$.timestamp', [Value] float '$.value') v
I have JSON values like this stored in a table:
{
"properties":[
{
"address":{
"value":"A3",
"name":"",
"prop":"",
"path":[
"RealOptionsList9293"
],
"type":"local"
},
"value":{
"type":11,
"value":"portland"
},
"dependents":[
],
"save":true
}
]
}
I'd like to index on address.value and value.value so that I can query on them. MSDN examples are for basic property using computed column. It does not cover indexing an array. Is indexing possible on array? Any example will be helpful.
I'd like to query for rows with:
JSON_VALUE(mycolumn, '$.properties[*].address.value') = 'A3'
AND JSON_VALUE(mycolumn, $.properties[*].value.value) = 'portland'
I don't see [*] syntax. Should I use OPENJSON() instead? If I use it, should I use a materialized view?
If you want to query a JSON array, OPENJSON() is more appropriate:
Table:
CREATE TABLE #Data (
JsonData nvarchar(max)
)
INSERT INTo #Data (JsonData)
VALUES (N'{
"properties":[
{
"address":{
"value":"A3",
"name":"",
"prop":"",
"path":[
"RealOptionsList9293"
],
"type":"local"
},
"value":{
"type":11,
"value":"portland"
},
"dependents":[
],
"save":true
},
{
"address":{
"value":"A4",
"name":"",
"prop":"",
"path":[
"RealOptionsList9293"
],
"type":"local"
},
"value":{
"type":11,
"value":"portland"
},
"dependents":[
],
"save":true
}
]
}')
Statement:
SELECT d.*
FROM #Data d
CROSS APPLY OPENJSON(d.JsonData, '$.properties') WITH (
AddressValue nvarchar(1000) '$.address.value',
ValueValue nvarchar(1000) '$.value.value'
) j
WHERE j.AddressValue = 'A3' AND ValueValue = 'portland'
When you want to query JSON arrays or objects you should use JSON_QUERY which is designed to work with that. JSON_VALUE is designed to return a scalar, thus, if you use JSON_VALUE with address, value or even dependents (in your JSON), it'll always returns null. But if you use it with save, it'll return its value.
So, what you need to do is something like this :
SELECT
JSON_VALUE([Address],'$.value')
FROM (
SELECT
JSON_QUERY(#json,'$.properties[0].address') AS [Address]
, JSON_QUERY(#json,'$.properties[0].value') AS [Value]
, JSON_QUERY(#json,'$.properties[0].dependents') AS [dependents]
) arrays
{
"studentID": 1,
"StudentName": "jhon",
"Data":{
"schoolname":"school1",
"enrolmentInfo":
[{
"year":"2015",
"info":
[
{
"courseID":"csc213",
"school":"IT",
"enrollmentdate":"2015-01-01",
"finshdate":"2015-07-01",
"grade": 80 },
{
"courseID":"csc113",
"school":"IT1",
"enrollmentdate":"2015-09-02",
"finshdate":null,
"grade": 90 } ]
},
{
"year":"2014",
"info":
[{
"courseID":"info233",
"school":"IT",
"enrollmentdate":"2014-03-11",
"finshdate":"2014-09-01",
"grade": 81 },
{
"courseID":"csc783",
"school":"IT",
"enrollmentdate":"2014-01-02",
"finshdate":"2014-08-01",
"grade": 87 } ]
} ]
}
}
I have stored in postgresql database json objects of the above format. Each object consists of informations about a certain student with enrollment information. I have complex objects with nested array inside arrays. I am trying to select all the element inside the "info" array. I tried to use the following query:
with recursive x (info) as (select value->'info' from jsontesting r, json_array_elements(r.data->'Data'->'enrolmentinfo')
UNION ALL
SELECT (e).value->'courseID', (e).value->'school', (e).value->'grade',(e).value->'enrollmentdate', (e).value->'finshdate'
from (select json_each(json_array_elements (info)) e from x) p)
select * from x;
This query is not working and it is giving the following error:"cannot call json_array_elements on a scalar". Is there any other query that I can use to extract the elements of the nested array "info"??
-- assuming that jsontesting.data contains your JSON
WITH info_data AS (
SELECT enrolment_info->'info' AS info
FROM jsontesting t, json_array_elements(t.data -> 'Data' -> 'enrolmentInfo') AS enrolment_info
)
SELECT info_item->>'courseID',
info_item->>'school',
info_item->>'enrollmentdate',
info_item->>'finshdate',
info_item->>'grade'
FROM info_data idata, json_array_elements(idata.info) AS info_item;