I have a users table and a comments table and i want select from users the top users that have the big amount of comments from the comments table and order them by numbers of comments
table structure
users
id | username | password
comments
id | text | author_username
Use the following MySQL statement to list the users with the most comments. CommentCount tells you the number of comments made by a particular user.
SELECT
users.username,
COUNT(comments.id) AS CommentCount
FROM
users
INNER JOIN comments ON users.id = comments.author_userid
GROUP BY
users.username
ORDER BY
COUNT(comments.id) DESC
Please note that you will have to change author_userid into author_username first!
My SQL is a bit rusty, but something like this should give you what you're looking for (although as I mentioned, the user ID should be the only user identifier in the comments table.)
Select count(id), author_username from comments group by author_username
select u.username,count(c.comments) as total
from users as u
left join comments as c
on u.username = c.author_username
group by u.username
order by total desc
I would change join field as dutchie432 suggested. Add a limit clause in order to have your desired number of records.
Related
I have 3 tables, users, news, news_viewed. I'm trying to join these 3 tables and find a list of news each user has not viewed.
TABLE users
userid
username
status
TABLE news
newsid
title
post_time
TABLE news_viewed
nvid
username
newsid
Looking to find a list from users that have not read news (found in news_viewed)
I've tried many different joins, including left joins and inners and outers but cannot get the results I need.
$_30daysago = strtotime('-30 days');
SELECT * FROM
(
SELECT users.username, news_id
FROM users inner join news_viewed ON
users.username = news_viewed.username and users.status='active'
UNION
SELECT news_viewed.username, post_time
FROM news_viewed inner join news ON
news_viewed.newsid = news.newsid and news.post_time>'$_30daysago'
) as JoinedTable
I need the required results to include the users.username, news.newsid and news.title.
Any help would be appreciated, thank you!
This is a good spot to use the LEFT JOIN antipattern:
SELECT u.username, n.newsid, n.title
FROM users u
INNER JOIN news n ON n.post_time > ?
LEFT JOIN news_viewed nv
ON n.newsid = nv.newsid
AND nv.username = u.username
WHERE
u.status = 'active'
AND nv.nvid IS NULL
This query generates a cartesian product of users and recent news (ie having a post time greater than the parameter indicated by ?), and returns the users/news tuples for which the left join on news_viewed did not succeed (hence the antipattern).
Note: it is unclear what column to use in the join; column name news_viewed (username) tend to indicate that it relates to users(username), whereas the primary key of users seems to be userid. Fix your column names or fix your relationship.
Ellaborating on #GMB's answer
Your query:
$_30daysago = strtotime('-30 days');
SELECT * FROM
(
SELECT users.username, news_id
FROM users inner join news_viewed ON
users.username = news_viewed.username and users.status='active'
UNION
SELECT news_viewed.username, post_time
FROM news_viewed inner join news ON
news_viewed.newsid = news.newsid and news.post_time>'$_30daysago'
) as JoinedTable
is saying:
get all active users with the news they have read (inner join)
SELECT users.username, news_id
FROM users inner join news_viewed ON
users.username = news_viewed.username and users.status='active'
and add all the news with the users that have read them in the last 30 days (inner join again)
SELECT news_viewed.username, post_time
FROM news_viewed inner join news ON
news_viewed.newsid = news.newsid and news.post_time>'$_30daysago'
That is actually bringing up all the tuples from news_viewed minus the ones where the user is not active AND the new is over 30 days old.
however, given the usage of inner join, you're bringing a lot of duplicate records
1.- The results from the first query where the new is less than 30 days old
2.- The results from the second query where the user is active
since you're using UNION and not UNION ALL, you are implicitly asking for a SELECT DISTINCT, but the fields are different (it makes no sense to display newsid and then post_time in the same field)
plus, you have a typo in the field name, which is not news_id
You have to look at it from the other way around. The potential combinations amount for a scenario where every user has read every new. So you get that universe as a basis (number of users times number of news) and then
1- remove inactive users
2- remove news older than 30 days
3- remove tuples that are unrelated in the news_viewed table
SELECT users.username, news.newsid
FROM users
JOIN news
ON users.status='active' -- removes inactive users
AND news.post_time>'$_30daysago' -- removes older news
LEFT JOIN
news_viewed nv USING (username, newsid)
WHERE nv.nvid IS NULL -- removes unrelated entries
I have been looking for a solution for this for over an hour now and decided to resort to asking here.
I am creating a "Twitter-like", following system for users of my Website and I wanted to be able to display each and every one of the users that a specific user follows or is followed by, I also want to then order this by the timestamp on the follow table, descending so that the latest follower is at the top.
The solutions I have come across seem to use inner joins etc. which is all well and good, but I was wondering whether there is a logical solution for my current query to do this.
Table structures:
users:
id | username
follows:
id | follower_id | following_id | timestamp
My current query:
SELECT * FROM users WHERE id IN (SELECT follower_id FROM follows WHERE following_id = $user_id) ORDER BY id ASC
Of course this will simply order by the user ID, how would I (using the current query structure), be able to add the order to list by the follows timestamp?
MySQL INNER JOIN
"SELECT users.* FROM users
INNER JOIN follows ON follows.follower_id = users.id
WHERE follows.following_id = $user_id
ORDER BY follows.timestamp DESC";
You can sort using multiple columns like this:
ORDER BY [column1] [ASC|DESC], [columm2] [ASC|DESC], ...
Therefore, edit your query's order by clause to include the second column and sort it descending.
You must use a join to add the column; here's the basic syntax of a join:
SELECT [table_name].[column_name], ...
FROM [table1]
JOIN [table2] ON [join condition]
...
Your code should look somewhat like this:
SELECT users.*
FROM users
JOIN follows ON users.id = follows.following_id
WHERE follows.following_id = $user_id
ORDER BY users.id ASC, follows.timestamp DESC
As far as I know, there is no way to do this without joining the tables; perhaps its possible to sort the returned list, but no guarantees):
SELECT * FROM users
WHERE id IN (SELECT follower_id FROM follows
WHERE following_id = $user_id
ORDER BY timestamp DESC)
ORDER BY id ASC;
The above may or may not work (I didn't test it); if it does not, you must use a join query.
I want to have some help creating my query to get information from three different tables sharing information in common.
My first table is:
auctions
id title description user_id(who posted it)
My second table is:
bids
id user_id bid auction_id owner_id
My third table is:
users
id username X XX XXX XXXX
...and my SQL is as follows however it's not returning any results:
SELECT auction_bids.user_id AS applicant, auction_bids.*, auctions.title FROM auction_bids, auctions
WHERE auctions.user_id=".$_SESSION['userid']."
INNER JOIN users ON auction_bids.user_id = users.id
WHERE auction_bids.owner_id = ".$_SESSION['userid']."
What I need is to capture the auction's title, username who bidded on the auction and the bid. the auction has to have a bid and posted by the user who owns the $_SESSION['userid'].
Any help is appreciated.
You have two different 'where' statements, which may just need combining;
SELECT auction_bids.user_id AS applicant, auction_bids.*, auctions.title FROM auction_bids, auctions
INNER JOIN users ON auction_bids.user_id = users.id
WHERE auction_bids.owner_id = ".$_SESSION['userid']." AND auctions.user_id=".$_SESSION['userid']."
However, I'm not sure this is really what you want, as it will return only records where the specific user both 'owns' the item AND has bidded on it (both based on the userid session), rather than displaying all records from different people who have bidded on an item 'owned' by the user.
Something like: ?
SELECT auction_bids.user_id AS applicant, auction_bids.*, auctions.title FROM auction_bids, auctions
INNER JOIN users ON auction_bids.user_id = users.id,
WHERE auction.owner_id = ".$_SESSION['userid']."
Hope this points you in the right direction!
you have 2 where clauses, that is incorrect. I have revised your query based on your requirements.
SELECT auction_bids.user_id AS applicant, auction_bids.*, auctions.title
FROM auction_bids, auctions
INNER JOIN users ON auction_bids.owner_id = users.id
WHERE auction_bids.owner_id = ".$_SESSION['userid']."
AND auctions.user_id=auctions_bids.owner_id
I have a table called users which looks like:
-id
-email
-login
-admin
-coins
-cash
-premium
-IP
-pass
-ref
-signup
-online
-promote
-activate
-banned
-rec_hash
-country
-c_changes
-sex
-daily_bonus
If say user with id 81 referred 10 people then those 10 people would have "81" in their ref column.
I would like to create a top 5 referral table but I'm having trouble with the query and displaying that in PHP, would anybody be able to help?
I FORGOT TO MENTION IF THEY HAVE NO REFERRAL IT SHOWS AS 0 HOW WOULD I EXCLUDE 0 FROM BEING SHOWN AS A REFERRAL?
You can do it in a single SQL statement like this:
SELECT ref, COUNT(*) AS num FROM users
GROUP BY ref ORDER BY num DESC LIMIT 5
But that will just get you the 5 IDs, rather than their user rows. You can then perform a further query to get the actual rows. Alternatively, use the above query with a join to do it all in one.
IF THEY HAVE NO REFERRAL IT SHOWS AS 0
messy design - this should be null. Regardless...
SELECT u.login, ilv.referred
FROM
(SELECT ref, COUNT(*) AS referred
FROM users
WHERE ref IS NOT NULL
AND ref>0
GROUP BY ref
ORDER BY COUNT(*) DESC
LIMIT 0,5) ilv
INNER JOIN users u
ON ilv.ref=users.id
ORDER BY ilv.referred DESC;
Or and SQL like this:
SELECT u.*, COUNT(*) as referrers FROM users r JOIN users u ON r.ref = u.id
GROUP BY u.id ORDER BY referrers DESC LIMIT 5
It is faster to use just one statement even with a join on the same table.
I've been adding a like feature to an entries database... here's the structure of the DBs:
**Users**
user_id
user_name
etc.
**Entries**
entry_id
entry_content
etc.
**Likes**
user_id
entry_id
(It's a little more complicated than that, there are groups/categories, but that should explain it fine...) Here's the SQL query I'm working with at the moment:
SELECT
entries.*,
DATE_FORMAT(entry_date, "%M %D, %Y") as entry_date,
groups.group_short_name,
users.user_name, users.user_id,
FROM entries
INNER JOIN groups ON groups.group_id = entries.group_id
INNER JOIN users ON users.user_id = entries.user_id
ORDER BY entry_date DESC
I'm trying to also retrieve likes per entry with this query and wondering if it is possible. I've been trying:
COUNT(DISTINCT likes.like_id) as likes
with
LEFT JOIN likes ON likes.entry_id = entries.entry_id
But I don't think that is anywhere near right. Am I way off? Is this possible? Hope it all made sense.
Thanks for the help in advance.
Give your tables aliases, for one..
FROM entries e
Then add a column query:
select e.*, (select count(*) from Likes where entry_id = e.entry_id) as entry_likes
Add:
GROUP BY entries.entry_id
See if that works.