MySQL query from three different tables - mysql

I built working MySQL query:
SELECT
v.*, u.username
FROM
video AS v, users AS u
WHERE
v.type = 'public'
AND
v.user_ID = u.user_ID
Now I want to add a third table and count() results from the table comments where video_ID from this table will be equal to those from the table video.
I tried this but it wasn't successful:
SELECT
v.*, u.username, count(c.video_ID)
FROM
video AS v, users AS u, comments AS c
WHERE
v.type = 'public'
AND
v.user_ID = u.user_ID
AND
v.video_ID = c.video_ID
In return I want to get the number of comments related to certain video_ID's.
I don't understand how to make it work correctly in one query.
Could you please help me out?
Thank you in advance,
Ilia

If you are using an aggregate function like a COUNT in a query, you need to specify the grouping with a GROUP BY clause.
Try this
SELECT
v.*,
u.username,
count(c.video_ID) AS comment_count
FROM
video AS v
INNER JOIN users AS u ON v.user_ID = u.user_ID
INNER JOIN comments AS c ON v.video_ID = c.video_ID
WHERE
v.type = 'public'
GROUP BY
v.id,
u.username,
v.v.add_time
ORDER BY
v.add_time
While MySQL lets you leave out the some elements of the GROUP BY clause, it is good practice to specify them.
When joining two tables, it is good practice to use the INNER JOIN syntax, rather than a WHERE filter.

Related

Sort the table by COUNT after displaying values from multiple tables

My goal is: display how often is specific ID repeated as the topic_poster in one table, phpbb_topics, but only if the proper forum_id condition is also met, then also display the corresponding username from another table, phpbb_users.
I have successfully extracted the count of how often is one specific userID occuring as the topic_poster in table phpbb_topics, like that:
SELECT topic_poster, COUNT(topic_poster)
FROM phpbb_topics WHERE forum_id = 156
GROUP BY topic_poster
Thanks to another question on StackOverflow I now also know how to get data from another table to get the username corresponding to the specific userID, like that:
SELECT t.topic_poster, u.user_id, u.username
FROM phpbb_topics t
LEFT JOIN phpbb_users u ON u.user_id = t.topic_poster
I also managed to finally mix the two to get what I want:
SELECT t.topic_poster, COUNT(t.topic_poster), u.user_id, u.username
FROM phpbb_topics t
LEFT JOIN phpbb_users u ON u.user_id = t.topic_poster WHERE t.forum_id = 156
GROUP BY t.topic_poster
However, I do not know how to properly sort in descending or ascending order based on the counter. phpmyAdmin won't let me just click on the column's name to sort by it, and any queries i write with GROUP BY or ORDER BY are reporting errors.
Update:
after putting this in:
SELECT t.topic_poster, COUNT(t.topic_poster), u.user_id, u.username
FROM phpbb_topics t
LEFT JOIN phpbb_users u ON u.user_id = t.topic_poster WHERE t.forum_id = 156
ORDER BY COUNT(topic_poster)
the results display only one row:
topic_poster |COUNT(t.topic_poster) | user_id | username
6 | 254 6 | Opix
Same happens if I use this:
SELECT t.topic_poster, COUNT(t.topic_poster), u.user_id, u.username
FROM phpbb_topics t
LEFT JOIN phpbb_users u ON u.user_id = t.topic_poster WHERE t.forum_id = 156
ORDER BY COUNT(t.topic_poster)
Same happens if I use this:
SELECT t.topic_poster, COUNT(t.topic_poster), u.user_id, u.username
FROM phpbb_topics t
LEFT JOIN phpbb_users u ON u.user_id = t.topic_poster WHERE t.forum_id = 156
ORDER BY topic_poster
If I use this: SELECT t.topic_poster, COUNT(t.topic_poster), u.user_id, u.username FROM phpbb_topics t LEFT JOIN phpbb_users u ON u.user_id = t.topic_poster WHERE t.forum_id = 156 GROUP BY t.topic_poster I get all the results, but I can't sort by the counter.
mySQL extends the group by so you don't have to have one. However, it assumes all values for each column are the same; so it's free to pick what to put in from each column. However, if the values are different, what it picks (1 value) isn't representative of the entire set, so you must use group by when the values are different.
Put a different way: if t.forum_ID = 156 limited to a specific topic_poster, user_Id and username. you'd have no problem. But since t.forum_ID represents many different values in each of those columns, group by is needed or the engine will "somewhat" randomly select a value for each of them. The engine assumes all are the same.
Thus the downfall of the mySQL Group by extension. But, if all the non-aggregrated columns did have the same value... you get a performance gain by allowing the engine to just aggregate and 'pick' a value for each column.
Based on your response, you think you should be getting multiple rows. So that tells me the non-aggregated fields are different so add a group by...
SELECT t.topic_poster, COUNT(t.topic_poster), u.user_id, u.username
FROM phpbb_topics t
LEFT JOIN phpbb_users u ON u.user_id = t.topic_poster
WHERE t.forum_id = 156
GROUP BY t.topic_poster, u.user_id, u.username
ORDER BY COUNT(t.topic_poster)
You could have ties, so you may also want to order by poster or user name after the count...

Multiple aggregate functions in SQL query

For this example I got 3 simple tables (Page, Subs and Followers):
For each page I need to know how many subs and followers it has.
My result is supposed to look like this:
I tried using the COUNT function in combination with a GROUP BY like this:
SELECT p.ID, COUNT(s.UID) AS SubCount, COUNT(f.UID) AS FollowCount
FROM page p, subs s, followers f
WHERE p.ID = s.ID AND p.ID = f.ID AND s.ID = f.ID
GROUP BY p.ID
Obviously this statement returns a wrong result.
My other attempt was using two different SELECT statements and then combining the two subresults into one table.
SELECT p.ID, COUNT(s.UID) AS SubCount FROM page p, subs s WHERE p.ID = s.ID GROUP BY p.ID
and
SELECT p.ID, COUNT(f.UID) AS FollowCount FROM page p, follow f WHERE p.ID = f.ID GROUP BY p.ID
I feel like there has to be a simpler / shorter way of doing it but I'm too unexperienced to find it.
Never use commas in the FROM clause. Always use proper, explicit, standard JOIN syntax.
Next, learn what COUNT() does. It counts the number of non-NULL values. So, your expressions are going to return the same value -- because f.UID and s.UID are never NULL (due to the JOIN conditions).
The issue is that the different dimensions are multiplying the amounts. A simple fix is to use COUNT(DISTINCT):
SELECT p.ID, COUNT(DISTINCT s.UID) AS SubCount, COUNT(DISTINCT f.UID) AS FollowCount
FROM page p JOIN
subs s
ON p.ID = s.ID JOIN
followers f
ON s.ID = f.ID
GROUP BY p.ID;
The inner joins are equivalent to the original query. You probably want left joins so you can get counts of zero:
SELECT p.ID, COUNT(DISTINCT s.UID) AS SubCount, COUNT(DISTINCT f.UID) AS FollowCount
FROM page p LEFT JOIN
subs s
ON p.ID = s.ID LEFT JOIN
followers f
ON p.ID = f.ID
GROUP BY p.ID;
Scalar subquery should work in this case.
SELECT p.id,
(SELECT Count(s_uid)
FROM subs s1
WHERE s1.s_id = p.id) AS cnt_subs,
(SELECT Count(f_uid)
FROM followers f1
WHERE f1.f_id = p.id) AS cnt_fol
FROM page p
GROUP BY p.id;

Join two different tables ordered by common value (hotness)

I'm trying to select results from two different unrelated tables, showcase and questions to appear in a feed. They should be ordered by the common column hotness which is a float value.
SELECT s.id,s.date,s.title,s.views,s.image,s.hidpi,s.width,s.description,u.display_name,u.avatar
FROM showcase AS s
INNER JOIN users AS u ON s.user_id = u.id
UNION
SELECT q.id,q.date,q.title,q.views,q.text,u.display_name,u.avatar,0,0,0
FROM questions AS q
INNER JOIN users AS u ON q.user_id = u.id
ORDER BY hotness DESC
LIMIT 10
I've tried UNION, but I have no idea how I should be using it here and get this error unknown column hotness
You need to select the value in order for the ORDER BY to recognize it:
SELECT s.id,s.date,s.title,s.views,s.image,s.hidpi,s.width,s.description,u.display_name,u.avatar, s.hotness
FROM showcase AS s
INNER JOIN users AS u ON s.user_id = u.id
UNION ALL
SELECT q.id,q.date,q.title,q.views,q.text,u.display_name,u.avatar,0,0,0, q.hotness
FROM questions AS q
INNER JOIN users AS u ON q.user_id = u.id
ORDER BY hotness DESC;
Note that I also changed the UNION to UNION ALL. Unless you intend to remove duplicates, there is no reason to incur the extra processing for doing that.
You can try this query:
SELECT r.* FROM (
SELECT s.id,s.date,s.title,s.views,s.image,s.hidpi,s.width,s.description,u.display_name,u.avatar, s.hotness
FROM showcase AS s
INNER JOIN users AS u ON s.user_id = u.id
UNION
SELECT q.id,q.date,q.title,q.views,q.text,u.display_name,u.avatar,0,0,0, q.hotness
FROM questions AS q
INNER JOIN users AS u ON q.user_id = u.id
) as r
ORDER BY r.hotness DESC
LIMIT 10
You need to merge Union result in subquery to apply Order by on the result. I also added hotness in select clause, please check I take field from good table.

MySQL check if value is in result set before summing

I have a table of ratings for comments, when I fetch comments, I also fetch the ratings and I also want to be able to display which comments the logged user has already voted on. This is what I am doing now
SELECT
c.id,
c.text,
c.datetime,
c.author,
u.email AS author_name,
SUM(cr.vote) AS rating,
cr2.vote AS voted
FROM comments c
LEFT JOIN users u ON u.id = c.author
LEFT JOIN comments_ratings cr ON c.id = cr.comment
LEFT JOIN comments_ratings cr2 ON c.id = cr2.comment AND cr2.user = :logged_user_id
GROUP BY c.id ORDER BY c.id DESC
But I don't like how I'm performing a second join on the same table. I know it is perfectly valid but if I could get the information I want from the first join, which is there anyway, why perform a second one?
Is it possible to figure out if a row with column user equal to :logged_user_id exists on table comments_ratings cr before executing the aggregate function(s)?
P.S.: If someone could come up with a better title, people can find in future, I'd also appreciate that.
You can do what you want with conditional aggregation:
SELECT c.id, c.text, c.datetime, c.author, u.email AS author_name,
SUM(cr.vote) AS rating,
MAX(cr.user = :logged_user_id) as voted
FROM comments c LEFT JOIN
users u
ON u.id = c.author LEFT JOIN
comments_ratings cr
ON c.id = cr.comment
GROUP BY c.id
ORDER BY c.id DESC;

Joining on a Select Distinct in MySQL reorders by joined table

I've looked around for an answer to this but I'm not finding it. I have a site with articles stored in an article table and writers in the user table. I wanted to get a list of authors ordered by how recently they'd written an article.
This gives me the User ids:
SELECT distinct a.user_id
FROM `article` as a
ORDER BY a.id desc
The problem is that as soon as I try to bring the names in by joining the order changes so that it's by user id. I've tried this:
SELECT distinct a.user_id, u.name
FROM `article` as a
LEFT JOIN user as u on u.id = a.user_id
ORDER BY a.id desc, u.id desc
and
SELECT distinct a.user_id, u.name
FROM `article` as a
LEFT JOIN user as u on u.id = a.user_id
ORDER BY u.id desc, a.id desc
but both alter the order of the names. I'm obviously doing something stupid, but what?
The fact that DISTINCT happens to work with ORDER BY in your first example is a fluke, and not standard SQL. You need something like this:
SELECT a.user_id, u.name
FROM article a
LEFT JOIN user u ON u.id = a.user_id
GROUP BY a.user_id
ORDER BY MAX(a.id) desc
try this
SELECT a.user_id, u.name
FROM `article` a
LEFT JOIN user u on u.id = a.user_id
GROUP BY a.user_id
ORDER BY a.id desc
Your ORDER BY sorts the rows by either the User ID and/or Article ID (depending on the example).
If you want reliable sorting by name, then include the u.name field in the ORDER BY fields.
this is postgres syntax, but you can easily translate it into mysql
select u.*, q.last_article_id
from user u
inner join (
select a.user_id, max(a.id) as last_article_id
from article a
group by a.user_id
) q
on u.id = a.q.id
order by q.last_article_id desc
Something along the lines of the following should work:
select users.id, articles.id, max(articles.created_at) as recent_date
from users
left join articles on users.id = articles.user_id
group by articles.id
order by recent_date
Not sure what exactly your fields are named and all, but this should point you in the right direction.
You got a lot of answers to choose from... I believe the key to what you're asking is use of the aggregate function MAX() http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_max