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
Related
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.
Suppose I have a blog where users can make a comment. If the comment is spam, people can vote it deleted.
When that happens, a row is inserted in this table:
SPAM_REPORTS
comment_id - ip
The table is unique on comment_id, ip.
Now I want to get outputted the comment_id's ordered by those with the maximum number of reports.
Suppose SPAM_REPORTS is:
comment_id ip
6 888.xxx.xxx.xxx
5 111.xxx.xxx.xxx
5 222.xxx.xxx.xxx
6 444.xxx.xxx.xxx
1 333.xxx.xxx.xxx
5 555.xxx.xxx.xxx
I want the output to be:
comment_id count
5 3
6 2
1 1
try this
select comment_id , count(*) as count from SPAM_REPORTS
group by comment_id
order by count desc
DEMO HERE
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
I'm trying to count the number of purchases and 'up'/'down' votes for all items that match a given search term. Unfortunately, as I've set up my query now, the purchases and vote counts are multiplied by a mysterious factor of 22 that I can not figure out where it comes from.
As an example for one of the items: the purchases, up, and down votes should be 7, 2, and 1 respectively but they are instead 154, 44, and 22.
here's my SQL code:
SELECT *
sum(purchaseyesno) as tots,
sum(rating=1) as yes,
sum(rating=0) as no
from items
join items_purchased
on items_purchased.item_id=items.item_id
join accounts
on items.account_id=accounts.account_id
like subject='%album by joe%' or description='%album by joe%'
group by item_id
order by tots desc, yes desc
Here's some sample data:
subject tots yes no full_name
album by joe 154 44 22 joe smith
album by fred 88 44 0 fred jones
Here's how i'd like the data to look:
subject tots yes no full_name
album by joe 7 2 1 joe smith
album by fred 4 2 0 fred jones
Would someone be able to help me figure out what is going on here? I can't figure out this factor of 22 issue which persists despite changing the group by and other things (meaning, this 22 number is independent of the # of returned rows).
You don't show your schema but it might help to use a subquery:
SELECT subject, tots, yes, no, fullnane
FROM (
SELECT item_id, SUM(purchaseyesno) AS tots, SUM(rating=1) AS yes, SUM(rating=0) AS no
FROM items_purchased
GROUP BY item_id
) i
JOIN items ON i.item_id = items.item_id
JOIN accounts ON items.account_id=accounts.account_id
WHERE subject LIKE '%album by joe%' OR description LIKE '%album by joe%'
ORDER BY tots DESC, yes DESC
Firstly, don't do select * if you're grouping by. Remember you must group by all non-aggregated fields.
Secondly, the high amount of results must be comming from those joins. Remove the group by and the aggregated columns and inspect the results and you'll see why you're getting so many records.
Finally... you're missing a where clause there...
SELECT subject,
sum(purchaseyesno)/22 as tots,
sum(CASE WHEN rating=1 THEN 1 END)/22 as yes,
sum(CASE WHEN rating=0 THEN 1 END)/22 as no,
full_name
from items
join items_purchased
on items_purchased.item_id=items.item_id
join accounts
on items.account_id=accounts.account_id
like subject='%album by joe%' or description='%album by joe%'
group by subject,full_name
order by tots desc, yes desc
In this query,the SUM function basically works on the columns purchaseyesno,rating=1,rating=0.
For eg.
Subject purchaseyesno rating full_name
1 5 1 Mark
1 6 0 Mark
2 7 1 Robert
2 8 0 Robert
The above query will return as below.
Subject tots yes no full_name
1 11 1 1 Mark
2 15 1 1 Robert
I have a database that has a table that has different LIKES that people have. Each like is stored as a different record. The only info I have in each record is userID, likeID.
The search will be based on the current userID that is signed in. So I'm not matching EVERYONE with EVERYONE but instead matching EVERYONE vs 1
So userID 45 might have likeID 3, likeID 6 and likeID 22 in the table (for instance)
What I want to do is have the table return, in descending order userIDs that they match with based on the total likes they match with someone else.
For example, (based on user 45 example above) it would look at userID 1 and see how many of likeID 3, likeID 6 and likeID 22 they have. then it'd go to userID 2 and see how many of likeID 3, likeID 6 and likeID 22 they have.....it'd do this for everyone.
Then it'd show the results for any that have more than 0 matches for those 3 likeIDs:
Matches:
You and UserID 12 share 3 of the same likes.
You and UserID 87 share 3 of the same likes.
You and UserID 22 share 2 of the same likes.
You and UserID 73 share 2 of the smae likes.
You and UserID 71 share 1 of the same likes.
etc...
I hope that explains what I'm trying to do. I don't think the mySQL query will be too hard but right now I'm baffled at how to do it!
THANKS IN ADVANCE!
select
UL2.userID,
count(*) as LikeMatches
from
UserLikes UL1
JOIN UserLikes UL2
on UL1.LikeID = UL2.LikeID
AND UL1.UserID != SingleUserBasisParameter
where
UL1.UserID = SingleUserBasisParameter
group by
UL2.UserID
order by
2 desc
The "2" in the order by is the ordinal column representing the COUNT(*)
select userid,count(*) as common_likes
from table
where likeid in (select likeid from table where userid = 45) and userid <> 45
group by userid
order by common_likes desc
how about:
SELECT COUNT(likes.likeID) as like_count FROM
LIKES as likes,
(SELECT UserID,likeID FROM likes WHERE UserID = $user_id) as user_likes
WHERE
user_likes.likeID = likes.likeID AND user_likes.userID != likes.userID
GROUP BY likes.UserID
ORDER BY like_count
I haven't tested it, but I think that it should at least give you the right idea. The 2nd SELECT finds all the likes of the main user, which you can then use to filter out the other likes.