MySQL, get sum of two queries - mysql

I have three different tables about Product having different columns and structure, assume
Product1, Product2, Product3
So, I'm trying to get sum of count(*) of three tables having same user_id, i.e. foreach user_id field.
Table - Product1
select P.user_id, count(*)
from Product1 P
group by P.user_id
Table - Product2
select P.user_id, count(*)
from Product2 P
group by P.user_id
Table - Product3
select P.user_id, count(*)
from Product3 P
group by P.user_id
They give me user_id field and count(*),
Can I add results of count(*), foreach user_id field? Thanks, in advance

Having three tables with the same structure is usually a sign of poor database design. You should figure out ways to combine the tables into a single table.
In any case, you can aggregate the results. One way is:
select user_id, sum(cnt)
from ((select user_id, count(*) as cnt
from product1
group by user_id
) union all
(select user_id, count(*) as cnt
from product2
group by user_id
) union all
(select user_id, count(*) as cnt
from product3
group by user_id
)
) up
group by user_id;
You want to use union all rather than a join because MySQL does not support full outer join. Union all ensures that users from all three tables are included.
Aggregating twice (in the subqueries and the outer query) allows MySQL to use indexes for the inner aggregations. That can be a performance advantage.
Also, if you are looking for a particular user or set of users, use a where clause in the subqueries. That is more efficient (in MySQL) than bringing all the data together in subqueries and then doing the filtering.

Combine the results using UNION and then do the addition.
Query
select t.`user_id`, sum(`count`) as `total` from(
select `user_id`, count(*) as `count`
from `Product1`
group by `user_id`
union all
select `user_id`, count(*)
from `Product2`
group by `user_id`
union all
select `user_id`, count(*)
from `Product3`
group by `user_id`
) t
group by t.`user_id`;

You could sum the result of union all
select user_id, sum(my_count)
from (
select P.user_id, count(*) my_count
from Product1 P
group by P.user_id
UNION ALL
select P.user_id, count(*)
from Product2 P
group by P.user_id
UNION ALL
select P.user_id, count(*)
from Product3 P
group by P.user_id ) t
group by user_id

Yes you can :)
SELECT SUM(userProducts) userProducts
FROM (
SELECT count(user_id) userProducts FROM Product1 WHERE user_id = your_user_id
UNION ALL
SELECT count(user_id) userProducts FROM Product2 WHERE user_id = your_user_id
UNION ALL
SELECT count(user_id) userProducts FROM Product3 WHERE user_id = your_user_id
) s

Please try below. Not tried in db so could get syntax error.
select p.user_id ,sum(total) from (
select P.user_id, count() total from product1 p group by P.user_id
union all
select P.user_id, count() total from product2 p group by P.user_id
union all
select P.user_id, count(*) total from product3 p group by P.user_id
) a

Yes, we can aggregate results from different tables using join and union based on our requirement. In your case Union All will work perfectly and can write optimised query by using count(1) instead of count(*), as it uses first Index of the table which is more often clustered Index.
select user_id, sum(cnt)
from ((select user_id, count(1) as cnt
from product1
group by user_id
) union all
(select user_id, count(1) as cnt
from product2
group by user_id
) union all
(select user_id, count(1) as cnt
from product3
group by user_id
)
) a
group by user_id;

Related

SQL - Difficult Query

I want to do a query where for each diagnostic code - ID (this is a column), select the name of the most common medication - p_name (another column) used to treat that condition i.e., the medication name that more often appears associated to prescriptions (table) for that diagnosis.
This is the structure of my prescription table:
| p_name | lab | doctor_VAT | date_timestamp | ID | dosage | prescription_description |
I started by making a query to count the tuple pairs p_name and ID:
SELECT DISTINCT p.ID,
p.p_name,
COUNT(*) OVER (PARTITION BY p.p_name, p.ID) AS Cnt
FROM prescription AS p
ORDER BY Cnt DESC
And then to this I tried to apply the "greatest_n_per_group" problem to this
(SQL select only rows with max value on a column):
FROM (SELECT DISTINCT p.ID,
p.p_name,
COUNT(*) OVER (PARTITION BY p.p_name, p.ID) AS Cnt
FROM prescription AS p
ORDER BY Cnt DESC) as Tabela
INNER JOIN(
SELECT Tabela2.ID, MAX(Tabela2.Cnt)
FROM (SELECT DISTINCT p.ID,
p.p_name,
COUNT(*) OVER (PARTITION BY p.p_name, p.ID) AS Cnt
FROM prescription AS p
ORDER BY Cnt DESC) as Tabela2
GROUP BY Tabela2.ID
)
But this produces errors, am I going at this the right the way? do you suggest a different method?
Since your MySQL supports Window function, You can simply use -
SELECT ID, p_name
FROM (SELECT ID, p_name, RANK() OVER(PARTITION BY ID ORDER BY CNT DESC) RNK
FROM (SELECT ID,
p_name,
COUNT(*) CNT
FROM prescription
GROUP BY ID,
p_name
) T1
) T2
WHERE RNK = 1
You need FROM ( ) T in this case T is the table name alias for the FROM subquery clause
FROM (SELECT DISTINCT p.ID,
p.p_name,
COUNT(*) OVER (PARTITION BY p.p_name, p.ID) AS Cnt
FROM prescription AS p
ORDER BY Cnt DESC) as Tabela
INNER JOIN(
SELECT Tabela2.ID, MAX(Tabela2.Cnt)
FROM (SELECT DISTINCT p.ID,
p.p_name,
COUNT(*) OVER (PARTITION BY p.p_name, p.ID) AS Cnt
FROM prescription AS p
ORDER BY Cnt DESC) as Tabela2
GROUP BY Tabela2.ID
) T
SELECT
p1.ID,
(SELECT TOP 1 p2.p_name
FROM prescription AS p2
WHERE p2.ID=p1.ID
GROUP BY p2.p_name
ORDER BY count(*) DESC) as MostUsed
FROM prescription AS p1
GROUP BY p1.ID
Above is for MSSQL, below is for MySQL
SELECT
p1.ID,
(SELECT p2.p_name
FROM prescription AS p2
WHERE p2.ID=p1.ID
GROUP BY p2.p_name
ORDER BY count(*) DESC
LIMIT 1) as MostUsed
FROM prescription AS p1
GROUP BY p1.ID
dbfiddle

Merging multiple similar query

I can get the result from multiple but simple queries but I would like to merge them further as sub queries.
All sub queries are going to be similar to this one-
SELECT COUNT(count) AS acc1 FROM (SELECT COUNT(table.colname) AS count
FROM tablename GROUP BY tablename.sumcol HAVING count=1) as access1
likewise others will be
SELECT COUNT(count) AS acc2 FROM (SELECT COUNT(table.colname) AS count
FROM tablename GROUP BY tablename.sumcol HAVING count=2) as access2
You could use UNION ALL for get both the results in the same result set
SELECT 'acc1' , COUNT(count)
FROM ( SELECT COUNT(table.colname) AS count
FROM tablename G
ROUP BY tablename.sumcol
HAVING count=1) access1
UNION ALL
SELECT 'acc2', COUNT(count)
FROM (SELECT COUNT(table.colname) AS count
FROM tablename
GROUP BY tablename.sumcol
HAVING count=2) access2
I have addedd 'acc1' and 'acc2' literal value for a better result reading but you can avoid it
do the fact you have only a rows for both the query , if you need the two result on the same row, you can use a cross join (cartesian product)
SELECT COUNT(count) as count_acc1, T.count2
FROM ( SELECT COUNT(table.colname) AS count1
FROM tablename G
GROUP BY tablename.sumcol
HAVING count=1) access1
CROSS JOIN (
SELECT COUNT(count) as count2
FROM (SELECT COUNT(table.colname) AS count
FROM tablename
GROUP BY tablename.sumcol
HAVING count=2) access2 ) T

error in Max count(*) in SQL with same data

I wrote a SQL query to get users with the largest number of purchases.
SELECT name, count(*) as C
FROM sells
GROUP BY user_id
ORDER BY C
LIMIT 1
But, If i have two users with same number of purchase this query can not detect. what's the solution?
Try subquery:
SELECT name, count(*) as C
FROM sells
GROUP BY user_id
HAVING C >= ALL
(SELECT count(*)
FROM sells
GROUP BY user_id)
This will work in any sql version, without using LIMIT in a subquery
Write a subquery that gets the maximum count. Then use HAVING to select all the rows with that count.
SELECT name, COUNT(*) AS c
FROM sells
GROUP BY user_id
HAVING c = (SELECT COUNT(*) c
FROM sells
GROUP BY user_id
ORDER BY c DESC
LIMIT 1)
or this can be done as a join between subqueries:
SELECT t1.*
FROM (SELECT name, COUNT(*) AS c
FROM sells
GROUP BY user_id) AS t1
JOIN (SELECT COUNT(*) AS c
FROM sells
GROUP BY user_id
ORDER BY c DESC
LIMIT 1) AS t2
ON t1.c = t2.c
SELECT name, COUNT(*)
FROM sells
GROUP BY user_id
HAVING COUNT(*) = ( SELECT MAX(C) FROM ( SELECT COUNT(*) AS C FROM sells GROUP BY user_id ) )
You are using LIMIT 1 in the query. It restricts the number of records in the output to one. If you wish to see all the records from the output remove this LIMIT.
If you only need to see one row per every same count, you can modify this query as:
SELECT GROUP_CONCAT(name), count(*) as C
FROM sells
GROUP BY user_id
ORDER BY C
LIMIT 1
This will concatenate both the names having similar counts.

Making a select for a two select sql/oracle

I want to join join 2 select in single query :
Here are the two queries.
SELECT player_id, SUM(score) score
FROM (
SELECT id_p1 player_id, score_p1 score
FROM matchs
UNION ALL
SELECT id_p2, score_p2
FROM matchs
) q
GROUP BY player_id
AND
SELECT player_id, SUM(score) score
FROM (
SELECT id_p1 player_id, score_p2 score
FROM matchs
UNION ALL
SELECT id_p2, score_p1
FROM matchs
) q
GROUP BY player_id
Thank you !
Try this
SELECT table1.player_id, table1.score score1, table2.score score2,
abs(table1.score - table2.score) difference
FROM (
SELECT player_id, SUM(score) score
FROM (
SELECT player1_id player_id, score_p1 score FROM matchs
UNION ALL
SELECT player2_id , score_p2 FROM matchs
) q GROUP BY player_id
) table1
INNER JOIN
(
SELECT player_id, SUM(score) score
FROM (
SELECT player1_id player_id, score_p2 score FROM matchs
UNION ALL
SELECT player2_id , score_p1 FROM matchs
) q GROUP BY player_id
) table2 ON table1.player_id = table2.player_id
SQL Fiddle Demo
Ideally, one would perform this operation using a FULL JOIN:
SELECT COALESCE(t1.player1_id, t2.player2_id)
SUM(COALESCE(t1.score_p1,0) + COALESCE(t2.score_p2,0))
FROM table_name t1
FULL JOIN table_name t2 ON t1.player1_id = t2.player2_id
GROUP BY COALESCE(t1.player1_id, t2.player2_id)
However, sadly MySQL does not have native support for such a join operation. Instead, one can simulate it by making a UNION between a LEFT JOIN and a RIGHT JOIN, then aggregating:
SELECT p, SUM(s) FROM (
SELECT t1.player1_id p, SUM(t1.score_p1 + IFNULL(t2.score_p2,0)) s
FROM table_name t1
LEFT JOIN table_name t2 ON t1.player1_id = t2.player2_id
GROUP BY t1.player1_id
UNION
SELECT t2.player2_id, SUM(IFNULL(t1.score_p1,0) + t2.score_p2)
FROM table_name t1
RIGHT JOIN table_name t2 ON t1.player1_id = t2.player2_id
GROUP BY t2.player2_id
) t GROUP BY p
See it on sqlfiddle.

Way to improve this multiple UNION query?

I need to find the users who have either
a shared video credit;
a shared production credit; or
a shared group.
This is currently the query I came up with:
SELECT profile_id FROM productions_productionmember WHERE production_id in
(SELECT production_id FROM productions_productionmember WHERE profile_id=?)
UNION
SELECT profile_id FROM groups_groupmember WHERE group_id in
(SELECT group_id FROM groups_groupmember WHERE profile_id=?)
UNION
SELECT profile_id FROM videos_videocredit WHERE video_id in
(SELECT video_id FROM videos_videocredit WHERE profile_id=?)
Relevant tables:
groups_groupmember
- profile_id
- group_id
videos_videocredit
- profile_id
- video_id
productions_productionmember
- profile_id
- production_id
How can improve on this query?
SELECT DISTINCT p.profile_id
FROM productions_productionmember p
left outer join groups_groupmember g on g.profile_id = p.profile_id
left outer join videos_videocredit v on v.profile_id = p.profile_id
WHERE v.video_id is not null or g.group_id is not null
or p.production_id in (SELECT production_id
FROM productions_productionmember
WHERE profile_id=?)
You need a list of profiles who are present in either of 3 tables.
SELECT profile_id FROM productions_productionmember
UNION
SELECT profile_id FROM groups_groupmember
UNION
SELECT profile_id FROM videos_videocredit
If production_id\group_id\video_id can be null, than IS NOT NULL check can be used on these queries.
What is the profile_id that you are passing in you query? If you need to check if profile is present in any one of table, than you can put the where clause on the result returned by the above query.
SELECT count(*)
from (SELECT profile_id FROM productions_productionmember
UNION
SELECT profile_id FROM groups_groupmember
UNION
SELECT profile_id FROM videos_videocredit ) subq
where subq.profile_id = ?
SELECT pp.profile_id
FROM productions_productionmember pp
JOIN productions_productionmember pp2 ON pp.production_id=pp2.production_id
AND pp2.profile_id=?
UNION
SELECT gg.profile_id
FROM groups_groupmember gg
JOIN groups_groupmember gg2 ON gg.group_id=pp2.group_id
AND gg2.profile_id=?
UNION
SELECT vv.profile_id
FROM videos_videocredit vv
JOIN videos_videocredit vv2 ON vv.video_id=vv2.video_id
AND vv2.profile_id=?
This eliminates inner queries. I can't tell you if this will increase query speed.
This query returns profile_ids which can be found two or more in the tables.
Select *
From productions_productionmember as a,
groups_groupmember as b,
videos_videocredit as c
Where a.profile_id in (select ...) or
b.group_id in (select ...) or
c.video_id in (select...)
Hope it helps...