Postgres 9.3 JSON Output multi-dimensional object - json

Given this query:-
SELECT id as id,
attributes->>'name' as file_name,
status
from workflow.events
where schema='customer'
and type='FILE_UPLOAD'
id,file_name, status
1,name,status
2,name2,status2
I want to output this structure:-
{
"1" :{"id" :"1", "file_name" : "name", "status" : "status1"},
"2" :{"id" :"2", "file_name" : "name2","status" : "status2"}
}
I can do it at the moment using string functions but this seems messy and inefficient. CAn it be done using the native postgresql json functions?

If you want to get two records with json, use row_to_json() function:
with cte as (
select
id as id,
attributes->>'name' as file_name,
status
from workflow.events
where schema='customer' and type='FILE_UPLOAD'
)
select row_to_json(c) from cte as c
output:
{"id":1,"file_name":"name","status":"status"}
{"id":2,"file_name":"name2","status":"status2"}
If you want to get json array:
with cte as (
select
id as id,
attributes->>'name' as file_name,
status
from workflow.events
where schema='customer' and type='FILE_UPLOAD'
)
select json_agg(c) from cte as c
output:
[{"id":1,"file_name":"name","status":"status"},
{"id":2,"file_name":"name2","status":"status2"}]
But for you desired output, I can only suggest string transformation:
with cte as (
select
id::text as id,
file_name,
status
from workflow.events
where schema='customer' and type='FILE_UPLOAD'
)
select ('{' || string_agg('"' || id || '":' || row_to_json(c), ',') || '}')::json from cte as c
sql fiddle demo

Related

Is there any way to join multiple table and show result of some table as objects [duplicate]

I'd like to convert result table to JSON array in MySQL using preferably only plain MySQL commands. For example with query
SELECT name, phone FROM person;
| name | phone |
| Jack | 12345 |
| John | 23455 |
the expected JSON output would be
[
{
"name": "Jack",
"phone": 12345
},
{
"name": "John",
"phone": 23455
}
]
Is there way to do that in plain MySQL?
EDIT:
There are some answers how to do this with e.g. MySQL and PHP, but I couldn't find pure MySQL solution.
New solution:
Built using Your great comments, thanks!
SELECT JSON_ARRAYAGG(JSON_OBJECT('name', name, 'phone', phone)) from Person;
Old solution:
With help from #Schwern I managed to put up this query, which seems to work!
SELECT CONCAT(
'[',
GROUP_CONCAT(JSON_OBJECT('name', name, 'phone', phone)),
']'
)
FROM person;
You can use json_object to get rows as JSON objects.
SELECT json_object('name', name, 'phone', phone)
FROM person;
This won't put them in an array, or put commas between them. You'll have to do that in the code which is fetching them.
If you're stuck on MySQL 5.6 like me, try this:
SELECT
CONCAT(
'[',
GROUP_CONCAT(
CONCAT(
'{"name":"', name, '"',
',"phone":"', phone, '"}'
)
),
']'
) as json
FROM person
There are two "group by" functions for JSON called json_arrayagg, json_objectagg.
This problem can be solved with:
SELECT json_arrayagg(
json_merge(
json_object('name', name),
json_object('phone', phone)
)
) FROM person;
This requires MySQL 5.7+.
If you need a nested JSON Array Object, you can join JSON_OBJECT with json_arrayagg as below:
{
"nome": "Moon",
"resumo": "This is a resume.",
"dt_inicial": "2018-09-01",
"v.dt_final": null,
"data": [
{
"unidade": "unit_1",
"id_unidade": 9310
},
{
"unidade": "unit_2",
"id_unidade": 11290
},
{
"unidade": "unit_3",
"id_unidade": 13544
},
{
"unidade": "unit_4",
"id_unidade": 13608
}
]
}
You can also do it like this:
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_lst_caso`(
IN `codigo` int,
IN `cod_base` int)
BEGIN
DECLARE json TEXT DEFAULT '';
SELECT JSON_OBJECT(
'nome', v.nome,
'dt_inicial', v.dt_inicial,
'v.dt_final', v.dt_final,
'resumo', v.resumo,
'data', ( select json_arrayagg(json_object(
'id_unidade',`tb_unidades`.`id_unidade`,
'unidade',`tb_unidades`.`unidade`))
from tb_caso_unidade
INNER JOIN tb_unidades ON tb_caso_unidade.cod_unidade = tb_unidades.id_unidade
WHERE tb_caso_unidade.cod_caso = codigo)
) INTO json
FROM v_caso AS v
WHERE v.codigo = codigo and v.cod_base = cod_base;
SELECT json;
END
For most situations, I use DataGreap, but for big tables, it is not work.
My GIST shell script

SQL Server - Generate Dynamic JSon output

I have the following tables (pseudo names below) in SQL which I need to use to produce some dynamic JSON out from :
HeaderTable
RequestID int
RequestType varchar(50)
Sample Data
1 : User Name Change
2 : User Name Change
ValuesTable
RequestID int
Alias varchar(50)
FieldValue varchar(50)
Sample Data
1 : MobileNo : 07777777777
1 : Name : Fred Bloggs
2 : MobileNo : 07888888888
2 : Name : John Smith
The JSON I need to end up with is as follows :
[
{
"request_type":"User Name Change",
"request_details":[
{
"MobileNo":"07777777777",
"Name":"Fred Bloggs"
},
{
"MobileNo":"07888888888",
"Name":"John Smith"
}
]
}
]
So I effectively need to pass my alias value as the key name in the JSON.
My code so far is as follows but I assume I might need some sort of dynamic SQL?
SELECT hdr.RequestType AS request_type
, (
SELECT vals.FieldValue AS [request_details.value]
FROM ValuesTable vals
WHERE vals.RequestID = hdr.[RequestID]
FOR JSON PATH
) request_details
FROM HeaderTable hdr
FOR JSON PATH
I'm not sure if any of the other SQL JSON function might be useful here or if I need to somehow churn out some dynamic SQL as my only hope?
I don't think you can build the required JSON directly (usinf FOR JSON), but you may try to build one part of the JSON output using basic string concatenation and aggregation. Note, that starting for SQL Server 2016, you need to use FOR XML PATH for aggregation:
Data:
SELECT *
INTO HeaderTable
FROM (VALUES
(1, 'User Name Change')
) v (RequestID, RequestType)
SELECT *
INTO ValuesTable
FROM (VALUES
(1, 'MobileNo', '07777777777'),
(1, 'Name', 'Fred Bloggs'),
(1, 'Address', 'Full address'),
(2, 'MobileNo', '07888888888'),
(2, 'Name', 'John Smith')
) v (RequestID, Alias, FieldValue)
Statement for SQL Server 2017:
SELECT
hdr.RequestType AS request_type,
JSON_QUERY((
SELECT CONCAT(
'[{',
STRING_AGG(
CONCAT(
'"',
STRING_ESCAPE(vals.Alias, 'json'),
'":"',
STRING_ESCAPE(vals.FieldValue, 'json'), '"'
),
','
),
'}]'
)
FROM ValuesTable vals
WHERE vals.RequestID = hdr.[RequestID]
)) AS request_details
FROM HeaderTable hdr
FOR JSON PATH
Statement for SQL Server 2016:
SELECT
hdr.RequestType AS request_type,
JSON_QUERY(CONCAT(
'[{',
STUFF(
(
SELECT CONCAT(',"', vals.Alias, '":"', vals.FieldValue, '"')
FROM ValuesTable vals
WHERE vals.RequestID = hdr.[RequestID]
FOR XML PATH(''), TYPE
).value('.', 'varchar(max)'),
1, 1, ''
),
'}]'
)) AS request_details
FROM HeaderTable hdr
FOR JSON PATH
Result:
[
{
"request_type":"User Name Change",
"request_details":[
{
"MobileNo":"07777777777",
"Name":"Fred Bloggs",
"Address":"Full address"
}
]
}
]

Escaped for JSON nested nodes using union command

In a stored procedure I have a for json node (boxes):
select
(
select
os.Name,
os.Address,
ss.carrierCode,
(
select
ob.envelopeCode,
ob.boxNumber,
ob.weight,
ob.width,
ob.length,
ob.height
from OrdersBoxes ob
...
where os.OID=ob.OID
...
for json path
) boxes,
....
for json path
) orderDetails
In this way I correctly get:
"boxes":[{
"envelopeCode":"E70345D2AB90A879D4F53506FB465086",
"boxNumber":1,
"weight":3000,
"width":300,
"length":300,
"height":100
}]
Now I need to get details from 2 tables, therefore I will use union command, wrap the 2 select in another select the query to avoid following error:
The FOR XML and FOR JSON clauses are invalid in views, inline functions, derived tables, and subqueries when they contain a set operator. To work around, wrap the SELECT containing a set operator using derived table or common table expression or view and apply FOR XML or FOR JSON on top of it.
And add JSON_QUERY to avoid to get escaped nested node:
select
(
select
*
from
(
select
os.Name,
os.Address,
ss.carrierCode,
JSON_QUERY((
select
ob.envelopeCode,
ob.boxNumber,
ob.weight,
ob.width,
ob.length,
ob.height
from OrdersBoxes ob
...
where os.OID=ob.OID
...
for json path
)) boxes,
....
from table1
where....
union
select
os.Name,
os.Address,
ss.carrierCode,
JSON_QUERY((
select
ob.envelopeCode,
ob.boxNumber,
ob.weight,
ob.width,
ob.length,
ob.height
from OrdersBoxes ob
...
where os.OID=ob.OID
...
for json path
)) boxes,
....
from table2
where....
) jj
for json path
) orderDetails
That works, but boxes node is returned escaped:
"boxes":"[{\"envelopeCode\":\"E70345D2AB90A879D4F53506FB465086\",\"boxNumber\":1,\"weight\":3000,\"width\":300,\"length\":300,\"height\":100}]"
I tried also this Solution but it works well only if returning data from 1 table:
since it returns objects {} to get an array need to change first line from
select STRING_AGG (order_details,',') ods from (
to
select concat('[',STRING_AGG (order_details,','),']') ods from (
and it seems me not very "elegant" although it works.
Can someone suggest a better way to get all data correctly formatted (thus unescaped boxes node)?
The documentation about JSON_QUERY() explains: ... JSON_QUERY returns a valid JSON fragment. As a result, FOR JSON doesn't escape special characters in the JSON_QUERY return value. If you're returning results with FOR JSON, and you're including data that's already in JSON format (in a column or as the result of an expression), wrap the JSON data with JSON_QUERY without the path parameter.. So, if I understand the schema correctly, you need to use JSON_QUERY() differently:
Tables:
SELECT *
INTO table1
FROM (VALUES
(1, 'Name1', 'Address1')
) v (oid, name, address)
SELECT *
INTO table2
FROM (VALUES
(2, 'Name2', 'Address2')
) v (oid, name, address)
SELECT *
INTO OrdersBoxes
FROM (VALUES
(1, 'E70345D2AB90A879D4F53506FB465086', 1, 3000, 300, 300, 100),
(2, 'e70345D2AB90A879D4F53506FB465086', 2, 3000, 300, 300, 100)
) v (oid, envelopeCode, boxNumber, weight, width, length, height)
Statement:
select Name, Address, JSON_QUERY(boxes) AS Boxes
from (
select
os.Name,
os.Address,
(
select ob.envelopeCode, ob.boxNumber, ob.weight, ob.width, ob.length, ob.height
from OrdersBoxes ob
where os.OID = ob.OID
for json path
) boxes
from table1 os
union all
select
os.Name,
os.Address,
(
select ob.envelopeCode, ob.boxNumber, ob.weight, ob.width, ob.length, ob.height
from OrdersBoxes ob
where os.OID = ob.OID
for json path
) boxes
from table2 os
) j
for json path
As an additional option, you may try to use FOR JSON AUTO (the format of the JSON output is automatically determined based on the order of columns in the SELECT list and their source tables):
SELECT
cte.Name, cte.Address,
boxes.envelopeCode, boxes.boxNumber, boxes.weight, boxes.width, boxes.length, boxes.height
FROM (
SELECT oid, name, address FROM table1
UNION ALL
SELECT oid, name, address FROM table2
) cte
JOIN OrdersBoxes boxes ON cte.oid = boxes.oid
FOR JSON AUTO
Result:
[
{
"Name":"Name1",
"Address":"Address1",
"boxes":[{"envelopeCode":"E70345D2AB90A879D4F53506FB465086","boxNumber":1,"weight":3000,"width":300,"length":300,"height":100}]
},
{
"Name":"Name2",
"Address":"Address2",
"boxes":[{"envelopeCode":"e70345D2AB90A879D4F53506FB465086","boxNumber":2,"weight":3000,"width":300,"length":300,"height":100}]
}
]

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

Create json object and aggregate into json array in SqlServer

I have following query in ORACLE:
SELECT *
FROM "Supplier" s
OUTER APPLY(
SELECT JSON_ARRAYAGG(JSON_OBJECT(p."Id", p."Description", p."Price")) as "Products"
FROM "Products" p
WHERE p."SupplierId" = s."Id"
) sp
In OUTER APPLY subquery I am creating json from columns I need and then aggregating those objects into json array. I need those two functions because sometimes I use only one of them. The same operation I would like to do in SqlServer. This is solution I managed so far:
SELECT *
FROM "Supplier" as s
OUTER APPLY(
SELECT p."Id", p."Description", p."Price"
FROM "Products" as p
WHERE p."SupplierId" = s."Id"
FOR JSON PATH
) as sp("Products")
The problem is that SqlServer executing those two functions at once (this is purpose for FOR JSON PATH statement). So here are my questions:
1) Is there possible to create json object without wrapping it into array (oracle-like syntax)?
2) Is there possible to aggregate json objects into an array?
UPDATE
I am using SqlServer version 2019 15.0.2000.5
Expected result (single record of products in json format)
"Products":{
"Id":"FEB0646B709B45B5A306E10599716F28",
"Description":"Database Manager",
"Price":149.99
}
If I understand the question correctly, the following statements are possible soltion (of course, they are based on the example data and statements in the question):
How to create a single JSON object:
If you want to generate one single JSON object, you need to use FOR JSON PATh for each row in the OUTER APPLY statement with the appropriate path expression. JSON_QUERY() is needed, because it returns a valid JSON and FOR JSON doesn't escape special characters.
Tables:
CREATE TABLE Supplier (
Id int,
Description varchar(50),
DateStart date
)
CREATE TABLE Products (
Id varchar(5),
SupplierId int,
Description varchar(100),
Price numeric(10, 2)
)
INSERT INTO Supplier (Id, Description, DateStart)
VALUES (1, 'Oracle', '19900505')
INSERT INTO Products (Id, SupplierId, Description, Price)
VALUES ('11111', 1, 'Database Manager', 149.99)
INSERT INTO Products (Id, SupplierId, Description, Price)
VALUES ('22222', 1, 'Chassi', 249.99)
Statement:
SELECT *
FROM "Supplier" s
OUTER APPLY(
SELECT Products = JSON_QUERY((
SELECT
p."Id" AS 'Product.Id',
p."Description" AS 'Product.Description',
p."Price" AS 'Product.Price'
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
))
FROM "Products" as p
WHERE p."SupplierId" = s."Id"
) sp ("Products")
Result:
Id Description DateStart Products
1 Oracle 1990-05-05 {"Product":{"Id":"11111","Description":"Database Manager","Price":149.99}}
1 Oracle 1990-05-05 {"Product":{"Id":"22222","Description":"Chassi","Price":249.99}}
How to aggregate JSON objects into an array:
By default FOR JSON creates a JSON array with one JSON object for each row. You only need to set a root key:
Statement:
SELECT *
FROM "Supplier" s
OUTER APPLY(
SELECT p."Id", p."Description", p."Price"
FROM "Products" p
WHERE p."SupplierId" = s."Id"
FOR JSON PATH
) sp("Products")
Result:
Id Description DateStart Products
1 Oracle 1990-05-05 [{"Id":"11111","Description":"Database Manager","Price":149.99},{"Id":"22222","Description":"Chassi","Price":249.99}]
DECLARE #data varchar(max)
DECLARE #LIST NVARCHAR(MAX)
DECLARE #Temp TABLE (YourColumnName VARCHAR(MAX) NULL);
INSERT INTO #Temp SELECT DISTINCT columnName FROM YourTableName WHERE(Id > 1000);
SELECT #LIST = STRING_AGG(CONVERT(NVARCHAR(max), ISNULL(YourColumnName, 'N/A')), ',') FROM #Temp
SET #data =(select #LIST as Name1,#LIST as Name2 For Json PATH)
select #data