Postgres Build Complex JSON Object from Wide Column Like Design to Key Value - sql

I could really use some help here before my mind explodes...
Given the following data structure:
SELECT * FROM (VALUES (1, 1, 1, 1), (2, 2, 2, 2)) AS t(day, apple, banana, orange);
day | apple | banana | orange
-----+-------+--------+--------
1 | 1 | 1 | 1
2 | 2 | 2 | 2
I want to construct a JSON object which looks like the following:
{
"data": [
{
"day": 1,
"fruits": [
{
"key": "apple",
"value": 1
},
{
"key": "banana",
"value": 1
},
{
"key": "orange",
"value": 1
}
]
}
]
}
Maybe I am not so far away from my goal:
SELECT json_build_object(
'data', json_agg(
json_build_object(
'day', t.day,
'fruits', t)
)
) FROM (VALUES (1, 1, 1, 1), (2, 2, 2, 2)) AS t(day, apple, banana, orange);
Results in:
{
"data": [
{
"day": 1,
"fruits": {
"day": 1,
"apple": 1,
"banana": 1,
"orange": 1
}
}
]
}
I know that there is json_each which may do the trick. But I am struggling to apply it to the query.
Edit:
This is my updated query which, I guess, is pretty close. I have dropped the thought to solve it with json_each. Now I only have to return an array of fruits instead appending to the fruits object:
SELECT json_build_object(
'data', json_agg(
json_build_object(
'day', t.day,
'fruits', json_build_object(
'key', 'apple',
'value', t.apple,
'key', 'banana',
'value', t.banana,
'key', 'orange',
'value', t.orange
)
)
)
) FROM (VALUES (1, 1, 1, 1), (2, 2, 2, 2)) AS t(day, apple, banana, orange);
Would I need to add a subquery to prevent a nested aggregate function?

Use the function jsonb_each() to get pairs (key, value), so you do not have to know the number of columns and their names to get a proper output:
select jsonb_build_object('data', jsonb_agg(to_jsonb(s) order by day))
from (
select day, jsonb_agg(jsonb_build_object('key', key, 'value', value)) as fruits
from (
values (1, 1, 1, 1), (2, 2, 2, 2)
) as t(day, apple, banana, orange),
jsonb_each(to_jsonb(t)- 'day')
group by 1
) s;
The above query gives this object:
{
"data": [
{
"day": 1,
"fruits": [
{
"key": "apple",
"value": 1
},
{
"key": "banana",
"value": 1
},
{
"key": "orange",
"value": 1
}
]
},
{
"day": 2,
"fruits": [
{
"key": "apple",
"value": 2
},
{
"key": "banana",
"value": 2
},
{
"key": "orange",
"value": 2
}
]
}
]
}

Related

Format SQL output to custom JSON

I have this table which is very simple with this data
CREATE TABLE #Prices
(
ProductId int,
SizeId int,
Price int,
Date date
)
INSERT INTO #Prices
VALUES (1, 1, 100, '2020-01-01'),
(1, 1, 120, '2020-02-01'),
(1, 1, 130, '2020-03-01'),
(1, 2, 100, '2020-01-01'),
(1, 2, 100, '2020-02-01'),
(2, 1, 100, '2020-01-01'),
(2, 1, 120, '2020-02-01'),
(2, 1, 130, '2020-03-01'),
(2, 2, 100, '2020-01-01'),
(2, 2, 100, '2020-02-01')
I would like to format the output to be something like this:
{
"Products": [
{
"Product": 2,
"UnitSizes": [
{
"SizeId": 1,
"PerDate": [
{
"Date": "2020-01-02",
"Price": 870.0
},
{
"Date": "2021-04-29",
"Price": 900.0
}
]
},
{
"SizeId": 2,
"PerDate": [
{
"Date": "2020-01-02",
"Price": 435.0
},
{
"Date": "2021-04-29",
"Price": 450.0
}
]
}
]
},
{
"Product": 4,
"UnitSizes": [
{
"SizeId": 1,
"PerDate": [
{
"Date": "2020-01-02",
"Price": 900.0
}
]
}
]
}
]
}
I almost have it but I don't know how to format to get the array inside 'PerDate'. This is what I have
SELECT
ProductId AS [Product],
SizeId AS 'Sizes.SizeId',
date AS 'Sizes.PerDate.Date',
price AS 'Sizes.PerDate.Price'
FROM
#Prices
ORDER BY
ProductId, [Sizes.SizeId], Date
FOR JSON PATH, ROOT('Products')
I have tried with FOR JSON AUTO and nothing, I've tried with JSON_QUERY() but I was not able to achieve the result I want.
Every help will be very appreciated.
Thanks
Unfortunately, SQL Server does not have the JSON_AGG function, which means you would normally need to use a number of correlated subqueries and keep on rescanning the base table.
However, we can simulate it by using STRING_AGG against single JSON objects generated in an APPLY. This means that we only scan the base table once.
Use of JSON_QUERY with no path prevents double-escaping
WITH PerDate AS (
SELECT
p.ProductId,
p.SizeId,
PerDate = '[' + STRING_AGG(j.PerDate, ',') WITHIN GROUP (ORDER BY p.Date) + ']'
FROM #Prices AS p
CROSS APPLY ( -- This produces multiple rows of single JSON objects
SELECT p.Date, p.Price
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) j(PerDate)
GROUP BY
p.ProductId,
p.SizeId
),
UnitSizes AS (
SELECT
p.ProductId,
UnitSizes = '[' + STRING_AGG(j.UnitSizes, ',') WITHIN GROUP (ORDER BY p.SizeId) + ']'
FROM PerDate p
CROSS APPLY (
SELECT p.SizeId, PerDate = JSON_QUERY(p.PerDate)
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) j(UnitSizes)
GROUP BY
p.ProductId
)
SELECT
Product = p.ProductId,
UnitSizes = JSON_QUERY(p.UnitSizes)
FROM UnitSizes p
ORDER BY p.ProductId
FOR JSON PATH, ROOT('Products');
db<>fiddle
This is one way of doing it
DROP TABLE IF EXISTS #Prices
CREATE TABLE #Prices
(
ProductId INT,
SizeId INT,
Price INT,
Date DATE
)
-- SQL Prompt formatting off
INSERT INTO #Prices
VALUES (1, 1, 100, '2020-01-01'),
(1, 1, 120, '2020-02-01'),
(1, 1, 130, '2020-03-01'),
(1, 2, 100, '2020-01-01'),
(1, 2, 100, '2020-02-01'),
(2, 1, 100, '2020-01-01'),
(2, 1, 120, '2020-02-01'),
(2, 1, 130, '2020-03-01'),
(2, 2, 100, '2020-01-01'),
(2, 2, 100, '2020-02-01')
-- SQL Prompt formatting on
SELECT m.ProductId AS Product,
(
SELECT s.SizeId,
(
SELECT p.Date,
p.Price
FROM #Prices AS p
WHERE p.SizeId = s.SizeId
GROUP BY p.Date,
p.Price
ORDER BY p.Date
FOR JSON PATH
) AS PerDate
FROM #Prices AS s
WHERE s.ProductId = m.ProductId
GROUP BY s.SizeId
ORDER BY s.SizeId
FOR JSON PATH
) AS UnitSizes
FROM #Prices AS m
GROUP BY m.ProductId
ORDER BY m.ProductId
FOR JSON PATH, ROOT('Products')
Output:
{
"Products":
[
{
"Product": 1,
"UnitSizes":
[
{
"SizeId": 1,
"PerDate":
[
{
"Date": "2020-01-01",
"Price": 100
},
{
"Date": "2020-02-01",
"Price": 120
},
{
"Date": "2020-03-01",
"Price": 130
}
]
},
{
"SizeId": 2,
"PerDate":
[
{
"Date": "2020-01-01",
"Price": 100
},
{
"Date": "2020-02-01",
"Price": 100
}
]
}
]
},
{
"Product": 2,
"UnitSizes":
[
{
"SizeId": 1,
"PerDate":
[
{
"Date": "2020-01-01",
"Price": 100
},
{
"Date": "2020-02-01",
"Price": 120
},
{
"Date": "2020-03-01",
"Price": 130
}
]
},
{
"SizeId": 2,
"PerDate":
[
{
"Date": "2020-01-01",
"Price": 100
},
{
"Date": "2020-02-01",
"Price": 100
}
]
}
]
}
]
}

Query group by a column and return JSON

I have a table as below:
id
mid
handphone
coupono
status
1
1
0811111111
1
1
2
1
08222222222
2
1
3
1
08222222222
3
1
4
1
08222222222
4
1
5
1
08111111111
5
1
6
2
08333333333
6
1
7
2
08333333333
7
1
8
2
08444444444
8
1
-----
-----
---------------
--------
-------
I want to query the table using WHERE clause on mId column and filtered the couponno or listed on handphone number. How to query that?
The result that I want is:
{
"08111111111": [{
"Id": 1,
"CouponNo": 1,
"Status": 1
}, {
"Id": 5,
"CouponNo": 5,
"Status": 1
}],
"08222222222": [{
"Id": 2,
"CouponNo": 2,
"Status": 1
}, {
"Id": 3,
"CouponNo": 3,
"Status": 1
}, {
"Id": 4,
"CouponNo": 4,
"Status": 1
}]
}
Requiring Handphone to be object keys in your JSON makes it difficult to produce from SQL and probably won't scale well on the receiving side either as you add more data over time.
Here is some pivot-based SQL that will produce your desired JSON...
create table dbo.PivotJsonStuff (
Id int,
[Mid] int,
Handphone varchar(11),
CouponNo int,
Status int
);
insert dbo.PivotJsonStuff (Id, [Mid], Handphone, CouponNo, Status)
values
(1, 1, '08111111111', 1, 1),
(2, 1, '08222222222', 2, 1),
(3, 1, '08222222222', 3, 1),
(4, 1, '08222222222', 4, 1),
(5, 1, '08111111111', 5, 1),
(6, 2, '08333333333', 6, 1),
(7, 2, '08333333333', 7, 1),
(8, 2, '08444444444', 8, 1);
select
[08111111111] = json_query([08111111111]),
[08222222222] = json_query([08222222222])
from (
select Handphone,
[JSON] = (
select PJS2.Id, PJS2.CouponNo, PJS2.Status
from dbo.PivotJsonStuff PJS2
where PJS2.Handphone = PJS1.Handphone
for json path
)
from dbo.PivotJsonStuff PJS1
group by Handphone
) src
pivot (max([JSON]) for Handphone in ([08111111111], [08222222222])) pvt
for json path, without_array_wrapper;
{
"08111111111": [
{
"Id": 1,
"CouponNo": 1,
"Status": 1
},
{
"Id": 5,
"CouponNo": 5,
"Status": 1
}
],
"08222222222": [
{
"Id": 2,
"CouponNo": 2,
"Status": 1
},
{
"Id": 3,
"CouponNo": 3,
"Status": 1
},
{
"Id": 4,
"CouponNo": 4,
"Status": 1
}
]
}

Kusto Query JSON Array Values

can anyone offer a clue on how to do query values within arrays -- such as below, I want to find all records where
DiscoveredInformationTypes_s Confidence > 80
Can anyone help? How do I query inside this array?
MachineName_s
Version_s
ProcessName_s
ApplicationName_s
Operation_s
ObjectId_s
DiscoveredInformationTypes_s
[ { "Confidence": 55, "Count": 1, "SensitiveType": "3356946c-6bb7-449b-b253-6ffa419c0ce7", "UniqueCount": 1, "SensitiveInformationDetections": null, "Name": "International Classification of Diseases (ICD-10-CM)" } ]
you can use mv-apply: https://learn.microsoft.com/en-us/azure/data-explorer/kusto/query/mv-applyoperator
for example:
datatable(DiscoveredInformationTypes_s:dynamic)
[
dynamic([ { "Confidence": 55, "Count": 1, "SensitiveType": "3356946c-6bb7-449b-b253-6ffa419c0ce7", "UniqueCount": 1, "SensitiveInformationDetections": null, "Name": "International Classification of Diseases (ICD-10-CM)" } ]),
dynamic([ { "Confidence": 81, "Count": 1, "SensitiveType": "3356946c-6bb7-449b-b253-6ffa419c0ce7", "UniqueCount": 1, "SensitiveInformationDetections": null, "Name": "International Classification of Diseases (ICD-10-CM)" } ])
]
| mv-apply DiscoveredInformationTypes_s on (
where DiscoveredInformationTypes_s.Confidence > 80
)
or, if your column is string-typed and not dynamic-typed, you'll need to invoke parse_json() on it first:
datatable(s:string)
[
'[ { "Confidence": 55, "Count": 1, "SensitiveType": "3356946c-6bb7-449b-b253-6ffa419c0ce7", "UniqueCount": 1, "SensitiveInformationDetections": null, "Name": "International Classification of Diseases (ICD-10-CM)" } ]',
'[ { "Confidence": 81, "Count": 1, "SensitiveType": "3356946c-6bb7-449b-b253-6ffa419c0ce7", "UniqueCount": 1, "SensitiveInformationDetections": null, "Name": "International Classification of Diseases (ICD-10-CM)" } ]'
]
| project DiscoveredInformationTypes_s = parse_json(s)
| mv-apply DiscoveredInformationTypes_s on (
where DiscoveredInformationTypes_s.Confidence > 80
)

C3JS Acces value shown on X axis

I have simple bar chart like this:
Here is my C3JS
var chart = c3.generate({
data: {
json:[{"A": 67, "B": 10, "site": "Google", "C": 12}, {"A": 10, "B": 20, "site": "Amazon", "C": 12}, {"A": 25, "B": 10, "site": "Stackoverflow", "C": 8}, {"A": 20, "B": 22, "site": "Yahoo", "C": 12}, {"A": 76, "B": 30, "site": "eBay", "C": 9}],
mimeType: 'json',
keys: {
x: 'site',
value: ['A','B','C']
},
type: 'bar',
selection: {
enabled: true
},
onselected: function(d,element)
{
alert('selected x: '+chart.selected()[0].x+' value: '+chart.selected()[0].value+' name: '+chart.selected()[0].name);
},
groups: [
['A','B','C']
]
},
axis: {
x: {
type: 'category'
}
}
});
After some chart elemnt is selected (clicked), alert shows X and Value and Name attributes of first selected element. For example "selected x: 0 value: 67 name: A" after I click on left-top chart element. How can I get value shown on X axis? In this case it is "Google".
Property categories is populated when the x-axis is declared to be of type category as it is in this case. So to get the data from the x-axis you needs to call the .categories() function.
onselected: function(d,element){alert(chart.categories()[d.index]);}
https://jsfiddle.net/4bos2qzx/1/

In PostgreSQL, what's the best way to select an object from a JSONB array?

Right now, I have an an array that I'm able to select off a table.
[{"_id": 1, "count: 3},{"_id": 2, "count: 14},{"_id": 3, "count: 5}]
From this, I only need the count for a particular _id. For example, I need the count for
_id: 3
I've read the documentation but I haven't been able to figure out the correct way to get the object.
WITH test_array(data) AS ( VALUES
('[
{"_id": 1, "count": 3},
{"_id": 2, "count": 14},
{"_id": 3, "count": 5}
]'::JSONB)
)
SELECT val->>'count' AS result
FROM
test_array ta,
jsonb_array_elements(ta.data) val
WHERE val #> '{"_id":3}'::JSONB;
Result:
result
--------
5
(1 row)