How can I use the MySQL COUNT() statement on a generated query? - mysql

I've created a query that finds the users of a specific site that have liked every photo on the site and stand out as potential bots.
I'm trying to count the number of bots.
My query currently returns all the bot usernames and the number of photos that they've liked, but I'm having trouble simply counting them.
It would be ideal to be able to COUNT(*) on the table that the below query generates.
SELECT
username,
COUNT(*) AS total_likes
FROM users
JOIN likes
ON likes.user_id = users.id
GROUP BY likes.user_id
HAVING total_likes = (SELECT COUNT(*) FROM photos);

You can do this with a subquery:
SELECT COUNT(*) as num_potential_bots
FROM (SELECT u.username, COUNT(*) AS total_likes
FROM users u JOIN
likes l
ON l.user_id = u.id
GROUP BY l.user_id
HAVING total_likes = (SELECT COUNT(*) FROM photos)
) u;

Related

mysql join table and search for most recent record on where clause

I have two tables
users: id, email, firstName, lastName
subscriptions: id, userId, currentPeriodStart, currentPeriodEnd
Below just shows you how the two tables are related. I want to return subscriptions that expire after 1565827199, but it needs to check against each user's most recent subscription.
select
u.id
from users u
join subscriptions s on u.id s.userId
where s.currentPeriodEnd > 1565827199
ORDER BY u.lastName ASC
A user may have multiple subscriptions in the subscriptions table. What I need to do is modify the query above, so it checks against that user's most recent subscription and not the first one it finds.
select * from subscriptions ORDER BY currentPeriodEnd DESC LIMIT 1
I've tried a few different things (alias table, sub query) I found elsewhere on stackoverflow without any luck.
You can filter with a correlated subquery, like so:
select u.*, s.*
from users u
inner join subscriptions s on u.id = s.userId
where s.currentPeriodEnd = (
select max(s1.currentPeriodEnd)
from subscriptions s1
where s1.userId = u.id and s1.currentPeriodEnd > 1565827199
)
order by u.lastName
For performance, consider an index on subscriptions(userId, currentPeriodEnd).
Alternatively, if you are running MySQL 8.0, you can use row_number():
select *
from (
select
u.*,
s.*,
row_number() over(partition by u.id order by s.currentPeriodEnd desc)
from users u
inner join subscriptions s on u.id = s.userId
where s.currentPeriodEnd > 1565827199
) t
where rn = 1
order by lastName
Join with a subquery that gets the latest time for each user, and filters it down to just the ones after your specified timestamp.
select u.id
from users u
join (
select userid
FROM subscriptions
GROUP BY userid
HAVING MAX(currentPeriodEnd) > 1565827199
) s ON s.userid = u.id
ORDER BY u.lastName ASC

Mysql count and return just one row of data

I need to count the amount of users that have have answered all of those 3 profile_options (so they have at least 3 records in the profile_answers table).
SELECT COUNT(DISTINCT(users.id)) users_count
FROM users
INNER JOIN profile_answers ON profile_answers.user_id = users.id
WHERE profile_answers.profile_option_id IN (37,86,102)
GROUP BY users.id
HAVING COUNT(DISTINCT(profile_answers.id))>=3
The problem is that this query is return a table with rows for each user and how many they answered (in this case always 3). What I need is to return just one row that has the total number of users (so the sum of all rows of this example)
I know how to do it with another subquery but the problem is that I am running into "Mysql::Error: Too high level of nesting for select"
Is there a way to do this without the extra subquery?
SELECT SUM(sum_sub.users_count) FROM (
(SELECT COUNT(DISTINCT(users.id)) users_count
FROM users
INNER JOIN profile_answers ON profile_answers.user_id = users.id
WHERE profile_answers.profile_option_id IN (37,86,102)
GROUP BY users.id
HAVING COUNT(DISTINCT(profile_answers.id))>=3)
) sum_sub
Please give this query a shoot
SELECT COUNT(DISTINCT(u.id)) AS users_count
FROM users AS u
INNER JOIN (
SELECT user_id, COUNT(DISTINCT profile_option_id) AS total
FROM profile_answers
WHERE profile_option_id IN (37,86,102)
GROUP BY users.id
HAVING COUNT(DISTINCT profile_option_id) = 3
) AS a ON a.user_id = u.id
If you have lots of data in your tables, you will get a better/faster performance by using temporary tables like so
CREATE TEMPORARY TABLE a (KEY(user_id)) ENGINE = MEMORY
SELECT user_id, COUNT(DISTINCT profile_option_id) AS total
FROM profile_answers
WHERE profile_option_id IN (37,86,102)
GROUP BY users.id
HAVING COUNT(DISTINCT profile_option_id) = 3;
Then your final query will look like this
SELECT COUNT(DISTINCT(u.id)) as users_count
FROM a
INNER JOIN on a.user_id = u.id
Unless there is a need to join the users table you can go with this
SELECT COUNT(*) AS users_count
FROM (
SELECT user_id, COUNT(DISTINCT profile_option_id) AS total
FROM profile_answers
WHERE profile_option_id IN (37,86,102)
GROUP BY users.id
HAVING COUNT(DISTINCT profile_option_id) = 3
) AS a
Should you need another solution, please consider providing us you EXPLAIN EXTENDED for the query and the table definitions along with a better problem description.
I hope this helps
You can give the queries a name using the AS clause. See the updated query below.
SELECT SUM(sum_sub.users_count) FROM (
(SELECT COUNT(DISTINCT(users.id)) as users_count
FROM users
INNER JOIN profile_answers ON profile_answers.user_id = users.id
WHERE profile_answers.profile_option_id IN (37,86,102)
GROUP BY users.id
HAVING COUNT(DISTINCT(profile_answers.id))>=3)
) as sum_sub
You should not group by on a field not present in select statement.
select id, count(*) from users group by id is fine
select count(id) from users group by id is NOT
Regarding your query I think the link to user table is not necessary. Just using foreign key should be fine.
Try this one:
select count(*) from
(SELECT users_id count(*) as cnt
FROM profile_answers
INNER JOIN users ON profile_answers.user_id = users.id
WHERE profile_answers.profile_option_id IN (37,86,102)
group by users_id
having count(*) >3)

mysql average count from related table

I have two tables:
users table
list of users
stories
list of stories - multiple stories per user
I want to know the average number of stories a user has. (not a specific user, for all users)
Expected results: 2.3 average stories per user
Tried:
select avg(w) from (select count(distinct user_id) as w from stories group by user_id) a;
above ran but didn't seem correct
also:
SELECT user_id, ( SELECT COUNT(*) FROM stories w WHERE w.user_id = u.user_id ) as TotalStories FROM user u;
returned average stories for each user, not average for overall
First you need to know the number of stories per user:
select count(s.id) as n
from users u left outer join stories s on u.id = s.user_id
group by u.id
Then just apply avg to that:
select avg(n)
from (
select count(s.id) as n
from users u left outer join stories s on u.id = s.user_id
group by u.id
) as dt
SELECT COUNT(userid) AS totalusers, SUM(storycount) AS totalstories, SUM(storycount) / COUNT(userid) AS average_stories
FROM (
SELECT users.id AS userid, COUNT(stories.id) AS storycount
FROM users
LEFT JOIN stories ON (users.id = stories.user_id)
GROUP BY users.id
) AS child
Inner query does the per-user story counting. Outer query counts the total users, total stories, and the stories per user average.

MySQL reference row in sub query

I'm trying to count all messages sent by users AFTER they uploaded a photo.
I'm trying something like this.
select messages.created_at, count(*) as count from messages
inner join users on messages.user_id = users.id
inner join photos on photos.user_id = users.id
where
some_users_messages.created_at > some_users_first_photo.created_at
group by YEARWEEK(messages.created_at)
I'm thinking this needs to be a subquery? I'm not sure how to do this concept of one particular user's messages/photos in MySQL. Any ideas?
Thanks!
This would count the number of messages sent after the first photo per user:
select messages.user_id
, count(*) as count
from messages
where messages.created_at >
(
select min(created_at)
from photos
where photos.user_id = messages.user_id
)
group by
messages.user_id

mysql select top users problem

i have users table and i have posts table i want select from users the top users that have the big amount of posts from posts table and order them by numbers of posts
i can make it by array_count_values() by i cant order it
now i think if i make it by one mysql query by left and join will be more better
table structure
posts
id | auther_id
i tried this
SELECT COUNT(1) cnt, u.user_id
FROM users u
LEFT JOIN posts p
ON p.author_id=u.user_id
GROUP BY u.user_id
ORDER BY cnt DESC
LIMIT 20
it gave me this
alt text http://img511.imageshack.us/img511/6707/31154352.gif
see the arrow
what is this
i just have 2 posts under user_id 5
what is this first row
You need to aggregate the posts by user using GROUP BY u.user_id, get a COUNT value for the number of posts and ORDER BY that number, in descending order:
SELECT COUNT(1) cnt, u.user_id
FROM users u
LEFT JOIN posts p
ON p.author_id=u.user_id
GROUP BY u.user_id
ORDER BY cnt DESC
LIMIT 20
SELECT u.user_id, COUNT(*) as post_count
FROM users u
INNER JOIN posts p
USING (user_id)
GROUP BY u.user_id
ORDER BY post_count
i used this and its worked
is it true
SELECT COUNT( 1 ) cnt, a.auther_id
FROM `posts` a
LEFT JOIN users u ON a.auther_id = u.id
GROUP BY a.auther_id
ORDER BY cnt DESC
LIMIT 20