Is there a way to merge these json aggregations? - postgresql

I am trying to create json object from getting some info from one table, then creating interger arrays from some other tables' id's and adding n > 1 (2 or more) arrays to the json object. I am using Postgres version 10.7:
select json_build_object(
'id', bi.id,
'street', ba.street,
'features1', features1.f1_json_arr,
'features2', features2.f2_json_arr
)
from business.info bi
inner join business.address ba on bi.id = ba.location_id
left outer join (
select f1.location_id,
json_agg(f1_id) as f1_json_arr
from business.features1 as f1
group by f1.location_id
) features1 on bi.id = features1.location_id
left outer join (
select f2.location_id,
json_agg(f2_id) as f2_json_arr
from business.feature2 as f2
group by f2.location_id
) features2 on bi.id = features2.location_id
where bi.id='1234';
which gives me a result as I want, like so:
{
"id": "1234",
"street", "some street",
"features1": [
2,
1
],
"features2": [
3,
2,
1
]
}
Is there a cleaner way to do this? I tried this:
select json_build_object(
'id', bi.id,
'street', ba.street_name,
'features1', f1_and_f2.f1_json_arr,
'features2', f1_and_f2.f2_json_arr
)
from business.info bi
inner join business.address ba
on bi.id = ba.location_id
left outer join (
select f1.location_id,
json_agg(f1_id) as f1_json_arr,
json_agg(f2_id) as f2_json_arr
from business.feature1 as f1
inner join business.feature2 as f2 on f1.location_id = f2.location_id
group by f1.location_id
) f1_and_f2 on bi.id = f1_and_f2.location_id
where bi.id = '1234';
but got a result like this:
{
"id": "1234",
"street_name": "a street",
"features1": [
2,
2,
2,
1,
1,
1
],
"features2": [
3,
2,
1,
3,
2,
1
]
}

SELECT A.*, B.*, C_GROUPED.C_STUFF, D_GROUPED.D_STUFF
FROM A
INNER JOIN B ON B.A_ID = A.ID
LEFT JOIN ( SELECT A_ID, JSON_AGG(STUFF) AS C_STUFF FROM C GROUP BY A_ID ) AS C_GROUPED ON C_GROUPED.A_ID = A.ID
LEFT JOIN ( SELECT A_ID, JSON_AGG(OTHER_STUFF) AS D_STUFF FROM D GROUP BY A_ID ) AS D_GROUPED ON D_GROUPED.A_ID = A.ID
WHERE A.ID = 123;
should return the same result as
SELECT
A.*,
B.*,
( SELECT JSON_AGG(STUFF) FROM C WHERE C.A_ID = A.ID ) AS C_STUFF,
( SELECT JSON_AGG(OTHER_STUFF) FROM D WHERE D.A_ID = A.ID ) AS D_STUFF
FROM A
INNER JOIN B ON B.A_ID = A.ID
WHERE A.ID = 123
In fact, I would expect the second query be faster.
Ps - Since LEFT JOIN and LEFT OUTER JOIN are the same, I would suggest writing them in the same way in your query.

Related

Prisma ORM Generated SQL Performance

I have a query that I've written in Prisma. It basically connects 7 tables, each of which only has maybe 2-4 rows. Yes, 2-4 rows, that's it.
The query that Prisma generates can take upwards of 2-3 minutes to run.
When I write the query directly in SQL, it runs in 1ms.
So, the query I write looks like :
SELECT *
FROM rubricCellAssessment
INNER JOIN rubricAssessment ON rubricCellAssessment.rubricAssessmentId = rubricAssessment.id
INNER JOIN rubricCell ON rubricCellAssessment.rubricCellId = rubricCell.id
INNER JOIN criteriaDescriptor ON rubricCell.criteriaDescriptorId = criteriaDescriptor.id
INNER JOIN criteria ON criteriaDescriptor.criteriaId = criteria.id
INNER JOIN skill ON criteria.skillId = skill.id
INNER JOIN user AS student ON rubricAssessment.studentId = student.id
INNER JOIN user AS assessor ON rubricAssessment.assessorId = assessor.id
LEFT JOIN studentContentBase ON rubricAssessment.studentContentBaseId = studentContentBase.id
WHERE student.userTypeId = 1
AND assessor.userTypeId IN (2, 1000, 3)
AND skill.id = 'bd47bfff-760c-4fec-86d2-1063bbd89d63'
AND criteriaDescriptor.criteriaId = criteria.id
AND rubricCell.criteriaDescriptorId = criteriaDescriptor.id;
The query prisma produces looks like this:
select
`lp-mvp`.`RubricCellAssessment`.`id`,
`lp-mvp`.`RubricCellAssessment`.`createdAt`,
`lp-mvp`.`RubricCellAssessment`.`updatedAt`,
`lp-mvp`.`RubricCellAssessment`.`assessmentValue`,
`lp-mvp`.`RubricCellAssessment`.`rubricCellId`,
`lp-mvp`.`RubricCellAssessment`.`rubricAssessmentId`
from
`lp-mvp`.`RubricCellAssessment`
where
((`lp-mvp`.`RubricCellAssessment`.`id`) in (
select
`t0`.`id`
from
`lp-mvp`.`RubricCellAssessment` as `t0`
inner join `lp-mvp`.`RubricAssessment` as `j0` on
(`j0`.`id`) = (`t0`.`rubricAssessmentId`)
where
((`j0`.`id`) in (
select
`t1`.`id`
from
`lp-mvp`.`RubricAssessment` as `t1`
inner join `lp-mvp`.`User` as `j1` on
(`j1`.`id`) = (`t1`.`studentId`)
where
(`j1`.`userTypeId` = 1
and `t1`.`id` is not null))
and `t0`.`id` is not null))
and (`lp-mvp`.`RubricCellAssessment`.`id`) in (
select
`t0`.`id`
from
`lp-mvp`.`RubricCellAssessment` as `t0`
inner join `lp-mvp`.`RubricCell` as `j0` on
(`j0`.`id`) = (`t0`.`rubricCellId`)
where
((`j0`.`id`) in (
select
`t1`.`id`
from
`lp-mvp`.`RubricCell` as `t1`
inner join `lp-mvp`.`CriteriaDescriptor` as `j1` on
(`j1`.`id`) = (`t1`.`criteriaDescriptorId`)
where
((`j1`.`id`) in (
select
`t2`.`id`
from
`lp-mvp`.`CriteriaDescriptor` as `t2`
inner join `lp-mvp`.`Criteria` as `j2` on
(`j2`.`id`) = (`t2`.`criteriaId`)
where
((`j2`.`id`) in (
select
`t3`.`id`
from
`lp-mvp`.`Criteria` as `t3`
inner join `lp-mvp`.`Skill` as `j3` on
(`j3`.`id`) = (`t3`.`skillId`)
where
(`j3`.`id` = "bd47bfff-760c-4fec-86d2-1063bbd89d63"
and `t3`.`id` is not null))
and `t2`.`id` is not null))
and `t1`.`id` is not null))
and `t0`.`id` is not null))
and (`lp-mvp`.`RubricCellAssessment`.`id`) in (
select
`t0`.`id`
from
`lp-mvp`.`RubricCellAssessment` as `t0`
inner join `lp-mvp`.`RubricAssessment` as `j0` on
(`j0`.`id`) = (`t0`.`rubricAssessmentId`)
where
((`j0`.`id`) in (
select
`t1`.`id`
from
`lp-mvp`.`RubricAssessment` as `t1`
inner join `lp-mvp`.`User` as `j1` on
(`j1`.`id`) = (`t1`.`assessorId`)
where
(`j1`.`userTypeId` in (2,1000,3)
and `t1`.`id` is not null))
and `t0`.`id` is not null))
and (`lp-mvp`.`RubricCellAssessment`.`id`) in (
select
`t0`.`id`
from
`lp-mvp`.`RubricCellAssessment` as `t0`
inner join `lp-mvp`.`RubricCell` as `j0` on
(`j0`.`id`) = (`t0`.`rubricCellId`)
where
((`j0`.`id`) in (
select
`t1`.`id`
from
`lp-mvp`.`RubricCell` as `t1`
inner join `lp-mvp`.`CriteriaDescriptor` as `j1` on
(`j1`.`id`) = (`t1`.`criteriaDescriptorId`)
where
((`j1`.`id`) in (
select
`t2`.`id`
from
`lp-mvp`.`CriteriaDescriptor` as `t2`
inner join `lp-mvp`.`Criteria` as `j2` on
(`j2`.`id`) = (`t2`.`criteriaId`)
where
((`j2`.`id`) in (
select
`t3`.`id`
from
`lp-mvp`.`Criteria` as `t3`
inner join `lp-mvp`.`Skill` as `j3` on
(`j3`.`id`) = (`t3`.`skillId`)
where
(`j3`.`id` = "bd47bfff-760c-4fec-86d2-1063bbd89d63"
and `t3`.`id` is not null))
and `t2`.`id` is not null))
and `t1`.`id` is not null))
and `t0`.`id` is not null)))
Is there any way to optimize this (other than use RAWsql?)? I'm surprised Prisma's code generation is this bad, but I'd rather not move away from it at this point if possible.
All help gratefully accepted.

Postgresql: UNION ALL on two queries using "with <name> as"?

I have this query:
with active_cars as (
select c.id
from car c
inner join dealership d on c.dealer_id = d.id and d.active=true and d.ownerId=${userId}
)
select cd.*
from car_details cd
inner join active_cars ac on cd.car_id = ac.id
where cd.ratings=5;
Then I have query:
with ports_active_cars as (
select c.id
from car c
inner join ports p on c.port_id = p.id and p.active=true and p.ownerId=${userId}
)
select cd.*
from car_details cd
inner join ports_active_cars pac on cd.car_id = pac.id
I'd like to know how I can combine the result of the two queries into one, so I get one result including all car_details records.
I tried this:
with active_cars as (
select c.id
from car c
inner join dealership d on c.dealer_id = d.id and d.active=true and d.ownerId=${userId}
)
select cd.*
from car_details cd
inner join active_cars ac on cd.car_id = ac.id
where cd.ratings=5
union all
with ports_active_cars as (
select c.id
from car c
inner join ports p on c.port_id = p.id and p.active=true and p.ownerId=${userId}
)
select cd.*
from car_details cd
inner join ports_active_cars pac on cd.car_id = pac.id;
but that is wrong, does not run.
Is there a way to combine the two into one result returning all rows of car_details?
You need to define both CTEs at the start of the statement:
with active_cars as (
select c.id
from car c
inner join dealership d
on c.dealer_id = d.id and d.active = true and d.ownerId = ${userId}
),
ports_active_cars as (
select c.id
from car c
inner join ports p
on c.port_id = p.id and p.active=true and p.ownerId = ${userId}
)
select cd.*
from car_details cd
inner join active_cars ac on cd.car_id = ac.id
where cd.ratings = 5
union all
select cd.*
from car_details cd
inner join ports_active_cars pac on cd.car_id = pac.id;

PostgreSQL pass value into INNER JOIN

PostgreSQL 11
How to pass o.create_date value into INNER JOIN? I need Max ID before o.create_date
SELECT o.id,
o.create_date date,
sum(oi.quantity) qty,
sum(oi.quantity * sp.price) total
FROM ax_order o
LEFT JOIN ax_order_invenotry oi on o.id = oi.order_id
LEFT JOIN ax_inventory i on i.id = oi.inventory_id
LEFT JOIN ax_suppliers s on s.id = o.supplier_id
INNER JOIN ax_supplier_price sp ON (sp.inventory_id = oi.inventory_id and sp.supplier_id = o.supplier_id)
INNER JOIN
(
SELECT inventory_id,
max(id) id
FROM ax_supplier_price
WHERE create_date <= o.create_date
GROUP BY inventory_id
) lsp ON (sp.id = lsp.id)
WHERE o.store_id = 13
AND o.supplier_id = 35
GROUP BY o.id, o.create_date
ORDER BY o.id
You could use the LATERAL join mechanism to make it work:
WITH ax_order AS (
SELECT *
FROM (VALUES (1, '2000-1-1'::date, 1, 1)) as x(id, create_date, store_id, supplier_id)
), ax_order_inventory AS (
SELECT *
FROM (VALUES (1, 2, 4)) as x(order_id, inventory_id, quantity)
), ax_supplier_price AS (
SELECT *
FROM (VALUES (1, 2, 1, 10, '1999-12-31'::date)) as x(id, inventory_id, supplier_id, price, create_date)
)
SELECT o.id,
o.create_date date,
sum(oi.quantity) qty,
sum(oi.quantity * sp.price) total
FROM ax_order o
LEFT JOIN ax_order_inventory oi on o.id = oi.order_id
INNER JOIN ax_supplier_price sp ON (sp.inventory_id = oi.inventory_id and sp.supplier_id = o.supplier_id)
INNER JOIN LATERAL
(
SELECT inventory_id,
max(lsp.id) id
FROM ax_supplier_price lsp
WHERE sp.create_date <= o.create_date
GROUP BY inventory_id
) lsp ON sp.id = lsp.id
GROUP BY o.id, o.create_date
ORDER BY o.id
I deleted some JOINs that were not strictly necessary and mocked your data as well as I could see. Note, however, that you could also use a WHERE clause to find it - which should be more efficient:
WITH ax_order AS (
SELECT *
FROM (VALUES (1, '2000-1-1'::date, 1, 1)) as x(id, create_date, store_id, supplier_id)
),
ax_order_inventory AS (
SELECT *
FROM (VALUES (1, 2, 4)) as x(order_id, inventory_id, quantity)
),
ax_supplier_price AS (
SELECT *
FROM (VALUES (1, 2, 1, 10, '1999-12-31'::date)) as x(id, inventory_id, supplier_id, price, create_date)
)
SELECT o.id,
o.create_date date,
sum(oi.quantity) qty,
sum(oi.quantity * sp.price) total
FROM ax_order o
LEFT JOIN ax_order_inventory oi on o.id = oi.order_id
INNER JOIN ax_supplier_price sp
ON (sp.inventory_id = oi.inventory_id and sp.supplier_id = o.supplier_id)
WHERE sp.id =
(
-- NOTE: no GROUP BY necessary!
SELECT max(lsp.id) id
FROM ax_supplier_price lsp
WHERE sp.create_date <= o.create_date
AND lsp.inventory_id = sp.inventory_id
)
GROUP BY o.id, o.create_date
ORDER BY o.id

PostgreSQL Join with special condition

Lets assume we have the following table1:
1 2 3
a x m
a y m
b z m
I want to do an inner join on the table
INNER JOIN tabel2 ON table1.2 = table2.2
Somehow like this, but additional a condition that the value of table1.1 not unique. Thus on table1.1 = b no inner join will occure in this example.
What is the best way to achieve this?
Using a an aggregate in a sub query is how I would do it
SELECT *
FROM table1
JOIN table2
ON table1."2" = table2."2"
JOIN (
SELECT "1"
FROM table1
GROUP BY "1"
HAVING COUNT(*) > 1
) AS sub_q
ON sub_q."1" = table1."1";
Another option might be a cte or temporary table to hold the rows you're joining on
WITH _cte AS
(
SELECT "1"
FROM table1
GROUP BY "1"
HAVING COUNT(*) > 1
)
SELECT *
FROM table1
JOIN table2
ON table1."2" = table2."2"
JOIN _cte AS cte
ON cte."1" = table1."1";
temp table:
CREATE TEMPORARY TABLE _tab
(
"1" varchar
);
INSERT INTO _tab
SELECT "1"
FROM table1
GROUP BY "1"
HAVING COUNT(*) > 1;
SELECT *
FROM table1
JOIN table2
ON table1."2" = table2."2"
JOIN _tab AS tab
ON tab."1" = table1."1";

Sequelize: joining table on a subquery

I am trying to join a table on a subquery, but I don't know how to express it using Sequelize ORM. This is the raw SQL I want to run:
SELECT *
FROM table_a a
LEFT OUTER JOIN (SELECT * FROM table_b b WHERE col = VAL) ON a.id = b.id;
I tried
A.findAll({
include: [
{
model: B,
where: { col: val },
}
]
}).then(...);
but that doesn't get me the query I want. Instead it changes the join to an INNER JOIN, and joins on col = VALUE instead. Is there a way to do a join on the result of a subquery? I am using Postgres if it matters.
Update: After making the following change, the resulting query now uses a LEFT OUTER JOIN as expected:
include: [
{
model: B,
where: { col: val },
required: false,
}
]
However, it is still joining on col = VALUE, the generated query looks like:
SELECT * FROM table_a a
LEFT OUTER JOIN table_b b ON a.id = b.id AND b.col = VALUE;
These 2 queries ARE functionally equivalent:
SELECT * FROM table_a a
LEFT OUTER JOIN (SELECT * FROM table_b b WHERE col = VAL) ON a.id = b.id;
SELECT * FROM table_a a
LEFT OUTER JOIN table_b b ON a.id = b.id AND b.col = VALUE;
There is NO ADVANTAGE from the first one whatsoever. So, if the first one provides the wanted results, so should the second one.