Count number of Users with more then 3 posts - mysql

I have 2 tables Users and Posts and I need only number of users who have more than 3 posts and who have more than 5 posts.
I need something like this:
NumberOfUsers
Posts
555
>3
888
>5
SELECT
COUNT( u.Id)
FROM
Users u
INNER JOIN Posts p ON (u.Id=p.OwnerId)
HAVING COUNT(p.Id)>3
I try this but having count does not work I think.

You can use two levels of aggregation
select sum(case when cnt > 3 then 1 else 0 end) cnt_3,
sum(case when cnt > 5 then 1 else 0 end) cnt_5
from (select ownerid, count(*) cnt from posts group by ownerid) p
Note that you don't need to bring in the users table ; the posts table has all the information we need.
Depending on your database, syntax shortcuts might be available, as in MySQL :
select sum(cnt > 3) cnt_3,
sum(cnt > 5) cnt_5
from (select ownerid, count(*) cnt from posts group by ownerid) p
Or in Posgres :
select count(*) where(cnt > 3) cnt_3,
count(*) where(cnt > 5) cnt_5
from (select ownerid, count(*) cnt from posts group by ownerid) p

Related

How can I have multiple mysql aggregate columns

I have the table structure below
I want to be able to select the following groups
farmers who grow between 1 and 3 commodities
farmers who grow between 4 and 6 commodities
farmers who grow more than 6 commodities
My resultant query should look like below
I am completely lost as to how to go about this query. I tried
SELECT count(*) AS total,
(SELECT count(farmer_id) from farmer_commodities HAVING count(commodity_id) < 3) AS grow1_3,
(SELECT count(farmer_id) from farmer_commodities HAVING count(commodity_id) BETWEEN 4 AND 6) AS grow4_6,
(SELECT count(farmer_id) from farmer_commodities HAVING count(commodity_id) > 6) as grow_above_6
from farmer_commodities
Try this
With data as (
SELECT farmer_id,
count(commodity_id) cnt
From table group by
farmer_id)
Select
count(*),
Count(distinct case when cnt
< 3 then farmer_id end),
Count(distinct case when cnt
BETWEEN 4 AND 6 then farmer_id end),
Count(distinct case when cnt> 6 then farmer_id end) from
Data;
Can you do this:
select grow1_3_commodities, grow4_6_commodities, grow_above_6_commodities from (
(select farmer_id, count(commodity_id) as grow1_3_commodities from farmer_commodities group by farmer_id HAVING count(commodity_id) < 3) AS grow1_3
join (select farmer_id, count(commodity_id) as grow4_6_commodities from farmer_commodities group by farmer_id HAVING count(commodity_id) BETWEEN 4 AND 6) AS grow4_6
join (SELECT farmer_id, count(farmer_id) as grow_above_6_commodities from farmer_commodities group by farmer_id HAVING count(commodity_id) > 6) as grow_above_6)
where grow1_3.farmer_id = grow4_6.farmer_id and grow4_6.farmer_id = grow_above_6.farmer_id);

Count Group By and Separate If Included in Both Group

Not sure if this question is duplicated yet or not.
I have a simplified table below
User
Interest
Jason
Art
Jason
Sport
Sam
Sport
Sam
Art
Steve
Sport
Desmond
Sport
Tania
Art
Here's the result that I want to achieve
Interest
Count
Art
2
Sport
2
Both
2
I Managed to make a subquery to achieve the value for the Both data by this query
SELECT COUNT(USER) FROM (
SELECT User, COUNT(DISTINCT Interest) as interest_type FROM table WHERE interest_type = 2)
But for the user that are exclusively have Interest in Art and in Sport it's not separated.
You could use conditional aggregation here:
WITH cte AS (
SELECT User,
CASE WHEN COUNT(CASE WHEN Interest = 'Art' THEN 1 END) > 0 AND
COUNT(CASE WHEN Interest = 'Sport' THEN 1 END) > 0
THEN 'Both'
WHEN COUNT(CASE WHEN Interest = 'Art' THEN 1 END) > 0
THEN 'Art'
ELSE 'Sport' END AS Interest
FROM yourTable
GROUP BY User
)
SELECT Interest, COUNT(*) AS Count
FROM cte
GROUP BY Interest;
On MySQL or BigQuery, we can shorten the above to:
WITH cte AS (
SELECT User,
CASE WHEN SUM(Interest = 'Art') > 0 AND SUM (Interest = 'Sport') > 0
THEN 'Both'
WHEN SUM(Interest = 'Art') > 0
THEN 'Art'
ELSE 'Sport' END AS Interest
FROM yourTable
GROUP BY User
)
SELECT Interest, COUNT(*) AS Count
FROM cte
GROUP BY Interest;
Assuming your database supports the over() clause:
select
case when num_interests = 1 then interest else 'both' end as interest
, count(distinct user) as "Count"
from (
select
interest
, user
, count(*) over(partition by user) as num_interests
from yourTable
) d
group by
case when num_interests = 1 then interest else 'both' end

Combining several similar queries

How can I combine these three queries into one?
SELECT COUNT(*)
FROM Users
WHERE (SELECT COUNT(*) FROM Posts WHERE Posts.OwnerUserId = Users.Id) < 10;
SELECT COUNT(*)
FROM Users
WHERE (SELECT COUNT(*) FROM Posts WHERE Posts.OwnerUserId = Users.Id) BETWEEN 10 AND 20;
SELECT COUNT(*)
FROM Users
WHERE (SELECT COUNT(*) FROM Posts WHERE Posts.OwnerUserId = Users.Id) > 20;
If I follow you correctly, you can use two levels of aggregation. The following query puts each bucket in a separate row:
select
case
when cnt < 10 then '< 10'
when cnt < 20 then '10-20'
else '> 20'
end as bucket,
count(*) cnt
from (
select count(p.owneruserid) cnt
from users u
left join posts p on p.owneruserid = u.id
group by u.id
) t
group by case
when cnt < 10 then '< 10'
when cnt < 20 then '10-20'
else '> 20'
end
Or you can get all three counts on the same row as follows:
select
sum(case when cnt < 10 then 1 else 0 end) as cnt_less_than_10,
sum(case when cnt >= 10 and cnt < 20 then 1 else 0 end) as cnt_10_to_20,
sum(case when cnt > 20 then 1 else 0 end) as cnt_more_than_20
from (
select count(*) cnt
from users u
inner join posts p on p.owneruserid = u.id
group by u.id
) t

MySql GROUP BY Max Date

I have a table called votes with 4 columns: id, name, choice, date.
****id****name****vote******date***
****1*****sam*******A******01-01-17
****2*****sam*******B******01-05-30
****3*****jon*******A******01-01-19
My ultimate goal is to count up all the votes, but I only want to count 1 vote per person, and specifically each person's most recent vote.
In the example above, the result should be 1 vote for A, and 1 vote for B.
Here is what I currently have:
select name,
sum(case when uniques.choice = A then 1 else 0 end) votesA,
sum(case when uniques.choice = B then 1 else 0 end) votesB
FROM (
SELECT id, name, choice, max(date)
FROM votes
GROUP BY name
) uniques;
However, this doesn't work because the subquery is indeed selecting the max date, but it's not including the correct choice that is associated with that max date.
Don't think "group by" to get the most recent vote. Think of join or some other option. Here is one way:
SELECT v.name,
SUM(v.choice = 'A') as votesA,
SUM(v.choice = 'B') as votesB
FROM votes v
WHERE v.date = (SELECT MAX(v2.date) FROM votes v2 WHERE v2.name = v.name)
GROUP BY v.name;
Here is a SQL Fiddle.
Your answer are close but need to JOIN self
Subquery get Max date by name then JOIN self.
select
sum(case when T.vote = 'A' then 1 else 0 end) votesA,
sum(case when T.vote = 'B' then 1 else 0 end) votesB
FROM (
SELECT name,Max(date) as date
FROM T
GROUP BY name
) AS T1 INNER JOIN T ON T1.date = T.date
SQLFiddle
Try this
SELECT
choice,
COUNT(1)
FROM
votes v
INNER JOIN
(
SELECT
id,
max(date)
FROM
votes
GROUP BY
name
) tmp ON
v.id = tmp.id
GROUP BY
choice;
Something like this (if you really need count only last vote of person)
SELECT
sum(case when vote='A' then cnt else 0 end) voteA,
sum(case when vote='B' then cnt else 0 end) voteB
FROM
(SELECT vote,count(distinct name) cnt
FROM (
SELECT name,vote,date,max(date) over (partition by name) maxd
FROM votes
)
WHERE date=maxd
GROUP BY vote
)
PS. MySQL v 8
select
name,
sum( case when choice = 'A' then 1 else 0 end) voteA,
sum( case when choice = 'B' then 1 else 0 end) voteB
from
(
select id, name, choice
from votes
where date = (select max(date) from votes t2
where t2.name = votes.name )
) t
group by name
Or output just one row for the total counts of VoteA and VoteB:
select
sum( case when choice = 'A' then 1 else 0 end) voteA,
sum( case when choice = 'B' then 1 else 0 end) voteB
from
(
select id, name, choice
from votes
where date = (select max(date) from votes t2
where t2.name = votes.name )
) t
Based on #d-shish solution, and since introduction (in MySQL 5.7) of ONLY_FULL_GROUP_BY, the GROUP BY statement must be placed in subquery like this :
SELECT v.`name`,
SUM(v.`choice` = 'A') as `votesA`,
SUM(v.`choice` = 'B') as `votesB`
FROM `votes` v
WHERE (
SELECT MAX(v2.`date`)
FROM `votes` v2
WHERE v2.`name` = v.`name`
GROUP BY v.`name` # << after
) = v.`date`
# GROUP BY v.`name` << before
Otherwise, it won't work anymore !

Count rows with DISTINCT(several columns) and MAX(another column)

My table contains votes of users for different items. It has the following columns:
id, user_id, item_id, vote, utc_time
Only id is a unique field and the combination of user_id and utc_time is probably also unique. But user can cast votes for any item many times.
A vote is not a number but rather has one of several possible values (e.g., "awful", "bad", "good", "excellent").
I need to count how many different users cast their last vote for a given #item# as "excellent", as "good", etc. So assuming I have only four different possible vote values, I need to get four records with the following fields:
vote, count_of_users
I understand how to count all votes, not only last votes of users:
SELECT vote, COUNT(id) FROM votes WHERE item_id=#item# GROUP BY vote;
But I cannot figure out how to count only the votes where utc_time = MAX(utc_time) for each user... Thanks for your help.
This question is connected to the previous question of mine: Select one row with MAX(column) for known other several columns without subquery
try this solution if it fits with you,
SELECT a.item_ID,
SUM(CASE WHEN a.vote = 'awful' THEN 1 ELSE 0 END) awful,
SUM(CASE WHEN a.vote = 'bad' THEN 1 ELSE 0 END) bad,
SUM(CASE WHEN a.vote = 'good' THEN 1 ELSE 0 END) good,
SUM(CASE WHEN a.vote = 'excellent' THEN 1 ELSE 0 END) excellent
FROM tableName a
INNER JOIN
(
SELECT user_ID, MAX(utc_time) max_time
FROM tableName
GROUP BY user_ID
) b ON a.user_ID = b.user_ID AND
a.utc_time = b.max_time
-- WHERE a.item_ID = 'valueHere'
GROUP BY a.item_ID
UPDATE 1
SELECT a.item_ID,
a.vote,
COUNT(*) totalCount
FROM tableName a
INNER JOIN
(
SELECT user_ID, MAX(utc_time) max_time
FROM tableName
WHERE item_id = 'valueHere'
GROUP BY user_ID
) b ON a.user_ID = b.user_ID AND
a.utc_time = b.max_time
GROUP BY a.vote