Complex Joins (Joining Joins) - mysql

I have 4 tables: booking, address, search_address & search_address_log
Tables: (relevant cols)
booking: (pickup_address_id, dropoff_address_id)
address: (address_id, postcode)
search_address: (address_id, postcode)
search_address_log: (id, from_id, to_id)
What I need to do is have a count from both booking and search_address_log
grouped by the pickup/dropoff & from/to postcodes.
I can do this individually for each i.e.:
booking:
SELECT
a1.postcode b_From,
a2.postcode b_to,
COUNT(*) b_count
FROM booking b
INNER JOIN address a1 ON b.pickup_address_id = a1.address_id
INNER JOIN address a2 ON b.destination_address_id = a2.address_id
GROUP BY b_From, b_To
ORDER BY COUNT(*) DESC
LIMIT 10
search_address_log:
SELECT
sa1.postcode s_From,
sa2.postcode s_To,
COUNT(*) s_count
FROM search_address_log sal
INNER JOIN search_address sa1 ON sal.from_id=sa1.address_id
INNER JOIN search_address sa2 ON sal.to_id=sa2.address_id
GROUP BY s_From, s_To
ORDER BY COUNT(*) DESC
LIMIT 10
Returning tables like:
| b_To b_From b_count || s_To s_From s_count |
| x y 10 || x y 50 |
| a b 5 || a b 60 |
WHAT I NEED:
| To From b_count s_count |
| x y 10 50 |
| a b 5 60 |
Thanks,
George

Technically, what you want is a full outer join, but MySQL doesn't support that. However, the following should do what you want -- getting summaries for each from and to value for the two columns:
SELECT b_from, b_to, sum(b_count) as b_count, sum(s_count) as s_count
FROM ((SELECT a1.postcode as b_From, a2.postcode as b_to, COUNT(*) as b_count, 0 as s_count
FROM booking b INNER JOIN
address a1
ON b.pickup_address_id = a1.address_id INNER JOIN
address a2
ON b.destination_address_id = a2.address_id
GROUP BY b_From, b_To
) UNION ALL
(SELECT sa1.postcode as s_From, sa2.postcode as s_To, 0, COUNT(*) as s_count
FROM search_address_log sal INNER JOIN
search_address sa1
ON sal.from_id = sa1.address_id INNER JOIN
search_address sa2
ON sal.to_id = sa2.address_id
GROUP BY b_From, b_To
)
) ft
GROUP BY s_From, s_to;

Proposal: select on the addresses -- get counts of the to/from pairs, and then add them up by postcode
SELECT t.postcode, f.postcode, SUM(sal.count), SUM(b.count)
FROM search_address t, search_address f
LEFT JOIN ( SELECT from_id, to_id, COUNT(*) count
FROM search_address_log GROUP BY from_id, to_id ) sal
ON sal.from_id=f.address_id AND sal.to_id=t.address_id
LEFT JOIN ( SELECT pickup_address from_id, destination_address_id to_id, COUNT(*) count
FROM booking GROUP BY from_id, to_id) b
ON b.from_id=f.address_id AND b.to_id=t.address_id
WHERE sal.count > 0 OR b.count > 0
GROUP BY t.postcode, f.postcode;
This will scale based on number of addresses squared, which may end up worse than the "generate independent summaries and then union them" scheme outlined in another answer. It's a bit more concise, however.

If every booking has a pickup_address and a destination_address and every search_address_log from_id an to_id has a search address and also that the codes in booking are the same as those in the search address log then you can do a join as in
select t1.b_from, t1.b_to, t1.b_count, t2.s_count from
(SELECT
a1.postcode b_From,
a2.postcode b_to,
COUNT(*) b_count
FROM booking b
INNER JOIN address a1 ON b.pickup_address_id = a1.address_id
INNER JOIN address a2 ON b.destination_address_id = a2.address_id
GROUP BY b_From, b_To) t1
inner join
(SELECT
sa1.postcode s_From,
sa2.postcode s_To,
COUNT(*) s_count
FROM search_address_log sal
INNER JOIN search_address sa1 ON sal.from_id=sa1.address_id
INNER JOIN search_address sa2 ON sal.to_id=sa2.address_id
GROUP BY s_From, s_To) t2 on (t1.b_from = t2.s_from and t1.b_to = t2.s_to)

Related

combining two select statements with group by and order by

I have the following 2 select statements that work and return the info on their own, but was wondering if there was a way to join / combine them into one report with their own separate rows and columns
Select Sum (amount) as PDCGross
From Desk D Left Join
Master M
on D.code = M.desk INNER JOIN
pdc
ON m.number = pdc.number
Where teamid = 3
AND active = 1
And onhold is NULL
And deposit >='2020-10-01'
And deposit <= '2020-10-31'
Group by D.Name
Order by desk.Name Desc
Select Sum (amount) as GrossPDCC
FROM Desk D left JOIN
Master M
on D.code = M.Desk INNER JOIN
DebtorCreditCards P
ON m.number = P.Number
Where teamid = 3
AND IsActive = 1
And OnHoldDate is NULL
And DepositDate >='2020-10-01'
And DepositDate <= '2020-10-31'
Group by D.Name
Order by desk.Name Desc
The return for the 1st statement
PDCPross
2500
1500
1300
The result of the 2nd statement is
PDCCGross
1500
1300
1000
What i am looking for is
PDCPross PDCCGross
2500 1500
1500 1300
1300 1000
On MySQL v 8.0+ or MariaDB 10.2 +, you can use ROW_NUMBER() function:
Assign each query with ROW_NUMBER() then make both query as sub-query. Join the queries using the rownum result and you should be able to see your results side by side. Something like this query example:
SELECT A.PDCGross, B.GrossPDCC FROM
(SELECT ROW_NUMBER() OVER (ORDER BY D.Name DESC) AS rownum, SUM(amount) AS PDCGross
FROM Desk D LEFT JOIN
MASTER M
ON D.code = M.desk INNER JOIN
pdc
ON m.number = pdc.number
WHERE teamid = 3
AND active = 1
AND onhold IS NULL
AND deposit >='2020-10-01'
AND deposit <= '2020-10-31'
GROUP BY D.Name
ORDER BY D.Name DESC) A JOIN
(SELECT ROW_NUMBER() OVER (ORDER BY D.Name DESC) AS rownum, SUM(amount) AS GrossPDCC
FROM Desk D LEFT JOIN
MASTER M
ON D.code = M.Desk INNER JOIN
DebtorCreditCards P
ON m.number = P.Number
WHERE teamid = 3
AND IsActive = 1
AND OnHoldDate IS NULL
AND DepositDate >='2020-10-01'
AND DepositDate <= '2020-10-31'
GROUP BY D.Name
ORDER BY D.Name DESC) B ON A.rownum=B.rownum;

Couple that have ordered the maximum of common products - SQL

I have trouble selecting the couple of customers that have ordered the maximum of common products.
Example :
Customer1 ordered products : a, b, c, z
Customer2 ordered products : a, c, d, g
Customer1 ordered products : g, h, z
Expected result : Customer1 | Customer2 | 2 (Number of common products ordered)
What I tried :
SELECT c.pid, c.cid, d.cid, count(c.pid)
FROM orders c JOIN orders d join
(SELECT a.cid, b.cid FROM customers a JOIN customers b ON b.cname != a.cname AND b.cname > a.cname) as subq
ON c.cid = a.cid and d.cid = b.cid
AND c.pid = d.pid group by c.pid;
This returns every couple of names :
select a.cname client_1, b.cname client_2
from customers a join customers b on b.cname != a.cname and b.cname > a.cname;
How can I get the max of common pids for a couple of cids ?
Tables :
customers (cid, cname, residence)
orders (pid, cid, odate, quantity)
products (pid, pname, price, origin)
Might not work in every RDBMS. Which one are you using?
select o1.cid, o2.cid, o1.pid, o1.cnt, o2.cnt
from (
select cid, pid, count(*) as cnt
, ROW_NUMBER() OVER(PARTITION BY cid, pid ORDER BY count(*) DESC) AS rn
from order
group by cid, pid
) as o1
join (
select cid, pid, count(*) as cnt
, ROW_NUMBER() OVER(PARTITION BY cid, pid ORDER BY count(*) DESC) AS rn
from order
group by cid, pid
) as o2
on o1.cid <> o2.cid
and o1.pid = o2.pid
where o1.rn = 1
and o2.rn = 1

Group and count rows and then use them in condition

I have an query like:
SELECT * FROM account AS a
LEFT JOIN (SELECT SUM(bill.amount) total, bill.accountId FROM bill GROUP BY bill.accountId) b ON a.id = b.accountId
WHERE a.partner_id = 1 OR a.partner_id = 2
How can I check, how many groups in "bill" has the same a.partner_id?
For example: 3 groups has partner_id = 1, 2 groups has partner_id = 2.
And later include to left join only groups, if more than 2 groups have the same partner_id.
If I understand correctly, you just want an aggregation on top of your query:
SELECT a.partner_id, count(*) as cnt, sum(total) as total
FROM account a LEFT JOIN
(SELECT SUM(b.amount) as total, b.accountId
FROM bill b
GROUP BY b.accountId
) b
ON a.id = b.accountId
GROUP BY a.partner_id;
You should be able to use the "HAVING" clause. Below is an example from the following link:
https://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html
SELECT name, COUNT(name) AS c FROM orders
GROUP BY name
HAVING c = 1;

Mysql query returns duplicate entry

SELECT *
FROM ( SELECT a.*, a.id AS id_player,
(SELECT COUNT(id)
FROM `vd7qw_footsal_goals`
WHERE a.id = id_player
AND id_group IN (SELECT id_group
from `vd7qw_footsal_groupofleague`
WHERE id_league = 2)
) AS goals,
team.team_name
FROM `vd7qw_footsal_players` AS a
LEFT JOIN vd7qw_footsal_teams AS team
ON team.id = a.id_team
LEFT JOIN vd7qw_footsal_teamofgroup AS tog
ON tog.id_team = team.id
LEFT JOIN vd7qw_footsal_groups AS g
ON g.id = tog.id_group
WHERE (a.state IN (1))
) AS h
WHERE goals > 0
ORDER BY goals DESC
This is my query when I have 2 or more groups in 1 league and player score a goal in each group, query returns the correct number of goals but duplicates player for example:
John Doe got 3 goals in group 1
and
John Doe got 4 goals in group 2
Query returns:
John Doe got 7 goals
where is my mistake?
Try GROUP BY
SELECT *
FROM ( SELECT a.*, a.id AS id_player, (SELECT COUNT(id)
FROM `vd7qw_footsal_goals`
WHERE a.id = id_player
AND id_group IN (SELECT id_group
from `vd7qw_footsal_groupofleague`
WHERE id_league = 2)) AS goals, team.team_name
FROM `vd7qw_footsal_players` AS a
LEFT JOIN vd7qw_footsal_teams AS team
ON team.id = a.id_team
LEFT JOIN vd7qw_footsal_teamofgroup AS tog
ON tog.id_team = team.id
LEFT JOIN vd7qw_footsal_groups AS g
ON g.id = tog.id_group
WHERE (a.state IN (1))) AS h
WHERE goals > 0
GROUP BY id_group
ORDER BY goals DESC

How to Get number of post done by user

Here is my question:
I have 4 tables which hold post done by user(say CatA,CatB,CatC and CatD, each table hold created_by column). My requirement is to get all the user available from table USER with post count (sum of post).
I am struggling to find answer from past 2 days and i'm still clueless.
Any idea is much appreciated.
SELECT u.username,
a.cnt + b.cnt + c.cnt + d.cnt AS total_posts
FROM users u
LEFT JOIN (SELECT created_by, COUNT(*) AS cnt FROM CatA GROUP BY created_by) a
ON u.id = a.created_by
LEFT JOIN (SELECT created_by, COUNT(*) AS cnt FROM CatB GROUP BY created_by) b
ON u.id = b.created_by
LEFT JOIN (SELECT created_by, COUNT(*) AS cnt FROM CatC GROUP BY created_by) c
ON u.id = c.created_by
LEFT JOIN (SELECT created_by, COUNT(*) AS cnt FROM CatD GROUP BY created_by) d
ON u.id = d.created_by
ORDER BY total_posts DESC
This return me total post count on top (irrespective of user post count) like below instead of post count for each user
username | user1 | user2 | user3
total_posts | 11020 (Total post count) | NULL | NULL | and so on
More Info:
SELECT created_by, COUNT(*) AS cnt FROM CatA GROUP BY created_by
This returns me:
created_by | 22 | 26 | 88 | 90
cnt | 6 | 20 | 15 | 8
So it seems like you have a separate table for each category (which is actually not the most optimal design by the way). What you can do to get the count of posts for all users is:
SELECT u.*,
a.cnt + b.cnt + c.cnt + d.cnt AS total_posts
FROM users u
LEFT JOIN (SELECT user_id, COUNT(*) AS cnt FROM CatA GROUP BY user_id) a
ON u.user_id = a.user_id
LEFT JOIN (SELECT user_id, COUNT(*) AS cnt FROM CatB GROUP BY user_id) b
ON u.user_id = b.user_id
LEFT JOIN (SELECT user_id, COUNT(*) AS cnt FROM CatC GROUP BY user_id) c
ON u.user_id = c.user_id
LEFT JOIN (SELECT user_id, COUNT(*) AS cnt FROM CatD GROUP BY user_id) d
ON u.user_id = d.user_id