mysql query with count - mysql

I'm running into my own limits of MySQL query skills, so I hope some SQL guru can help out on this one. I have 2 tables:
Table "comments"
comment_ID | comment_post_ID
120 | 620
121 | 620
122 | 620
Table "comments_like"
like_ID | comment_ID
1 | 120
2 | 120
I will result the numbers of comment that have a like group by comment_post_ID:
COUNT(comments in comments like) | comment_post_ID
1 | 620

This query give the post_id and the no of like for its comment in comment_like table
SELECT comment_post_ID, count(like_ID)
from (select distinct comment_post_ID,like_ID
from comments_like cl , comments c
where cl.comment_ID= c.comment_ID ) as iq
group by comment_post_ID

I guess a subquery is the easiest here:
SELECT (SELECT COUNT(like_ID) FROM `comments_like` cl WHERE cl.comment_ID=c.comment_ID) AS comments_in_comments_like, c.comment_post_ID FROM `comments` c
This will produce count of 2 though for likes since comment id 120 has 2 likes in your example

select cmtLike.like_ID, commnts.comment_post_ID
from comments_like cmtLike left join comments commnts on cmtLike.comment_ID = commnts.comment_ID

If you need to count each comment listed in comments_like just once, then:
select comment_post_ID, count(distinct cl.comment_ID)
from comments c, comments_like cl
where c.comment_id = cl.comment_id
group by comment_post_ID

Related

Can this query, which groups users by amount of comments posted, be simplified?

Two tables are used in this query, and all that matters in the result is the number of users which have or haven't posted any comments so far. The table user of course has the column id, which is the foreign key in the table comment, identified by the column user_id.
The first super-simple query groups users by whether or not they have any comments so far. It outputs two rows (a row with the user count who have comments, and a row with the user count who have no comments), with two columns (number of users, and whether or not they have posted any comments).
SELECT
COUNT(id) AS user_count,
IF( id IN ( SELECT user_id FROM `comment` ), 1, 0) AS has_comment
FROM `user`
GROUP BY has_comment
An example of how the output would look like here:
+------------+-------------+
| user_count | has_comment |
+------------+-------------+
| 150 | 0 |
| 140 | 1 |
+------------+-------------+
Now here comes my question. I want slightly more information here, by grouping these users into 3 groups instead:
Users that have posted no comments
Users that have posted fewer than 10 comments
Users that have posted 10 or more comments
And the best query that I know how to write for this purpose is as follows, which works, but unfortunately runs 4 subqueries and has 2 derived tables:
SELECT
COUNT(id) AS user_count,
CASE
WHEN id IN ( SELECT user_id FROM ( SELECT COUNT(user_id) AS comment_count, user_id FROM `comment` GROUP BY user_id HAVING comment_count >= 10 ) AS a) THEN '10 or more'
WHEN id IN ( SELECT user_id FROM ( SELECT COUNT(user_id) AS comment_count, user_id FROM `comment` GROUP BY user_id HAVING comment_count < 10 ) AS b) THEN 'less than 10'
ELSE 'none'
END AS has_comment
FROM `user`
GROUP BY has_comment
An example of the output here would be something like:
+------------+-------------+
| user_count | has_comment |
+------------+-------------+
| 150 | none |
| 130 | less than 10|
| 100 | 10 or more |
+------------+-------------+
This second query; can it be written more simply and efficiently, and still produce the same kind of result? (potentially maybe even be expanded into more of these kinds of "groups")
You can use two levels of aggregation:
select
count(*) no_users,
case
when no_comments = 0 then 'none'
when no_comments < 10 then 'less than 10'
else '10 or more'
end has_comment
from (
select
u.id,
(select count(*) from comments c where c.user_id = u.id) no_comments
from users u
) t
group by has_comment
order by no_comments
The subquery counts how many comments each user has (you could also express this with a left join and aggregation); then, the outer query classifies and count the users per number of comments.

mysql How to select an ID when it's values are equal to X

I have tried quite a lot of solutions and I decided to post it here to try and find a solution. Any little help is welcome (so I can learn too).
I have a table formed by ArticleID, UserID, and Votes (1/-1).
I want to select the ArticleID that contains a certain UserID and which SUM of Votes is equal to 1.
So far I arrived to:
SELECT catch.ID, votes.postid, catch.text, votes.userid, votes.value, catch.name FROM catch INNER JOIN votes ON catch.ID=votes.postid AND votes.userid=:iduser AND votes.value='1' ORDER BY ID DESC LIMIT 100
but this gives me an erroneous result, as it doesn't consider articles that have votes 1 and -1 (which SUM should be 0).
Thanks!
UPDATE
ID + Value + userid
1 | 1 | 54
1 | -1 | 54
3 | 1 | 54
7 | 1 | 56
7 | -1 | 56
Given the above table, and selecting just the user '54' the wanted result should be ID 3.
Is this what you want?
SELECT c.ID, v.postid, c.text, v.userid, v.value, c.name
FROM catch c INNER JOIN
votes v
ON c.ID = v.postid AND v.userid = :iduser
GROUP BY c.ID
HAVING SUM(v.value) = 1;
This is what you describe but it is a bit different from your query.
Try like this, but for :iduser set what particular id of user
SELECT catch.ID,
votes.postid,
catch.text,
votes.userid,
SUM(votes.value),
catch.name
FROM catch
LEFT JOIN votes ON catch.ID=votes.postid
WHERE votes.userid = :iduser and votes.value='1'
ORDER BY ID DESC
LIMIT 100

PHP MySQL SELECT newest entry grouped by an ID and ordered by timestamp

I am working on an problem regarding Selecting data from two MySQL tables.
First table holds messages | messages | (id, msg_group_id, to_user_id, from_user_id, datetime)
Second table holds user data | profiles | (user_id, name, firstname, ...)
ATM it works the way, that I can select ALL messages with a certain 'to_id' and by adding a JOIN statement getting the name and firstname of the user who sends the message.
My problem now is that I can not figure out a way to ONLY select the newest message of a certain msg_group_id.
I already tried GROUP BY msg_group_id combined with ORDER BY datetime DESC.
But that only throws the very first entry in message table. But I want to last one. :-)
I hope you can help me. :-)
My actual SQL statement:
SELECT LEFT(messages.message, 10) AS message,
`messages`.`msg_group_id`,
`messages`.`datetime`,
`profiles`.`name`,
`profiles`.`firstname`
FROM `messages`
LEFT JOIN `profiles`
ON `messages`.`from_user_id` = `profiles`.`user_id`
WHERE `to_user_id` = '2'
ORDER BY `datetime` DESC
LIMIT 20;
Thanks in Advance
Sample INPUT:
[messages]
|id|msg_group_id|to_user_is|from_user_id|message |datetime|
0 | 1 | 1 | 2 | Hello World1 | 2015-12-21 10:42:00
1 | 1 | 1 | 2 | Hello World2 | 2015-12-21 10:43:00
2 | 1 | 1 | 2 | Hello World3 | 2015-12-21 10:44:00
[profiles]
user_id|name |firstname|
1 | Test | User
2 | Thanks | Worldname
Result (what I don't want)
message|msg_group_id|datetime|name|firstname
Hello World1 | 1 | 2015-12-21 10:42:00 | Thanks | Worldname
Result (what I want)
message|msg_group_id|datetime|name|firstname
Hello World3 | 1 | 2015-12-21 10:44:00 | Thanks | Worldname
May be this query can help:
SELECT m.message, m.msg_group_id, m.datetime, u.name, u.firstname
FROM message as m, profiles as u
WHERE m.from_user_id = u.user_id
GROUP BY m.msg_group_id
ORDER BY m.datetime DESC
Or use INNER JOIN
SELECT m.message, m.msg_group_id, m.datetime, u.name, u.firstname
FROM message as m
INNER JOIN profiles as u ON m.from_user_id = u.user_id
GROUP BY m.msg_group_id
ORDER BY m.datetime DESC
I guess I solved the Problem with the help of another thread:
https://stackoverflow.com/a/1313140/4493030
My SQL Statement as follows:
SELECT `messages`.*, `profiles`.`nick_name`
FROM `messages`
LEFT JOIN `profiles`
ON `messages`.`from_user_id` = `profiles`.`user_id`
INNER JOIN
(SELECT konversation_id, MAX(id) AS maxid FROM messages
WHERE messages.to_user_id = 2
GROUP BY konversation_id) AS b
ON messages.id = b.maxid
WHERE `to_user_id` = '2'
ORDER BY `datetime` DESC
LIMIT 20;
Thanks to all of you who tried to help.
I found a way to tight it down
SELECT messages.to_user_id, messages.msg_group_id, MAX(messages.id) AS maxid, messages.from_user_id, profiles.name
FROM messages
LEFT JOIN profiles
ON messages.from_user_id = profiles.user_id
WHERE messages.to_user_id = 2
GROUP BY msg_group_id

SQL query that produces the following output?

I am trying to write an SQL statement producing the below output.
I have the two following tables:
UserMovie
userID | movieID
-----------------
135 | k0jps
135 | p1zka
125 | v0t67
115 | opp2s
111 | xnwri
115 | kspdl
Follows
followerid | followingid
------------------------
122 | 135
192 | 111
125 | 240
120 | 125
45 | 111
I want to fetch the number of followers of each user who's userid is in the UserMovie Table, giving the following result:
Result
userid | followerCount
----------------------
135 | 1
125 | 1
115 | 0
111 | 2
The following statement gives me partially what i want:
SELECT followingid, count(*) as followerCount
FROM Follows
WHERE followingid in (SELECT DISTINCT userID FROM UserMovie)
GROUP BY followingid
The issue with the above query is that users with 0 followers do not appear in the results giving the following output:
userid | followerCount
----------------------
135 | 1
125 | 1
111 | 2
Any idea on how to do it?
Try this to include users with no follows:
SELECT UserId, Count(followerid) AS followerCount
FROM (SELECT DISTINCT userId FROM UserMovie ) m
LEFT JOIN Follows f
ON f.followingid = m.userID
GROUP BY UserId
Now it generates :
UserId followerCount
111 2
115 0
125 1
135 1
The following worked for me.
However I am getting NULLs instead of 0 for users with no followers
SELECT DISTINCT u.userid, t.followerCount
FROM UserMovie u
LEFT JOIN (
SELECT followingid, count(*) AS followerCount
FROM Follows
WHERE followingid in (SELECT DISTINCT userID FROM UserMovie)
GROUP BY followingid ) as t
on t.followingid = u.userid
How about a solution using CASE?
SELECT userId,
CASE
WHEN IFNULL(followerid, 0) = 0 THEN 0
ELSE count(*)
END
FROM UserMovie
LEFT JOIN Follows on followingid=userID
GROUP BY userId;
Seems to work fine in SQLite3, just replace IFNULL with ISNULL (if SQLServer) or any other equivalent. It's pretty similar to what you've done.
Here's one approach: get a distinct list of userID from UserMovie in an inline view (use either a GROUP BY or a DISTINCT keyword), and perform an "outer join" operation of that to the Followers table to find followers. Collapse the rows from that with a GROUP BY, and use an aggregate function to get a count of unique/distinct non-null values of userId from the Followers table.
For example:
SELECT u.userID
, COUNT(DISTINCT f.userID) AS cnt_followers
FROM ( SELECT m.userID
FROM UserMovie m
GROUP BY m.userID
) u
LEFT
JOIN Follows f
ON f.followingid = u.userID
GROUP BY u.userID
EDIT
There's an invalid column reference in the SELECT list, f.userID is not valid. That should be f.followerID.
When we fix that, the query returns:
userID cnt_followers
111 2
115 0
125 1
135 1
SQL Fiddle HERE http://sqlfiddle.com/#!9/de3e7/2
As long as we are counting "distinct" followerid (question doesn't give any guarantee that (followerID,followingID) is UNIQUE in Followers table), we could eliminate the inline view
SELECT u.userID
, COUNT(DISTINCT f.userID) AS cnt_followers
FROM UserMovie u
LEFT
JOIN Follows f
ON f.followingid = u.userID
GROUP BY u.userID

SQL find team that only contains specified 2 users

I have a table called team_members with this structure and contents:
+---------+---------+
| team_id | user_id |
+---------+---------+
| 1 | 18 |
+---------+---------+
| 1 | 7 |
+---------+---------+
| 3 | 18 |
+---------+---------+
What i am trying to do is to find a team that only contains 2 users and this users are supplied by me (in this case users with id 7 and 18). Unfortunately, i am having no ideas about how to make this query properly. I have tried something like
SELECT a.team_uid
FROM team_members a
INNER JOIN (
SELECT team_uid, user_id, COUNT(*) cnt_team
FROM team_members
GROUP BY team_uid
HAVING COUNT(*) = 2
) b ON a.user_id = b.user_id
Use Case statement in Having clause and Count only the required user_id's. Try this.
select teamid from yourtable
group by teamid
having count(case when userid=7 then 1 end)=1
and count(case when userid=18 then 1 end)=1
and count(1)=2
Something to think about (and assuming a PK on team_id,user_id)...
SELECT x.*, COUNT(*),SUM(user_id IN(7,18)) FROM my_table x GROUP BY team_id;
A couple more ways to do this (where $id1 and $id2 are the users in question):
SELECT team_id
FROM team_members
GROUP BY team_id
HAVING COUNT(*) = 2
AND MIN(user_id) = LEAST($id1,$id2)
AND MAX(user_id) = GREATEST($id1,$id2)
See SQL Fiddle Demo here with values of 7 and 18 for $id1 and $id2. I am using LEAST() and GREATEST() in case it's not known which is the higher and which is the lower (for example, if they're coming from user input).
SELECT team_id
FROM team_members
GROUP BY team_id
HAVING GROUP_CONCAT(user_id ORDER BY user_id) = ('7,18')
See SQL Fiddle Demo here. Again, if it isn't known which is the higher and which is the lower, then this might be written as (the ORDER BY in GROUP_CONCAT() would be unnecessary):
SELECT team_id
FROM team_members
GROUP BY team_id
HAVING GROUP_CONCAT(user_id) IN ('$id1,$id2','$id2,$id1')
You can also use:
select team_id
from team_members
group by team_id
having sum(user_id not in(7, 18)) = 0
Example above assumes you want teams with only users 7 or 18 (no others, but not necessarily both).
If you want teams with BOTH users 7 and 18, and no others, you can use:
select team_id
from team_members
group by team_id
having sum(user_id not in(7, 18)) = 0
and sum(user_id in(7, 18)) = 2