MySQL query with join + count for a specific user in DB - mysql

I want to get the sum of several entities from several tables using a query.
Specifically, I want the devices and messages count of a user. In addition, the timestamp of the last received message for that user.
I have 3 tables:
Users
id name (other fields)
1 Mike ...
2 John ...
3 Yay ...
4 Jim ...
Devices
id user_id (other fields)
1 1 ...
2 1 ...
3 1 ...
4 2 ...
Messages
id device_id message time (other fields)
1 1 Hi 2019-04-07 12:06:44 ...
2 1 Hey 2019-04-06 12:06:44 ...
3 2 Sup 2019-04-05 12:06:44 ...
4 3 Ok 2019-04-04 12:06:44 ...
5 4 Yay 2019-04-08 12:06:44 ...
... and, for example, for user Mike I want to end up with:
Result
nDevices nMessages time
3 4 2019-04-07 12:06:44
Any ideas?
Thanks in advance.

Join the 3 tables and count distinct values of the columns:
select
count(distinct d.id) devicescounter,
count(distinct m.id) messagescounter,
max(m.time) lastmessagetime
from users u
left join devices d on u.id = d.user_id
left join nessages m on m.device_id = d.id
where u.name = 'Mike'
If you want the results for all users:
select
u.id, u.name,
count(distinct d.id) devicescounter,
count(distinct m.id) messagescounter,
max(m.time) lastmessagetime
from users u
left join devices d on u.id = d.user_id
left join nessages m on m.device_id = d.id
group by u.id, u.name

The easiest way to do this would be to get the number of messages and most recent message time for each device in a subquery.
SELECT u.id,
COUNT(d.id) AS num_devices,
SUM(messages) AS num_messages,
MAX(most_recent) AS most_recent_message
FROM users u
JOIN devices d ON d.user_id = u.id
JOIN (SELECT device_id,
COUNT() AS messages,
MAX(TIME) AS most_recent_message
FROM messages
GROUP BY device_id) m ON m.device_id = d.id
GROUP BY u.id

You could try using a join between user and device and a inner join with the subquery for message and device
select a.name, a.id, count(d.user_id), t.nDevice, t.max_time
from Users a
inner join device d on a.id = d.user_id
inner join (
select m.device_id, count(m.device_id) nDevice, max(m.time) max_time
from Messages m
group by m.device_id
) t on t.device_id = d.id
group by a.name, a.id

Related

Query to fetch most recent record when joining multiple tables

I have 3 tables (relevant columns only)
Users: userId, email
EventRegistrations: userId, eventId
Events: id, time
Am trying to fetch the most recent event a user attended. From my research, it seems I have to do a subquery on max(time) inside one of the joins, but have had no luck.
Sample data:
Users:
email userId
test#test.com 1
test2#test.com 2
EventRegistrations:
userId eventId
1 10
1 11
1 12
2 11
2 13
Events:
id time
10 2020-12-10
11 2020-12-11
12 2020-12-12
13 2020-12-13
Expected result:
email eventId
test#test.com 12
test2#test.com 13
Here's what I have so far:
select u.email, e.id
from users as u
left join EventRegistrations as er on e.ruserId = u.userId
left join Events as e on e.id = er.eventId
JOIN (
select MAX(time), er.userId
from Events as e
left join EventRegistrations as er on er.eventId = e.Id
group by er.userId
) as mostRecentEvent
ON mostRecentEvent.userId = er.id;
Previously I had
select u.email, e.id
from users as u
left join EventRegistrations as er on er.userId = u.userId
left join Events as e on e.id = er.EventId
group by u.userId
order by e.time desc
But the problem there is that Group By is applied before the sorting, hence why I thought I needed a subquery with max to fetch the most recent event.
You can use a correlated subquery:
select u.*,
(select e.eventid
from EventRegistrations er join
Events e
on er.eventid = e.id
where er.userid = u.id
order by e.time desc
limit 1
) as eventid
from users u

how to use select query in a Group_concat sub query in mysql

I have 3 tables:
tbl_user stores all user details (user_id,name,address)
user_id name address
1 a (shop) home
2 b (shop) bakerstreet
3 c (staff) wallstreet
4 d (staff) georgia
5 e (staff) sydney
tbl_user_group stores user type (user_id,user_type : 1=shop_owner,2=staff)
user_id user_type
1 1
2 1
3 2
4 2
5 2
tbl_user_association holds the shop_owner and staff relation (shop_owner_id, staff_id)
shop_owner_id staff_id
1 3
1 4
1 5
2 3
2 4
desired result
i want to display the list of staffs and the respective shops that they are associated with as follows:
user_id staff_name shop_owner
3 c a,b
4 d a,b
5 e a
I tried using the group_concat as mentioned here. The query is as follows:
SELECT
u.id AS user_id,
u.name AS staff_name,
(SELECT GROUP_CONCAT(u.name separator ',') FROM tbl_user u WHERE u.id = ua.shop_owner_id) AS
shop_owner
FROM tbl_user u
JOIN tbl_user_group ug ON u.id = ug.user_id
LEFT JOIN tbl_user_association ua ON u.id = ua.staff_id
WHERE ug.user_type = 2
GROUP BY u.id
But it returns single row of staffs as below. where have i gone wrong?
user_id staff_name shop_owner
3 c a
4 d a
5 e a
This is how I'd do it:
SELECT
u.user_id,
u.name,
GROUP_CONCAT( so.name )
FROM
tbl_user_group ug
INNER JOIN tbl_user u
ON ( ug.user_id = u.user_id )
INNER JOIN tbl_user_association ua
ON ( ua.staff_id = u.user_id )
INNER JOIN tbl_user so -- here join again the user table to get shop owners names
ON ( ua.shop_owner_id = so.user_id )
WHERE
ug.user_type = 2
GROUP BY
u.user_id;

SQL join and sum of items

There are two tables
users
+--+----+
|id|name|
+--+----+
1 A
2 B
orders
+--+--------+-------+-------+
|id|subtotal|created|user_id|
+--+--------+-------+-------+
1 10 1000001 1
2 20 1000002 1
3 10 1000003 2
4 10 1000005 1
The idea is to get AVG, SUM and the last created order from the users.
SELECT
users.name,
users.phone,
SUM(a.subtotal),
COALESCE(a.created, NULL)
FROM users
LEFT JOIN
(
SELECT
orders.id,
orders.subtotal,
orders.user_id,
orders.created
FROM
orders
JOIN(
SELECT MAX(i.created) created, i.user_id
FROM orders i
GROUP BY i.user_id
)AS j ON(j.user_id = orders.user_id AND orders.created = j.created) GROUP BY orders.user_id
) AS a ON users.id = a.user_id
GROUP BY users.id
For example the SQL request should return this:
+--+----+---+--------+
|id|name|sum|date |
+--+----+---+--------+
1 A 40 1000005
2 B 10 1000003
But the SQL above failed to calculate sum. What did i miss?
Your query seems way too complicated. How about this?
SELECT u.id, u.name, SUM(o.subtotal), MAX(o.created)
FROM users u LEFT JOIN
orders o
ON u.id = o.user_id
GROUP BY u.id, u.name;
In MySQL it is particularly important to avoid unnecessary subqueries in the FROM clause. These are actually materialized and that can impede the use of indexes for performance.

Double count within a table

I have a table users:
user_id name
1 John
2 Dan
3 Jane
4 Sophie
5 Jodie
I then have a table named associates:
user_id assoc_id
1 2
1 3
3 4
3 1
3 5
4 1
5 1
5 2
What I want to do is show how many associates each user has, or none
So, the results would show
user_id Name Number of Associates
1 John 2
2 Dan 0
3 Jane 3
4 Sophie 1
5 Jodie 2
What I'm trying works but does not show those with 0
Here's what I'm trying, how do I get the 0s?
SELECT u.user_id, u.name, count(a.user_id) as howmany from users u
join associates a on a.user_id = u.user_id
group by u.user_id order by u.user_id asc
You need a LEFT OUTER JOIN:
SELECT u.user_id, u.name, count(a.user_id) as howmany
FROM users u LEFT OUTER JOIN associates a
ON a.user_id = u.user_id
GROUP BY u.user_id
ORDER BY u.user_id ASC
The LEFT OUTER JOIN will include every rows of the first table even is they aren't present in the joined table.
Try this :
SELECT u.user_id,
u.name,
count(a.user_id) as howmany
FROM users u
LEFT OUTER JOIN associates a
ON a.user_id = u.user_id
GROUP BY u.user_id
ORDER BY u.user_id ASC
Try using a left join instead of a inner join, that should select the rows that do not have corresponding associates.
SELECT u.user_id, u.name, count(a.user_id) as howmany
from users u
left join associates a
on a.user_id = u.user_id
group by u.user_id
order by u.user_id asc
This will work with strict rules also (if i had not made any typo):
SELECT users.*, ISNULL(associatesCount.AssocCount, 0) AS AssocCount
FROM users
LEFT JOIN (
SELECT user_id, COUNT(*) AS AssocCount
FROM associates
GROUP BY user_id
) AS associatesCount
ON users.user_id = associatesCount.user_id

Left Join 2 tables on 1 table

It must be pretty easy, but i can't think of any solution nor can I find an answer somewhere...
I got the table 'users'
and one table 'blogs' (user_id, blogpost)
and one table 'messages' (user_id, message)
I'd like to have the following result:
User | count(blogs) | count(messages)
Jim | 0 | 3
Tom | 2 | 3
Tim | 0 | 1
Foo | 2 | 0
So what I did is:
SELECT u.id, count(b.id), count(m.id) FROM `users` u
LEFT JOIN blogs b ON b.user_id = u.id
LEFT JOIN messages m ON m.user_id = u.id
GROUP BY u.id
It obviously doesn't work, because the second left join relates to blogs not users. Any suggestions?
First, if you only want the count value, you could do subselects:
select u.id, u.name,
(select count(b.id) from blogs where userid = u.id) as 'blogs',
(select count(m.id) from messages where userid = u.id) as 'messages'
from 'users'
Note that this is just a plain sql example, I have no mysql db here to test it right now.
On the other hand, you could do a join, but you should use an outer join to include users without blogs but with messages. That would imply that you get several users multiple times, so a group by would be helpful.
If you use an aggregate function in a select, SQL will collapse all your rows into a single row.
In order to get more than 1 row out you must use a group by clause.
Then SQL will generate totals per user.
Fastest option
SELECT
u.id
, (SELECT(COUNT(*) FROM blogs b WHERE b.user_id = u.id) as blogcount
, (SELECT(COUNT(*) FROM messages m WHERE m.user_id = u.id) as messagecount
FROM users u
Why you code does not work
SELECT u.id, count(b.id), count(m.id)
FROM users u
LEFT JOIN blogs b ON b.user_id = u.id <<-- 3 matches multiplies # of rows *3
LEFT JOIN messages m ON m.user_id = u.id <<-- 5 matches multiplies # of rows *5
GROUP BY u.id
The count will be off, because you are counting duplicate items.
Simple fix, but will be slower than option 1
If you only count distinct id's, you will get the correct counts:
SELECT u.id, count(DISTNICT b.id), count(DISTINCT m.id)
FROM users u
LEFT JOIN blogs b ON b.user_id = u.id
LEFT JOIN messages m ON m.user_id = u.id
GROUP BY u.id