I'm developing a commenting system like Stackoverflow or Disqus has where people can comment and vote on the comments. I have three tables for this: USERS, COMMENTS, and VOTES.
I'm having trouble figure out how to write the query to count votes and returning whether or not a given user has voted. Each user can only vote once per comment and can't vote on their own comment. Also, the comments and votes aren't just on one topic. Each page has its own topic and so when doing a GET to SELECT comments and votes, the WHERE topic_id='X' needs to be reflected in the query too.
here's my tables' data, how my query looks so far, and what i hope to have it return:
USERS table
user_id user_name
1 tim
2 sue
3 bill
4 karen
5 ed
COMMENTS table
comment_id topic_id comment commenter_id
1 1 good job! 1
2 2 nice work 2
3 1 bad job :) 3
VOTES table
vote_id vote comment_id voter_id
1 -1 1 5
2 1 1 4
3 1 3 1
4 -1 2 5
5 1 2 4
SELECT users.*, comments.*, count(vote as totals), sum(vote=1 as yes), sum(vote=-1 as no),
my_votes.vote as did_i_vote, users.* as IM_THE_USER
from comments
join votes
on comments.comment_id=votes.comment_id
join users
on comments.commenter_id=users.user_id
join votes as MY_votes
on MY_votes.voter_id=IM_THE_USER.user_id
where topic_id=1 and IM_THE_USER.user_id=1
group by comment_id
user_id user_name comment_id topic_id comment commenter_id totals yes no did_i_vote
1 tim 1 1 good job! 1 2 1 1 NULL
3 bill 3 1 bad job:) 3 1 1 0 1
SQL Fiddle for playing
select u.user_id, u.user_name,
c.comment_id, c.topic_id,
sum(v.vote) as totals, sum(v.vote > 0) as yes, sum(v.vote < 0) as no,
my_votes.vote as did_i_vote
from comments c
join users u on u.user_id = c.commenter_id
left join votes v on v.comment_id = c.comment_id
left join votes my_votes on my_votes.comment_id = c.comment_id
and my_votes.voter_id = 1
where c.topic_id = 1
group by c.comment_id, u.user_name, c.comment_id, c.topic_id, did_i_vote;
Update: fixed group by clause
update by OP (tim peterson): fixed count as totals and added comment itself to be returned by the query
final working SQLFiddle
Related
I have the following tables
user
id
name
1
John
2
Jack
3
George
essay
id
date_submitted
user_id
project_id
1
2020-12-10
1
1
2
2020-05-01
2
2
3
2020-04-10
3
2
project_phase
id
project_id
date
phase
1
1
2020-01-01
early
2
1
2020-05-31
mid
3
1
2020-11-30
late
4
2
2020-01-01
early
5
2
2020-03-31
mid
6
2
2020-04-30
late
1 User submits only 1 essay in one project
and I want to have a combined table which shows me the user and the phase at which the essay was submitted.
I'm trying to get the record with the MAX date from project_phase that is below the date_submitted
So using the above data I want to get
user
date_submitted
phase
John
2020-12-10
late
Jack
2020-05-01
late
George
2020-04-10
mid
so far I combined the tables with INNER JOIN but the following query returns duplicate records. I also tried ROW_NUMBER() and MAX() but I didn't structure it correctly to get the answer I'm looking for
SELECT
U.name,
E.date_submitted,
P.phase
FROM
essay E
INNER JOIN user U ON U.id = E.user_id
INNER JOIN project_phase P ON P.project_id = E.project_id and E.date <= P.date_submitted
Any help appreciated, thanks.
I would suggest a correlated subquery:
SELECT U.name, E.date_submitted,
(SELECT P.phase
FROM project_phase P
WHERE P.project_id = E.project_id AND
P.date <= E.date_submitted
ORDER BY p.date DESC
LIMIT 1
) as phase
FROM essay E JOIN
user U
ON U.id = E.user_id;
I have two tables with multiple rows. The first is called comments and has the following structure:
user_id last_commented
........................
9239289 2017-11-06
4239245 2017-11-05
4239245 2017-11-03
6239223 2017-11-02
1123139 2017-11-04
The second one is called users and has the following structure:
user_id user_name user_status
.................................
9239289 First Name 0
4239245 First Name2 2
6239223 First Name3 1
1123139 First Name4 2
I need a query that displays the users who have not added comments for the last 3 days, have a user_status equals to 2 and display the number of days since they last commented.
This is my query so far:
select u.*
from users u
where not exists (
select 1
from comments c
where c.user_id = u.user_id and last_commented > DATE(NOW()) - INTERVAL 3 DAY
) and user_status = 2
Which outputs correctly the users who haven't commented for the last 3 days. How can I modify it so it shows the number of days since they last commented?
If you need the number of days since the last comment, then you will need a join, of some sort:
select u.*, datediff(curdate(), last_commented)
from users u left join
comments c
on c.user_id = u.user_id
where u.status = 2
group by u.user_id
having max(last_commented) < curdate() - interval 3 day or
max(last_commented) is null;
This version includes users who have not commented at all.
I have 3 tables which I need to query where I need to group by 2 columns and also join the tables but still return all results.
Users
ID User_name Category Reason Change_date
1 John 1 2 2016-01-05
2 James 3 1 2015-10-02
3 Peter 1 4 2016-01-04
4 Tony 1 4 2016-01-15
5 Fred 1 4 2016-02-25
6 Rick 3 2 2016-04-19
7 Sonia 2 1 2016-10-14
8 Michelle 2 2 2015-11-09
9 Phillip 3 3 2016-03-01
10 Simon 3 3 2016-03-07
Category
ID Category_name
1 User
2 Super user
3 Admin
Reason
ID Reason_name
1 Promotion
2 Upgrade
3 Sponsor
4 Normal
I did some searching and found https://stackoverflow.com/a/28158276/1278201 and modified my query to try and use it:
SELECT category_name, reasons.reason_name, u1.id as user_id, user_name
from users as u1
JOIN (SELECT id from users where users.change_date BETWEEN '2016-01-01'
AND '2016-11-06' group by users.category, users.reason) AS u2
ON u1.id = u2.id
left join reason on u1.reason=category.id
left join category on u1.category=category.id
The results being returned are only using the group by - I should have 8 rows returned but I am only getting 5 which is one for each occurrence of each reason within each category.
My expected outcome is:
category_name reason_name user_id user_name
User Upgrade 1 John
"Upgrade" count 1
Normal 3 Peter
4 Tony
5 Fred
"Normal" count 3
"User" count 4
Super user Promotion 7 Sonia
"Promotion" count 1
"Super user" count 1
Admin Upgrade 6 Rick
"Upgrade" count 1
Sponsor 9 Phillip
10 Simon
"Sponsor" count 2
"Admin" count 3
How can I get all 8 rows returned as well as being able to get counts for each category_name and reason_name?
For what you are looking for in the expected output, this might be what you looking for:
SELECT
Category_name, reason_name, users.ID,User_name
FROM
Users
inner join Category on Category.ID=Users.Category
inner join Reason on Reason.ID=Users.Reason
where users.change_date BETWEEN '2016-01-01' AND '2016-11-06'
SQLFiddle
You shouldn't use GROUP BY in the subquery, because then it only returns one user ID from each group.
In fact, you don't need the subquery at all. You can just use a WHERE clause to select users that meet the change_date criteria.
SELECT category_name, reasons.reason_name, u1.id as user_id, user_name
from users as u1
left join reason on u1.reason=category.id
left join category on u1.category=category.id
where u1.change_date BETWEEN '2016-01-01' AND '2016-11-06'
To get subtotals of the groupings by category and reason, you can use GROUP BY and WITH ROLLUP.
SELECT category_name, reasons.reason_name, u1.id as user_id, user_name, COUNT(*) AS total
from users as u1
left join reason on u1.reason=category.id
left join category on u1.category=category.id
where u1.change_date BETWEEN '2016-01-01' AND '2016-11-06'
GROUP BY category_name, reason_name, user_id WITH ROLLUP
In the script that displays the results, the totals are in the rows where user_id is NULL. The category totals also have reason IS NULL. So you can display these rows appropriately in the script that displays the results. If you really need to do it all in MySQL, you can put the above query in a subquery, and the containing query can test for user_id IS NULL and reason_name IS NULL.
I have read the different answers here on SO, but I am stuck on this question. Please help.
I have this mysql view named "activeuser":
userid COUNT(*) ACRONYM
1 23 admin
2 2 doe
3 4 tompa
12 4 Marre
13 1 Mia
1 2 admin
3 1 tompa
12 1 Marre
13 1 Mia
2 1 doe
3 1 tompa
12 1 Marre
How can I sum the COUNT column so that I get the following wanted result?
userid COUNT(*) ACRONYM
1 25 admin
2 3 doe
3 6 tompa
12 6 Marre
13 1 Mia
EDITED:
I used this query to create the view:
CREATE VIEW activeuser AS
(SELECT boats_comments.userid, COUNT(boats_comments.userid), boats_user.acronym, boats_user.email
FROM boats_comments
INNER JOIN boats_user
ON boats_comments.userid = boats_user.id
GROUP BY boats_comments.userid
ORDER BY COUNT(boats_comments.userid) DESC)
UNION ALL
(SELECT boats_answers.userid, COUNT(boats_answers.userid), boats_user.acronym, boats_user.email
FROM boats_answers
INNER JOIN boats_user
ON boats_answers.userid = boats_user.id
GROUP BY boats_answers.userid
ORDER BY COUNT(boats_answers.userid) DESC)
UNION ALL
(SELECT boats_questions.userid, COUNT(boats_questions.userid), boats_user.acronym, boats_user.email
FROM boats_questions
INNER JOIN boats_user
ON boats_questions.userid = boats_user.id
GROUP BY boats_questions.userid
ORDER BY COUNT(boats_questions.userid) DESC)
My goal is to see which users are the most active by checking the number of comments, questions and answers... but I got stuck...
As the results in your view has duplicates I guess the underlying code for the view is grouping on something it maybe shouldn't be grouping on.
You can get the results you want by applying SUM to it:
select userid, sum("whatever column2 is named") as "Count", Acronym
from activeuser group by userid, Acronym;
select userid, count(*) from activeuser group by userid;
I have MySQL query which I think needs a subquery. I'd like to count the total number of "up" votes on each of many comments and determine whether a given user has already voted on each comment:
Here are my tables:
Comments Table:
comment_id comment acct_id topic_id comment_date
5 hello5 2 1 9:00am
7 hello7 3 1 10:00am
Votes Table:
comment_id vote acct_id topic_id
5 1 1 1
7 1 4 1
5 1 5 1
here's the output i'm getting:
comment_id commenter comment voter sum did_i_vote
5 2 hello5 1 2 1
7 3 hello7 4 1 1
Here's the desired output:
comment_id commenter comment voter sum did_i_vote
5 2 hello5 **5** 2 1
7 3 hello7 4 1 1
Here's my query:
SELECT votes.acct_id as voter, comments.comment_id, comment, comments.acct_id as
commenter, SUM(vote) as sum, vote as did_i_vote
from votes
right join comments
on votes.comment_id=comments.comment_id
join accounts on comments.acct_id=accounts.acct_id
where topic_id=1
group by comments.comment_id order by comment_date desc
You'll notice these 2 outputs are identical except for voter.
What my query is missing is a way to determine whether a given user, for example with voter=acct_id=5, was the one who voted on any of the comments. Without that condition, the query picks the first voter in the list which for comment_id=5 is voter=1.
So my question is I think I need to insert the following subquery:
SELECT from votes where voter='X'
I'm just not sure where or how. Putting it in parentheses in between the from and votes above eliminates the sum() function so I'm stuck.
Any thoughts?
If I've understood you correctly from your comments above, I think all you need to do is (outer) join the votes table to your query another time, this time only on the votes of the account in question:
SELECT
comments.comment_id AS comment_id,
comments.acct_id AS commenter,
comment AS comment,
-- votes.acct_id AS voter, -- ambiguous
SUM(votes.vote) AS sum,
my_votes.vote IS NOT NULL AS did_i_vote
FROM
votes
RIGHT JOIN comments ON votes.comment_id=comments.comment_id
JOIN accounts ON comments.acct_id=accounts.acct_id -- what purpose ?
LEFT JOIN votes AS my_votes ON
my_votes.commentid=comments.comment_id
AND my_votes.acct_id=#my_acct_id
WHERE topic_id = 1 -- ambiguous
GROUP BY comments.comment_id
ORDER BY comment_date DESC