I'm looking to create a computed column in Hasura which returns a set of another table and includes a running total column of the set. For example:
table_a
id product_id
----------- ----------
1 "1"
2 "2"
2 "3"
table_b
id product_id qty created_at
----------- ---------- --- ----------
1 "1" 6 01/01/20
2 "2" 4 01/02/20
3 "3" 2 01/02/20
4 "3" 2 01/02/20
5 "1" 4 01/03/20
6 "2" 6 01/03/20
Desired GQL Response:
{
"data": {
"table_a": [
{
"id": 1,
"product_id": "1",
"computed_table_b_rows": [
{
id: 1,
product_id: "1",
qty: 6,
created_at: 01/01/20,
running_total: 6,
},
{
id: 5,
product_id: "1",
qty: 4,
created_at: 01/03/20,
running_total: 10,
},
]
}
]
}
}
Here's what I have so far which does not work:
CREATE FUNCTION filter_table_b(table_a_row table_a)
RETURNS SETOF table_b AS $$
SELECT *,
SUM (qty) OVER (PARTITION BY product_id ORDER BY created_at) as running_total
FROM table_b
WHERE product_id = table_a_row.product_id
$$ LANGUAGE sql STABLE;
It seems like SETOF is supposed to be a subset of the table, and cannot have new columns. E.g. I tried:
CREATE FUNCTION getfoo(int) RETURNS SETOF table_a AS $$
SELECT *, 'asdf' as extra FROM table_a WHERE id = $1;
$$ LANGUAGE SQL;
resulting in the Hasura-reported error:
SQL Execution Failed
postgres-error: return type mismatch in function declared to return table_a
{
"path": "$.args[0].args",
"error": "query execution failed",
"internal": {
"arguments": [],
"error": {
"status_code": "42P13",
"exec_status": "FatalError",
"message": "return type mismatch in function declared to return table_a",
"description": "Final statement returns too many columns.",
"hint": ""
},
"prepared": false,
"statement": "CREATE FUNCTION getfoo(int) RETURNS SETOF table_a AS $$\n SELECT *, 'asdf' as extra FROM table_a WHERE id = $1;\n$$ LANGUAGE SQL;"
},
"code": "postgres-error"
}
There are a few other options, however:
Add the "running total" column to table_b itself (by changing the function definition. Detailed below.
If this is not performant enough, another option is to create a view over table_b that has the running_total column using sum and partition. And then create a relationship from table_a to table_b_view.
Create a trigger on table_b, that when a new row is added it computes the running_total column and stores it, as that data seems to be static as it's based on historical data.
Option 1 is detailed below as it most closely resembles original implementation (use of a function):
CREATE OR REPLACE FUNCTION public.table_b_running_total(table_b_row table_b)
RETURNS bigint
LANGUAGE sql
STABLE
AS $function$
SELECT SUM (qty)
FROM table_b
WHERE product_id = table_b_row.product_id
AND created_at <= table_b_row.created_at
LIMIT 1
$function$
With that, I was able to get the desired result -- tho the desired graphql response does not exactly match.
query:
query MyQuery {
table_a(where:{ id: { _eq: 1 }}) {
id
product_id
table_bs {
id
product_id
qty
created_at
table_b_running_total
}
}
}
response:
{
"data": {
"table_a": [
{
"id": 1,
"product_id": "1",
"table_bs": [
{
"id": 1,
"product_id": "1",
"qty": 6,
"created_at": "2020-01-01T00:00:00+00:00",
"table_b_running_total": 6
},
{
"id": 5,
"product_id": "1",
"qty": 4,
"created_at": "2020-01-03T00:00:00+00:00",
"table_b_running_total": 10
}
]
}
]
}
}
Related
I have cosmos DB with a container having multiple documents. I want to get all the ids with the same value of a property. Since it's Cosmos I cannot use the having clause.
eg: If there is a container with the schema,
{
"id": 1,
"source": "online",
"type": "login"
},
{
"id": 1,
"source": "online",
"type": "login"
},
{
"id": 2,
"source": "online",
"type": "login"
},
{
"id": 2,
"source": "In store",
"type": "login"
}
I want all the ids where the source value is all same and "online". So in the above example, it should return "id" as 1 only.
This request selects all IDs that did not have other source types at all, only those online
select distinct(id), source
from(
select id, source
from your_table as t
WHERE NOT EXISTS (SELECT id
FROM your_table
WHERE source != 'online'
AND your_table.id = t.id))x
-- If need at the end use this WHERE x.id IS NOT NULL
Result :
| id | source
|:---- |:------:
| 1 | online
I have a jsonb column in my table and data is in this format:
[
{
"id": 1,
"DATA": {
"a": "XXX",
"key": "value1"
}
},
{
"id": 2,
"DATA": {
"a": "XXX",
"key": "value2"
}
}
]
I would like to get the count of rows in which key = value1. I tried some queries like:
select count(t.id)
from my_table t,
jsonb_array_elements(summary->'DATA') elem
where elem->>'key' = 'value1';
It returned 0 rows, though there are rows in db with that key value pair. Thanks in advance,
Use jsonb_array_elements() for the column summary as it is in the form of json array.
select count(distinct t.id)
from my_table t
cross join jsonb_array_elements(summary) elem
where elem->'DATA'->>'key' = 'value1';
Alternatively, you can get rid of the function using #> operator:
select count(t.id)
from my_table t
where summary #> '[{"DATA":{"key":"value1"}}]'
The second solution should be faster.
Db<>fiddle.
If I have the below example_table in BigQuery. When I query the table with "Original Query" I get the "Actual Result" (which makes since). Is there a way to query BigQuery directly to get the "Desired Result"
Original Query
SELECT ID, SUBID FROM `example_table ORDER BY ID
example_table
ID | SUBID
12 abc
12 def
12 ghi
34 jkl
34 mno
56 prg
Actual Result
[{
"ID": "12",
"SUBID": "abc"
}, {
"ID": "12",
"SUBID": "def"
}, {
"ID": "12",
"SUBID": "ghi"
}, {
"ID": "34",
"SUBID": "jkl"
}, {
"ID": "34",
"SUBID": "mno"
}, {
"ID": "56",
"SUBID": "prg"
}]
Desired Result
[{
"ID": "12",
"SUBID": ["abc", "def", "ghi"]
}, {
"ID": "34",
"SUBID": ["jkl", "mno"]
}, {
"ID": "56",
"SUBID": ["prg"]
}]
Below is for BigQuery Standard SQL
#standardSQL
SELECT ID, ARRAY_AGG(SUBID) SUBID
FROM `project.dataset.example_table`
GROUP BY ID
You can test, play with above using sample data from your question as in example below
#standardSQL
WITH `project.dataset.example_table` AS (
SELECT 12 ID, 'abc' SUBID UNION ALL
SELECT 12, 'def' UNION ALL
SELECT 12, 'ghi' UNION ALL
SELECT 34, 'jkl' UNION ALL
SELECT 34, 'mno' UNION ALL
SELECT 56, 'prg'
)
SELECT ID, ARRAY_AGG(SUBID) SUBID
FROM `project.dataset.example_table`
GROUP BY ID
-- ORDER BY ID
with result
If BigQuery does use MySQL syntax you might be able to do this. If not you can continue CONCAT throughout all of your query using multiple selects but it would be a little more convoluted instead of the JSON_ARRAYAGG.
SELECT CONCAT('{','ID:', ID,', SUBID:', JSON_ARRAYAGG(SUBID),'}') as JSON
FROM contact GROUP BY ID;
https://www.db-fiddle.com/f/37ru5oq4dFQSscwYsfx386/25
I'm struggling to understand arrays and structs in BigQuery. When I run this query in Standard SQL:
with t1 as (
select 1 as id, [1,2] as orders
union all
select 2 as id, null as orders
)
select
id,
orders
from t1
order by 1
I get this result in json:
[
{
"id": "1",
"orders": [
"1",
"2"
]
},
{
"id": "2",
"orders": []
}
]
I want to remove to remove the orders value for id = 2 so that I instead get:
[
{
"id": "1",
"orders": [
"1",
"2"
]
},
{
"id": "2"
}
]
How can I do this? Do I need to add another CTE to remove null values, how?
This is a follow-up question of Extract all values from json in sql table
What if the json value has multiple levels?
For example,
{
"store-1": {
"Apple": {
"category": "fruit",
"price": 100
},
"Orange": {
"category": "fruit",
"price": 80
}
},
"store-2": {
"Orange": {
"category": "fruit",
"price": 90
},
"Potato": {
"category": "vegetable",
"price": 40
}
}
}
In this case, I want to extract the price for all the items. But I get error when I run the below query.
with my_table(items) as (
values (
'{"store-1":{"Apple":{"category":"fruit","price":100},"Orange":{"category":"fruit","price":80}},
"store-2":{"Orange":{"category":"fruit","price":90},"Potato":{"category":"vegetable","price":40}}}'::json
)
)
select key, (value->value->>'price')::numeric as price
from my_table,
json_each(json_each(items))
I get the below error.
ERROR: function json_each(record) does not exist
LINE 10: json_each(json_each(items))
If I remove one json_each(), it throws
ERROR: operator does not exist: json -> json
LINE 8: select key, (value->value->>'price')::numeric as price
You can use lateral join, something like:
with my_table(items) as (
values (
'{"store-1":{"Apple":{"category":"fruit","price":100},"Orange":{"category":"fruit","price":80}},
"store-2":{"Orange":{"category":"fruit","price":90},"Potato":{"category":"vegetable","price":40}}}'::json
)
)
select outer_key, key, value->>'price' from (
select key as outer_key, value as val from my_table
join lateral json_each(items)
on true
)t
join lateral json_each(val)
on true