Mysql re-using sums of subqueries - mysql

I have the following simplified query:
Select
(select sum(f1) from tableA a where a.id = t.id) sum1,
(select sum(f2) from tableB b where b.id = t.id) sum2,
t.*
from Table t;
My wish is to have sum1 and sum2 re-used without calculating them again:
Select
(select sum(f1) from tableA a where a.id = t.id) sum1,
(select sum(f2) from tableB b where b.id = t.id) sum2,
sum1 + sum2 `sum3`,
t.*
from Table t;
Of course i can do the following query but this will unnecessary double the run time:
Select
(select sum(f1) from tableA a where a.id = t.id) sum1,
(select sum(f2) from tableB b where b.id = t.id) sum2,
(select sum(f1) from tableA a where a.id = t.id) +
(select sum(f2) from tableB b where b.id = t.id) `sum3`,
t.*
from Table t;
or even inserting the sum1 and sum2 results to a temporary table but can't imaging i'm overlooking something to have mysql do some efficient querying on summed fields.
Is there a better, more efficient way to re-use summed fields?

Try running this query,
select Resutl.*,Result.sum1+Result.sum2 as sum3 from(
SELECT (SELECT SUM(f1) FROM tableA a WHERE a.id = t.id) sum1,
(SELECT SUM(f2) FROM tableB b WHERE b.id = t.id) sum2,
t.*
FROM Table t)Result
)
Hope it will help.

My wish is to have sum1 and sum2 re-used without calculating them again
Whoa, steady on. The original query is not as efficient as it might be. Without knowing what the data distribution looks like its hard to advise what the most appropriate query is, but assuming that the tuples in A and B have a foreign key constraint (implicit or explicit) on table T but no implicit foreign key restraint on each other, that t.id is unique, and most of the rows in tableT have corresponding rows in tableA and tableB then....
SELECT t.*, sum_f1, sum_f2
FROM tableT t
LEFT JOIN (SELECT a.id, SUM(f1) AS sum_f1
FROM tableA a
GROUP BY a.id) AS a_agg
ON t.id=a_agg.id
LEFT JOIN (SELECT b.id, SUM(f2) AS sum_f2
FROM tableB b
GROUP BY b.id) as b_agg
ON t.id=b_agg.id
GROUP BY t.id
Will be much more efficient.
(this also assumes that ONLY_FULL_GROUP_BY is disabled - otherwise you'll need to replace 't.*' in the SELECT clause and 't.id' in the group by clause with each of the attributes you need from the table).
Note that in practice you're rarely going to be looking at allyour data in a single query. Since MySQL doesn't handle push predicates very well, simply adding a filter in the outer SELECT probably won't be the optimal solution.
Once you've structured your query as above, it's trivial to add the sums:
SELECT t.*, sum_f1, sum_f2,
IFNULL(sum_f1,0)+INFULL(sum_f2,0) AS sum3
FROM tableT t
LEFT JOIN (SELECT a.id, SUM(f1) AS sum_f1
FROM tableA a
GROUP BY a.id) AS a_agg
ON t.id=a_agg.id
LEFT JOIN (SELECT b.id, SUM(f2) AS sum_f2
FROM tableB b
GROUP BY b.id) as b_agg
ON t.id=b_agg.id
GROUP BY t.id
(but note that I've made a lot of assumptions about your data and schema)

Related

How to remove this subquery from this SQL statement?

I have a SQL statement that does left join with a table:
select a.id, b.col1 from tableA a
left join (select col1 from tableB where date = 'xxx') b on a.id = b.id
For some application constraint (I need to use Spring JPQL query that does not permit subquery), I need to "flatten" this query to remove the subquery without changing the meaning of the query: I want to enrich tableA with a subset of tableB.
I have tried a few queries such as:
select a.id, b.col1 from tableA a
left join tableB b on a.id = b.id
where (date = 'xxx' or date is null)
But that gave me different set of answer from previous query.
How do I remove this subquery?
It can be done in multiple different ways - using cte, using joins
Using join it can be implemented as -
select a.id, b.col1 from tableA a left join tableB b on a.id = b.id and b.date = 'xxx'
using CTE it can be implemented as -
with t as
(
select col1, id from tableB where date = 'xxx'
)
select a.id, b.col1 from tableA a
left join t on a.id = t.id

SQL alternative for fetching latest record of each item in a table using partition

Im have been query the database to collectively fetch latest record or each item using PARTITION and ROW_COUNT() which works on MariaDB version 10.4* but i want to query the same on a MySQL version 5.7* database but it doesn't work there. I would like to figure out the alternative that will work on the MySQL database. Kindly help me out.
The query is as follows.
SELECT A_id, B_id, Created_at
FROM
(
SELECT a.id as A_id, b.id as B_id, b.Created_at,
ROW_NUMBER() OVER (PARTITION BY a.id ORDER BY b.Created_at DESC) AS rn
FROM beta b
JOIN alpha a ON b.a_id = a.id
) q
WHERE rn = 1
You may use a join to subquery which finds the latest record for each id:
SELECT a.id AS A_id, b.id AS B_id, b.Created_at
FROM alpha a
INNER JOIN beta b
ON a.id = b.a_id
INNER JOIN
(
SELECT a.id AS max_id, MAX(b.Created_at) AS max_created_at
FROM alpha a
INNER JOIN beta b ON a.id = b.a_id
GROUP BY a.id
) t
ON t.max_id = a.id AND t.max_created_at = b.Created_at;
The idea here is that the additional join to the subquery above aliased as t will only retain the record, for each a.id, having the latest Created_at value from the B table. This has the same effect as your current approach using ROW_NUMBER, without actually needing to use analytic functions.

How to use GROUP_CONCAT on multiple JOIN

I am currently retrieving data from multiple tables using a cus_id as the initial query. From here I have multiple tables that can have various rows of data that I would like to retrieve and use GROUP_CONCAT to return this data in a single row. At the moment, my query is returning duplicate data based on the max number of rows returned by one of the group concats.
SELECT a.id,c.x,c.y,c.z
GROUP_CONCAT(a.column_a) AS aca,
GROUP_CONCAT(a.column_b) AS acb,
GROUP_CONCAT(b.column_a) AS bca,
GROUP_CONCAT(b.column_b) AS bcb,
FROM `table_a` a
INNER JOIN `table_b` b ON a.id = b.id
INNER JOIN `table_c` c ON a.id = c.id
WHERE a.id = ?
Also, in this scenario, what is the correct join method to use. I am expecting all the fields I am requesting to have some sort of data.
Problem was resolved by using sub queries to isolate the GROUP_CONCAT requests. This allowed me to get only the data I wanted without duplicate results manipulated by other JOIN requests.
SELECT a.id,c.x,c.y,c.z
(SELECT GROUP_CONCAT(column_a) FROM table_a) AS aca,
(SELECT GROUP_CONCAT(column_b) FROM table_a) AS acb,
(SELECT GROUP_CONCAT(column_a) FROM table_b) AS bca,
(SELECT GROUP_CONCAT(column_b) FROM table_b) AS bcb,
FROM table_a a
INNER JOIN `table_c` c ON a.id = c.id
WHERE a.id = ?
Aggregate before joining. Somthing along the lines of:
select
a.*,
b.grp_a,
b.grp_b,
c.grp_x,
b.grp_y
from table_a a
join
(
select
a_id,
group_concat(a order by b_id) as grp_a,
group_concat(b order by b_id) as grp_b
from table_b
group by a_id
) b on b.a_id = a.id
join
(
select
a_id,
group_concat(x order by c_id) as grp_x,
group_concat(y order by c_id) as grp_y
from table_c
group by a_id
) c on c.a_id = a.a_id
order by a.a_id;

CASE and IF query only returning one conditional result

SELECT a.*, IF(b.pid=a.id,b.id,0) AS bpid
FROM table1 a, table2 b
ORDER BY a.datetime DESC;
I have fifteen records in table1 a, and two records in table2 b where b.pid=a.id.
Only one of those records from table b is being pulled into the result at any given time. Both should be pulled in as bpid, but only the latter of the two makes the trip.
I've also tried using CASE (tried CASE first, actually):
SELECT a.*, (CASE WHEN b.pid=a.id THEN b.id ELSE NULL END) AS bpid
FROM table1 a, table2 b
ORDER BY a.datetime DESC;
What am I missing?
For clarity: I get all 15 records from table1 a in the result, but not both records from table2 b.
You are not joining the tables correctly. As a tip just stop using commas in the FROM clause, this will help make you consider what the joins should be.
/* for all records */
SELECT
a.*
, b.id AS bpid
FROM table1 a
LEFT JOIN table2 b ON a.id = b.id
ORDER BY a.DATETIME DESC
or
/* for only matching records */
SELECT
a.*
, b.id AS bpid
FROM table1 a
INNER JOIN table2 b ON a.id = b.id
ORDER BY a.DATETIME DESC

How to use JOINS instead of sub query

SELECT *
FROM table_a
LEFT JOIN table_b ON (table_b.id = table_a.id)
WHERE table_b.created BETWEEN ? AND ?
AND table_b.manager IN(
SELECT DISTINCT (b.id)
FROM table_b a
INNER JOIN table_b b ON a.manager = b.id
AND b.user_level > 0
AND b.id != 1
)
How can I remove the sub query and use JOINS instead in the above query
Thanks
MySQL 5.5 (and lower version)'s optimizer produces a DEPENDENT SUBQUERY for IN (SELECT ...) which means every matching row is found, IN(SELECT ...) is evaluated that makes select slow.
Simple optimization of your query is make inline view as follows:
SELECT *
FROM table_a LEFT JOIN table_b ON (table_b.id = table_a.id)
INNER JOIN (
SELECT DISTINCT b.id AS id
FROM table_b a
INNER JOIN table_b b ON a.manager = b.id
AND b.user_level > 0
AND b.id != 1
) x ON table_b.manager = x.id
WHERE table_b.created BETWEEN ? AND ?
AND x.id IS NOT NULL;
I think avobe query would produce as same as your own.
Last, DISTINCT is not a function. please use SELECT DISTINCT b.id rather SELECT DISTINCT (b.id)