SQL left join two times - mysql

The user table looks like this:
user_id
name
surname
1
a
aa
2
b
bb
3
c
cc
The book's table looks like this:
user_id
book_name
1
book1
1
book2
1
book3
2
book1
The expenses table looks like this:
user_id
amount_spent
date
1
10
2020-02-03
1
30
2020-02-02
1
10
2020-02-01
1
15
2020-01-31
1
13
2020-01-15
2
15
2020-02-01
3
20
2020-02-01
The result which I want:
CountUsers
amount_spent
2
65
Explanation: I want to count how many users have book1 and how much total they spend on a date between 2020-02-01 - 2020-02-03.
Now how the query should look like?
I am using MySQL version 8.
I have tried:
SELECT
count(*), sum(amount_spend) as total_amount_spend
FROM
(select sum(amount_spend) as amount_spend
FROM expanses
LEFT JOIN books ON books.user_id = expanses.user_id WHERE books.book_name ='book1 GROUP BY expanses.user_id) src'
And the result is wrong because I am getting a higher amount_spend than in my table result above. I think while joining the table there are some duplicates but I do not know how to fix them.

I want to count how many users have book1 and how much total they spend on a date between 2020-02-01 - 2020-02-03.
I am thinking:
select count(*), sum(e.amount_spent)
from user_books ub join
expenses e
on ub.user_id = e.user_id
where book_name = 'book1';
Note: This assumes that user_books doesn't have duplicate rows.

FIDDLE
You miss the date part in your code.
SELECT
count(*), sum(amount_spent) as total_amount_spend
FROM
(select sum(amount_spent) as amount_spent
FROM expanses
LEFT JOIN books ON books.user_id = expanses.user_id
WHERE books.book_name ='book1'
and expanses.date between '2020-02-01' and '2020-02-03'
GROUP BY expanses.user_id) src;
will do a job.
Please note that you don't need to have left join here (unless you're sure that it may happen that no expenses at all for given user will be), and you don't need to have grouping in subquery. So your query could look like:
select count(distinct expanses.user_id), sum(amount_spent) as amount_spent
from expanses
inner join books on books.user_id = expanses.user_id
where books.book_name ='book1'
and expanses.date between '2020-02-01' and '2020-02-03';

Related

SQL Inner Join only one row based on filtered records

I have the following tables
user
id
name
1
John
2
Jack
3
George
essay
id
date_submitted
user_id
project_id
1
2020-12-10
1
1
2
2020-05-01
2
2
3
2020-04-10
3
2
project_phase
id
project_id
date
phase
1
1
2020-01-01
early
2
1
2020-05-31
mid
3
1
2020-11-30
late
4
2
2020-01-01
early
5
2
2020-03-31
mid
6
2
2020-04-30
late
1 User submits only 1 essay in one project
and I want to have a combined table which shows me the user and the phase at which the essay was submitted.
I'm trying to get the record with the MAX date from project_phase that is below the date_submitted
So using the above data I want to get
user
date_submitted
phase
John
2020-12-10
late
Jack
2020-05-01
late
George
2020-04-10
mid
so far I combined the tables with INNER JOIN but the following query returns duplicate records. I also tried ROW_NUMBER() and MAX() but I didn't structure it correctly to get the answer I'm looking for
SELECT
U.name,
E.date_submitted,
P.phase
FROM
essay E
INNER JOIN user U ON U.id = E.user_id
INNER JOIN project_phase P ON P.project_id = E.project_id and E.date <= P.date_submitted
Any help appreciated, thanks.
I would suggest a correlated subquery:
SELECT U.name, E.date_submitted,
(SELECT P.phase
FROM project_phase P
WHERE P.project_id = E.project_id AND
P.date <= E.date_submitted
ORDER BY p.date DESC
LIMIT 1
) as phase
FROM essay E JOIN
user U
ON U.id = E.user_id;

How do I get the MAX date of foreign table in MySQL and return only the row with that data without mixing records?

I have a few MySQL tables from which I need to JOIN and return data. The return data must show only one row for one of the JOINed tables, but MySQL mixes the rows.
I have tried different methods using subqueries and normal JOIN with GROUP but the results remain pretty much the same.
Example table structure
suppliers
id name ...
1 ACME ...
2 EMCA ...
3 ORG ...`
ratings
id supplier_id rating expiry_date report_file
1 1 5.0 2017-01-31 a.pdf
3 1 7.9 2019-06-30 c.pdf
4 2 5.0 2016-01-31 d.pdf
5 2 2.0 2018-11-30 g.pdf
6 245 9.5 2009-03-31 p.pdf
spends
id report_id supplier_id amount
1 1 1 150.00
2 1 2 100.00
3 1 245 200.00
Here are example queries I have tried to resolve this and return the correct dataset with no luck.
SELECT
reports.id,
suppliers.id AS supplier_id,
suppliers.name,
...
spends.amount,
...
ratings.rating,
ratings.report_file,
ratings.expiry_date
FROM reports
INNER JOIN spends ON reports.id=spends.report_id
INNER JOIN suppliers ON spends.supplier_id=suppliers.id
LEFT JOIN (
SELECT id,
level,
report_file,
supplier_id,
MAX(expiry_date) AS expiry_date
FROM ratings
GROUP BY supplier_id
) ratings ON (ratings.supplier_id=suppliers.id
AND ratings.expiry_date >= reports.period_start)
...
WHERE reports.id = 1
GROUP BY spends.id
ORDER BY spends.amount DESC
Another query
SELECT
reports.id,
suppliers.id AS supplier_id,
suppliers.name,
...
spends.amount,
...
ratings.rating,
ratings.report_file,
MAX(ratings.expiry_date) AS expiry_date
FROM reports
INNER JOIN spends ON reports.id=spends.report_id
INNER JOIN suppliers ON spends.supplier_id=suppliers.id
LEFT JOIN ratings ON (ratings.supplier_id=suppliers.id
AND ratings.expiry_date >= reports.period_start)
...
WHERE reports.id = 1
GROUP BY spends.id
ORDER BY spends.amount DESC
I expect the results to be
id supplier_id name amount rating report_file expiry_date
1 1 ACME 150.00 7.9 c.pdf 2019-06-30
1 2 EMCA 100.00 2.0 g.pf 2018-11-30
1 245 MACE 200.00 null null```
However, the actual output is
```sql
id supplier_id name amount rating report_file expiry_date
1 1 ACME 150.00 5.0 a.pdf 2019-06-30
1 2 EMCA 100.00 5.0 d.pf 2018-11-30
1 245 MACE 200.00 null null
Please could anyone advise how I can fix this.
try a query like this:
SELECT
reports.id,
suppliers.id AS supplier_id,
suppliers.name,
...
spends.amount,
...
r.rating,
r.report_file,
r.expiry_date
FROM reports
INNER JOIN spends ON reports.id=spends.report_id
INNER JOIN suppliers ON spends.supplier_id=suppliers.id
LEFT JOIN ratings r ON r.supplier_id=suppliers.id
AND r.`expire_date` = (
SELECT MAX(`expire_date`) FROM ratings WHERE supplier_id = supplier.id GROUP BY supplier_id)
) as maxdate
...
WHERE reports.id = 1
GROUP BY spends.id
ORDER BY spends.amount DESC

Get most recent groups ordered by users last login date DESC

The title makes it sound easy but what I'd like to do is get the last 20 groups from the groups table, ordered by their corresponding users last login date. A group can have one or more users and ultimately what I want to do is find out which groups have had the least user activity and retrieve the last login date.
Here's the query I came up with -
SELECT DISTINCT g.name, user_max.max_login_last_at FROM groups g
LEFT JOIN group_user gu on g.id = gu.group_id
LEFT JOIN (
SELECT MAX(login_last_at) max_login_last_at, u.id
FROM users u GROUP BY id
) AS user_max ON (user_max.id = gu.user_id)
ORDER BY user_max.max_login_last_at ASC
The problem is when I perform the join, it pulls in every group_user record and results in duplicates. I feel like there could be an easy solution to this one but I can't seem to figure it out!
groups table
id name
1 Group 1
2 Group 2
users table
id email login_last_at
1 user1#example.com 2018-10-17 16:08:47
2 user2#example.com 2018-10-02 15:41:53
3 user3#example.com NULL
4 user4#example.com 2018-10-08 12:01:48
5 user5#example.com 2018-10-15 9:24:57
6 user6#example.com 2018-10-17 11:10:58
7 user7#example.com 2018-10-17 15:33:03
group_user table
id group_id user_id
1 1 1
2 2 1
3 1 2
4 1 3
5 1 4
6 2 5
7 1 5
8 2 6
9 1 7
Current example result -
name max_login_last_ts
Group 1 2018-10-02 15:41:53
Group 1 2018-10-08 12:01:48
Group 2 2018-10-15 09:24:57
Group 1 2018-10-15 09:24:57
Group 2 2018-10-17 11:10:58
Group 1 2018-10-17 15:33:03
Group 1 2018-10-17 16:08:47
Group 2 2018-10-17 16:08:47
Group 1 2018-10-18 08:55:17
The problem is as you can see in the result above is that I'm getting all groups, all I really want is the following -
name max_login_last_ts
Group 2 2018-10-17 16:08:47
Group 1 2018-10-18 08:55:17
Thanks in advance!
I think this query will do what you want. There isn't enough data in your sample to replicate your desired results though.
SELECT g.name, MAX(u.login_last_at) AS max_login_last_at
FROM `groups` g
JOIN group_user gu on gu.group_id = g.id
JOIN users u ON u.id = gu.user_id
GROUP BY g.name
ORDER By max_login_last_at DESC
LIMIT 20
Demo on dbfiddle

How to join 3 table with this condition?

I have 3 tables: product, customer and transaction.
Product:
id_product price
1 1000
2 2000
Customer:
id_customer name
1 Tom
2 Jack
Transaction:
id_transaction id_product id_customer qty date
1 1 1 10 2013-02-21
2 2 1 50 2013-02-21
3 1 2 15 2013-02-21
I want to achieve this result:
id_customer name total_transaction purchase_qty subtotal
1 Tom 2 60 110000
2 Jack 1 15 15000
How I can get that result using a query in MySQL?
SELECT t.id_customer, c.name,
COUNT(t.id_customer) AS total_transaction,
SUM(t.qty) as purchase_qty
FROM transaction t
INNER JOIN customer c
ON t.id_customer = c.id_customer
GROUP BY t.id_customer,c.name
SELECT cs.id_customer, cs.name, COUNT(tr.transaction) AS total_transaction, SUM(tr.qty) as purchase_qty, SUM(tr.qty*pr.prize)
FROM customer AS cs
LEFT JOIN transaction AS tr ON tr.id_customer = cs.id_customer
LEFT JOIN product AS pr ON pr.id_product = tr.id_product
GROUP BY cs.id_customer
I suppose you are a total beginner, so I did this for you. Next time, if you have ANY own ideas, give to us, so we don't have to write the whole query for you. Show some effort.
SELECT c.id_customer, c.name, count(t.id_transaction) AS total_transaction
FORM Customer c INNER JOIN Transaction T
ON C.id_customer = T.id_customer
GROUP BY c.id_customer
You need a group by statement since you want to aggregate results for a given customer :
SELECT id_customer, name, count(id_transaction) AS total_transaction
FORM Customer, Transaction
WHERE Transaction.id_customer = Customer.id_customer
GROUP BY id_customer
This does not solve your whole problem, but you've got the idea.

showing one column several times with data from 3 different date intervals

I am trying to show three different figures of the same column In a mysql query and joining all three tables, I would like to keep one month static: April, so it would be a case like this I want to show The current month, the previous month and the static month of the year I'm working with, in this case let us stick with 2012
table: persons
ID name
1 Carl
table: vehicle
ID v_name person_veh
100 Dodge Viper 1
Table:payment
pay_id , pay_date, amount person_id
1 2012-02-12 1000 1
2 2012-03-11 780 1
3 2012-04-15 890 1
4 2012-05-12 1200 1
5 2012-06-12 1890 1
6 2012-07-12 1350 1
7 2012-08-12 1450 1
So what I want to do is show the column amount for the month of April as I said I want to keep that row static: 890, the current month lets say the current month is August:1450 and the previous month amount which would be July:1350: so the final result would be something like this:
name v_name april_amount current_month_amount previous_month_amount
Carl Dodge viper 890 1450 1350
You can use the following - which uses an aggregate function with a CASE statement:
select p.name,
v.v_name,
sum(case when Month(py.pay_date) = 4 then amount end) april_amount,
sum(case when Month(py.pay_date) = Month(curdate())
then amount end) current_month_amount,
sum(case when Month(py.pay_date) = Month(curdate())-1
then amount end) previous_month_amount
from persons p
left join vehicle v
on p.id = v.person_veh
left join payment py
on p.id = py.person_id
group by p.name,
v.v_name
see SQL Fiddle with Demo
Try
SELECT a.name, b.pay_date, b.amount, b.person_id, c.v_name
FROM persons a
LEFT JOIN payment b on a.id = b.person_id
LEFT JOIN vehicle c on a.id = c.person_veh
This query assumes id, person_veh and person_id are the like columns which links this user across these three tables