mysql multiple COUNT() from multiple tables with LEFT JOIN - php

I want to show the conclusion of all users.
I have 3 tables.
table post
post_id(index) user_id
1 1
2 3
3 3
4 4
table photo
photo_id(index) user_id
1 2
2 4
3 1
4 1
table video
photo_id(index) user_id
1 4
2 4
3 3
4 3
and in table user
user_id(index) user_name
1 mark
2 tommy
3 john
4 james
in fact, it has more than 4 rows for every tables.
I want the result like this.
id name post photo videos
1 mark 1 2 0
2 tommy 0 1 0
3 john 2 0 2
4 james 1 1 2
5 .. .. .. ..
Code below is SQL that can work correctly but very slow, I will be true appreciated if you help me how it using LEFT JOIN for it. Thanks.
SQL
"select user.*,
(select count(*) from post where post.userid = user.userid) postCount,
(select count(*) from photo where photo.userid = user.userid) photoCount,
(select count(*) from video where video .userid = user.userid) videoCount
from user order by user.id"
(or ORDER BY postCount, photoCount or videoCount ASC or DESC as i want )
I done researched before but no any helped me.

SELECT u.user_id,
u.user_name,
COUNT(DISTINCT p.post_id) AS `postCount`,
COUNT(DISTINCT ph.photo_id) AS `photoCount`,
COUNT(DISTINCT v.video_id) AS `videoCount`
FROM user u
LEFT JOIN post p
ON p.user_id = u.user_id
LEFT JOIN photo ph
ON ph.user_id = u.user_id
LEFT JOIN video v
ON v.user_id = u.user_id
GROUP BY u.user_id
ORDER BY postCount;
Live DEMO

Your method of doing this is quite reasonable. Here is your query:
select user.*,
(select count(*) from post where post.userid = user.userid) as postCount,
(select count(*) from photo where photo.userid = user.userid) as photoCount,
(select count(*) from video where video.userid = user.userid) as videoCount
from user
order by user.id;
For this query, you want the following indexes:
post(userid)
photo(userid)
video(userid)
user(id)
You probably already have the last one, because user.id is probably the primary key of the table.
Note that a left join approach is a bad idea in this case. The three tables -- posts, photos, and videos -- are independent of each other. If a user has five of each, then joining them together would produce 125 intermediate rows. If a user has fifty of each, it would be 125,000 -- a lot of extra processing.

Your answer is probably slow as it is using a correlated sub-query i.e. the sub query is running once for each user_id (unless the optimizer is doing something smart - which shouldn't be counted on).
You could use a left outer join and count or use something temporary like:
SELECT u.user_id,
u.user_name,
ph.user_count AS 'photoCount',
p.user_count AS 'postCount',
v.user_count AS 'videoCount'
FROM user u
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM photo
GROUP BY user_id
) ph
ON ph.user_id=u.user_id
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM post
GROUP BY user_id
) p
ON p.user_id=u.user_id
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM video
GROUP BY user_id
) v
ON v.user_id=u.user_id
There are pros and cons for both (depending on indexes). Always have a look at the query plan (using EXPLAIN for MySQL).

Related

How to select latest records from multiple tables Mysql Php

I have three tables and i want to get records according to datetime for all tables,
Here is my table "users"
id name
1 abc
2 xyz
Here is my table "paymentHistory"
id bookingId userId createdOn
1 101 1 2020-07-10 12:11:14
2 102 1 2020-07-11 10:31:19
3 105 1 2020-07-11 12:31:19
4 109 2 2020-07-10 11:45:32
Here is my table "cancelPaymentHistory"
id bookingId userId createdOn
1 103 1 2020-07-07 11:31:28
2 100 1 2020-07-11 11:31:28
3 109 2 2020-07-08 19:28:41
Here is my table "usr_booking"
id bookingId userId status created_on
1 104 1 Inprocess 2020-07-07 10:31:28
Now i want to get all record where userId='1' and according to createdOn (DESC) of three tables
How can i do this ?
I tried with following code but showing so many records( showing wrong result)
SELECT u.id as userId,ph.bookingId as paymentHistoryBooking,cph.bookingId as CancelBookingId,ub.bookingId as usrBooking
FROM users u
JOIN paymentHistory ph ON u.id=ph.userId
JOIN cancelPaymentHistory cph ON u.id=cph.id
JOIN usr_booking ub ON u.id=ub.userId
WHERE u.id='152'
Your query is not correct.
First you miss the alias u of the users table so the second row must look like:
FROM users u
Second, in the the fourth row you accidently wrote u.id = cph.id, but you want to match with the userId, not the cancelPaymentHistory.id. Additionally you miss the alias for the cancelPaymentHistory table. So the correct fourth line must look like:
JOIN cancelPaymentHistory cph ON u.id = cph.userId
The whole query would look like:
SELECT u.id as userId,ph.bookingId as paymentHistoryBooking,cph.bookingId as CancelBookingId,ub.bookingId as usrBooking
FROM users u
JOIN paymentHistory ph ON u.id=ph.userId
JOIN cancelPaymentHistory cph ON u.id=cph.userId
JOIN usr_booking ub ON u.id=ub.userId
WHERE u.id='152';
To order your result by the latest createdOn of all tables, you must first identify the latest createdOn of all tables:
SELECT userId, max(createdOn) as latestActivity
(SELECT userId, max(createdOn) as createdOn
FROM cancelPaymentHistory
UNION
SELECT userId max(createdOn) as createdOn
FROM usr_booking
UNION
SELECT userId max(createdOn) as createdOn
FROM paymentHistory)
This result must be joined with the prior query:
SELECT u.id as userId,ph.bookingId as paymentHistoryBooking,cph.bookingId as CancelBookingId,ub.bookingId as usrBooking
, max(latestActivity)
FROM users u
JOIN paymentHistory ph ON u.id=ph.userId
JOIN cancelPaymentHistory cph ON u.id=cph.userId
JOIN usr_booking ub ON u.id=ub.userId
JOIN (SELECT userId, max(createdOn) as latestActivity
FROM (SELECT userId, max(createdOn) as createdOn
FROM cancelPaymentHistory
UNION
SELECT userId, max(created_on) as created_on
FROM usr_booking
UNION
SELECT userId, max(createdOn) as createdOn
FROM paymentHistory) a) activity ON activity.userId = u.id
WHERE u.id='1'
ORDER BY activity.latestActivity DESC;
Note, that I still got the ORDER BY in the query. I am assuming you want to run this statement for multiple users, so you can modify the WHERE-Statement like
WHERE u.id IN ('1','2','3') and still have a sorted list.
I have set up a fiddle for you http://sqlfiddle.com/#!9/ea835a/28/0

Count in Joins in SQL

i have two tables (post.id is primary key which become seconday key in like.post_id )
table "post"
id user_id image
1 10 abc.jpg
2 20 xyz.jpg
3 10 ajb.jpg
Table "Like"
id user_id post_id likes
1 10 1 1
2 20 2 1
3 10 1 1
4 10 1 1
3 10 3 1
now i want whenever i pass user_id then i wan to get all post of users with number of likes of posts
i tried with following code but not worked,
SELECT selfie_info.id,selfie_info.user_id,selfie_info.image, (SELECT COUNT(m.likes)FROM post_likes m WHERE m.user_id='10') as total_likes FROM selfie_info where user_id='10'
how can i do this ? i want result like following (if i pass user_id=10 )
user_id post_id likes
10 1 3
10 3 1
SELECT p.user_id, p.id AS post_id, COUNT(l.id) AS total_likes
FROM post p
LEFT JOIN likes l ON l.post_id =p.id
WHERE p.user_id=10 GROUP BY p.id;
You can use aggregation:
select pl.post_id, count(*) as numlikes,
si.*
from selfie_info si join
post_likes pl
on si.user_id = pl.user_id
where si.user_id = 10
group by si.user_id, si.post_id
You can use si.* in the select, even though this is a group by query because you are aggregating by the primary key/unique id in the table.
You have all necessary data in table "like"
You can use one select from "like" table.
select user_id, post_id, sum(likes) as likes from like where user_id = 10;
You need to count or sum likes?
Depends on you needs You can sum(likes) or count(*) likes

Select each first record with id in another table's result

I have two tables, Users(id, username) and Posts(id, user_id, content). I want to list a summary of them which, I want to list all users and the first post of each user. How can I realize this in one query?
I tried something like.
QUERY
SELECT Users.*, Posts.content
FROM Users, Posts
WHERE Posts.user_id=T_Users.id
But it will return all posts for each user (I cannot add LIMIT 1 at the tail of course which only returns one user).
Some sample records:
Users table:
id username
1 test1
2 test2
Posts table:
id user_id content
1 1 This is a test1's content.
2 1 This is another test1's content.
3 2 This is a test2's content.
And I want the result:
Users.id Users.username Posts.content
1 test1 This is a test1's content.
2 test2 This is a test2's content.
Here is one approach to get the latest record per user i assume the latest record will be considered as the minimum post id
SELECT u.*, p.content
FROM Users u
join Posts p on p.user_id=u.id
join (select user_id ,min(id) id from Posts group by user_id ) p1
on (p.id = p1.id and p.user_id = p1.user_id )
Demo
Try this
select min(x.id) as Id,x.user_id,x.content from(
select p.id,p.user_id,p.content from Users u inner join Posts p
on u.id=p.user_id and u.gender=1
)x group by x.user_id
Have you tried group by clause.
SELECT Users.*, Posts.content
FROM Users, Posts
WHERE Posts.user_id=T_Users.id GROUP BY Posts.user_id

MySQL: Get all users not a part of a group

I'm trying to get my head around this query but one of the records still keeps popping up. In summary, my intention is to show group members on the left and names from a phonebook which are not included in the group on the right. That way the user can select them and add them to the group.
user_id firstname group_id grpname
------- --------- -------- ----------
1 Luker 3 Abc
2 John 1 Some Group
3 Sam 2 Awesome Group
4 Mitch 1 Some Group
4 Mitch 2 Awesome Group
5 Rocky (NULL) (NULL)
6 Pops (NULL) (NULL)
The unique thing is that if one of the users is a part of multiple groups (user_id 4), their name should not be shown in the phonebook at all since it would already be placed in the list of existing members.
-- Query for group_id 2
SELECT user.id user_id, user.firstname, grp.id group_id, grp.grpname FROM agi_user user
LEFT JOIN agi_group_user gu ON user.id = gu.user_id
LEFT JOIN agi_groups grp ON gu.group_id = grp.id
WHERE grp.id IS NULL OR grp.id != 2
GROUP BY user.id
but for some reason the user Mitch still keeps popping up.
user_id firstname group_id grpname
------- --------- -------- ----------
1 Luker 3 Abc
2 John 1 Some Group
4 Mitch 1 Some Group
5 Rocky (NULL) (NULL)
6 Paps (NULL) (NULL)
Edit:: The output I need is
user_id firstname group_id grpname
------- --------- -------- ----------
1 Luker 3 Abc
2 John 1 Some Group
5 Rocky (NULL) (NULL)
6 Paps (NULL) (NULL)
Basically, to I want to get all users not a part of that group including NULL. But since one user is a part of multiple groups, her record still comes up when it shouldn't because that other group probably has a group_id of 3 or 4 (or any other besides 2).
You can use NOT EXIST to check if there are no group/user rows at all for group 2 and the given user:
SELECT
user.id user_id,
user.firstname,
grp.id group_id,
grp.grpname
FROM
agi_user user
LEFT JOIN agi_group_user gu ON user.id = gu.user_id
LEFT JOIN agi_groups grp ON gu.group_id = grp.id
WHERE
NOT EXISTS (
SELECT 'x' FROM agi_group_user x
WHERE
x.user_id = user.user_id and
x.group_id = 2)
GROUP BY
user.id
Note that if a user is not in group 2, but is in two other groups, he will show up 1 time in this result and only one of the groups he is in is returned. To fix that, you can add grp.id to the GROUP BY clause, or maybe you can use GROUP_CONCAT to return a list of group names in a single field.
Alternatively, this should work too, and in MySQL it might even perform better since it sucks at subqueries. Personally I think it is semantically less clear what is going on, though.
It joins the user/groups table a second time, but adds the group_id (2) to the join. If there are no rows returned for this table, then the user is not in group 2. The comment regarding GROUP_CONCAT applies to this query as well.
SELECT
user.id user_id,
user.firstname,
grp.id group_id,
grp.grpname
FROM
agi_user user
LEFT JOIN agi_group_user gu ON user.id = gu.user_id
LEFT JOIN agi_groups grp ON gu.group_id = grp.id
LEFT JOIN agi_group_user x ON user.id = x.user_id and x.group_id = 2
WHERE
x.group_id IS NULL
GROUP BY
user.id
If I understood correctly, you want this: "All users that are not in group 2"
This is called an anti-semijoin (or just anti-join) and can be done with a LEFT JOIN /IS NULL query:
SELECT u.id user_id, u.firstname
FROM agi_user AS u
LEFT JOIN agi_group_user AS gu
ON u.id = gu.user_id
AND gu.group_id = 2
WHERE gu.group_id IS NULL ;
or with a NOT EXISTS subquery:
SELECT u.id user_id, u.firstname
FROM agi_user AS u
WHERE NOT EXISTS
( SELECT 1
FROM JOIN agi_group_user AS gu
WHERE u.id = gu.user_id
AND gu.group_id = 2
) ;

adding and multiplying COUNT() of several tables

Is it possible to add and multiply the count of different tables where the id is the same?
Imagine:
Table_1 Table_2 Table_3
id id id
1 1 1
1 2 2
2 2 3
3 2 3
3 2 3
3 3 3
So that the end result would be this table with 2 columns:
id (COUNT(Table_1.id) + 2*COUNT(Table_2.id) + 3*COUNT(Table_3.id))
1 7
2 12
3 17
I don't know if I understood you correctly but give this a try,
SELECT a.ID,
a.aa + (2 * b.bb) + (3 * c.cc)
FROM
(
SELECT ID, COUNT(*) aa
FROM table1
GROUP BY ID
) a LEFT JOIN
(
SELECT ID, COUNT(*) bb
FROM table2
GROUP BY ID
) b ON a.ID = b.ID
LEFT JOIN
(
SELECT ID, COUNT(*) cc
FROM table3
GROUP BY ID
) c ON a.ID = c.ID
SQLFiddle Demo
SELECT id, counts_1.number + 2 * counts_2.number + 3 * counts_3.number
FROM
(SELECT id, COUNT(*) AS number FROM Table_1 GROUP BY id) AS counts_1
JOIN
(SELECT id, COUNT(*) AS number FROM Table_2 GROUP BY id) AS counts_2 USING (id)
JOIN
(SELECT id, COUNT(*) AS number FROM Table_3 GROUP BY id) AS counts_3 USING (id)
Note that this solution requires that every id exists at least once in each of the tables, otherwise it will be left out of the result. Changing this would require a FULL OUTER JOIN that MySQL is incapable of. There are ways around that limitation, though.

Categories