I'm having some problems with a query I'm writing. This seems like table structure that is very frequent so I'd love some help.
Let's say I have 3 tables similar to a facebook structure. Users, Wall Posts, and Comments. Users can make wall posts, and comment on other wall posts.
On a users page I would like to show a users wall posts and a count of how many comments that post has. This is what I have so far
I query the Wall Post table using the users id as an inner join to the User table. That gives me a result set of wall posts for that user's page. Then I loop through that result set, take the Wall Post id from each result set, and query the Comment table for the Count of comments for that Wall Post Id. This works, however I have to hit the db twice. Can anyone think of a way that I could do this with one query?
First Query Example:
SELECT wallPost.*, user.currentDefault, user.displayName, user.userName
FROM wallPost
INNER JOIN user ON user.id = wallPost.sourceUserId
WHERE wallPost.recipientId = ? ORDER BY wallPost.id DESC
Second Query Example:
SELECT COUNT(id) AS count
FROM comment
WHERE wallPostId = ?
I would add the count as a subquery and join the subquery to the main query
SELECT
wallPost.*,
user.currentDefault,
user.displayName,
user.userName,
wallpost_commentcount.total
FROM
wallPost
INNER JOIN user ON user.id=wallPost.sourceUserId
LEFT JOIN (SELECT wallPostId,COUNT(*) as total FROM comment GROUP BY wallPostId) as wallpost_commentcount ON (wallpost_commentcount.wallPostId=wallPost.id)
WHERE
wallPost.recipientId = ?
ORDER BY wallPost.id DESC
Please make sure you have an index on comment.wallPostId otherwise this query will take a long time.
I used the LEFT JOIN because you always want to get the wallPost even if there are no comments records yet
Related
How can I get the number of posts that a user has posted using one MySQL query?
I can't really think of anything but this, but there is no aggregate function on the join. So I'm not sure how to proceed. I am positive that joins will not accomplish what I need.
select a1.username as Username
from `logs` as a1
left join `logs` as a2
on a1.username = a2.username
For example, my logs table is filled with information about posts people have made. I want to find how many posts each user has made, i.e.
Username, Posts
User1 100
User2 200
etc
EDIT: Sorry for not providing enough information.
I have a table called logs and it has two columns. One column is called username and another column is called msg. It basically holds information about posts that people have posted.
For example, let's say someone named Red posts Hello world. It will be saved to the table logs and a new row will be created. username will be Red, and msg will be Hello world
I basically want to get the number of messages that EVERY SINGLE user has posted by their username. I.e. here is an example of what I want
Username Posts
Red 1
Blue 10
Sally 30
try this
SELECT Username, count(Posts)
FROM `logs`
GROUP BY Username;
Good luck.
I'm assuming that when you say you "can't use count(*) in a join", you mean that you tried and saw that it didn't work, rather than you can't use COUNT at all. So I'm using it here.
You're right that a JOIN is the wrong place for a COUNT. You want it up in the SELECT column list, and a GROUP BY down below. Aggregate by Username, and count the number of entries in each aggregate.
SELECT Username, COUNT(*) AS Count
FROM logs
GROUP BY Username
This query may help you, change this query as for your requirement
SELECT users.*, count( posts.user_id )
FROM posts LEFT JOIN users ON users.id=posts.user_id
GROUP BY posts.user_id
may be like
SELECT users.username, count( logs.username ) as posts
FROM users LEFT JOIN logs ON users.username=posts.username
GROUP BY users.username
You know how the Facebook home feed lists all the recent posts? It shows the user that posted, their actual post and then the first few comments attached to that post. That's what I'm trying to achieve, but I'm having a hard time building a single query that can gather all that data.
I have 3 tables: Uses, Posts and Comments. Each has a unique ID, but they reference each other's IDs. i.e, the Comments table has columns for the user_id of the user who posted and the post_id of the post it is attached to.
At the minute I'm querying SQL to gather all the posts. I join my Users and Comments tables to learn the Username of the poster and a total of how many comments the post has, like so:
$query = "
SELECT `posts`.`id`,`posts`.`message`,`posts`.`link`,
`posts`.`posted`,`posts`.`category`,`posts`.`user_id`,
`users`.`username`,
count(`comments`.`id`)
FROM `posts`
INNER JOIN `users`
ON `posts`.`user_id`=`users`.`id`
JOIN `comments`
ON `comments`.`post_id`=`posts`.`id`
WHERE `posts`.`group_id` = '$id'
AND `posts`.`category`='$filter'
GROUP BY `posts`.`id`
ORDER BY `posts`.`posted`
DESC
";
But instead of finding how many comments a post has, I would instead like to read the first few posts. Can anyone think of a way to achieve this with just the one query?
You can use the LIMIT clause to the the "first" posts. By making the "first posts" a subquery and then joining in the comments you can get everything in a single query. The comments should be left-joined in case of posts with no comments.
Notes:
This query is untested, but it should be close.
This will get all comments for the first few posts, so you'll need to limit the display of "max 3 comments per post" using the front-end display code.
It may be possible to limit comments to 3 per post within this query using variables, but that's not something I know how to do.
Here's the query:
SELECT
FirstPosts.id,
FirstPosts.message,
FirstPosts.link,
FirstPosts.posted,
FirstPosts.posts,
FirstPosts.user_id,
FirstPosts.username,
comments.<< your comment column >>
FROM (
SELECT `posts`.`id`,`posts`.`message`,`posts`.`link`,
`posts`.`posted`,`posts`.`category`,`posts`.`user_id`,
`users`.`username`
FROM `posts`
INNER JOIN `users`
ON `posts`.`user_id`=`users`.`id`
WHERE `posts`.`group_id` = '$id'
AND `posts`.`category`='$filter'
ORDER BY `posts`.`posted` DESC
LIMIT 20) FirstPosts
LEFT JOIN comments ON FirstPosts.id = comments.post_id
ORDER BY FirstPosts.Posted, comments.<< column you use to determing comment order >>
If you determine comment order by a date or sequence, you'll have to ORDER BY FirstPosts.Posted, comments.whatever DESC.
Hope this helps!
I hope itll be legal to post this as i'm aware of other similar posts on this topic. But im not able to get the other solutions to work, so trying to post my own scenario. Pretty much on the other examples like this one, im unsure how they use the tablenames and rows. is it through the punctuation?
SELECT bloggers.*, COUNT(post_id) AS post_count
FROM bloggers LEFT JOIN blogger_posts
ON bloggers.blogger_id = blogger_posts.blogger_id
GROUP BY bloggers.blogger_id
ORDER BY post_count
I have a table with articles, and a statistics table that gets new records every time an article is read. I am trying to make a query that sorts my article table by counting the number of records for that article id in the statistics table. like a "sort by views" functions.
my 2 tables:
article
id
statistics
pid <- same as article id
Looking at other examples im lacking the left join. just cant wrap my head around how to work that. my query at the moment looks like this:
$query = "SELECT *, COUNT(pid) AS views FROM statistics GROUP BY pid ORDER BY views DESC";
Any help is greatly appreciated!
SELECT article.*, COUNT(statistics.pid) AS views
FROM article LEFT JOIN statistics ON article.id = statistics.pid
GROUP BY article.id
ORDER BY views DESC
Ideas:
Combine both tables using a join
If an article has no statistics, fill up with NULL, i.e. use a left join
COUNT only counts non-NULL values, so count by right table to give correct zero results
GROUP BY to obtain exactly one result row for every article, i.e. to count statistics for each article individually
I have a database of Facebook Likes from several people. There are duplicate "like_id" fields across many "user_id"s. I want a query that will find the amount of "like_id"s person A has in common with person B.
This query is fantastic for comparing likes when only 2 "user_id"s are in the database, but as soon as I add a 3rd, it messes it up. Basically, I want to see who has the most "likes" in common with with person A.
SELECT *,
COUNT(*)
FROM likes
GROUP BY like_id
HAVING COUNT(*) > 1
Anyone have a query that might work?
This SQL should work. You just need to put in the User A's user_id and it should compare with all other users and show the top matching one. You can change it to show the top 5 or do whatever else you need to do.
Basically what it is doing is that it is doing a self join on the table, but making sure that when it does a join, it is a different user_id but the "like" is the same. Then it does a group by each of the other user_id's and sums the same amount of likes for that user_id.
SELECT all_other_likes.user_id, count(all_other_likes.like_id) AS num_similar_likes
FROM likes original_user_likes
JOIN likes all_other_likes
ON all_other_likes.user_id != original_user_likes.user_id
AND original_user_likes.like_id = all_other_likes.like_id
WHERE original_user_likes = USER_ID_YOU_WANT_TO_COMPARE
GROUP BY all_other_likes.user_id
ORDER BY count(all_other_likes.like_id) DESC
LIMIT 1;
Not sure what database you are using. You might need to do a SELECT TOP 1 if it is MS-SQL, but this is valid PostgreSQL and MySQL syntax.
I think this will do it:
SELECT
likes_a.user_id,
likes_b.user_id
FROM
likes as likes_a JOIN likes as likes_b
ON
likes_a.like_id = likes_b.like_id
WHERE
likes_a.user_id <> likes_b.user_id
And then post-process the results to count up who has the most in common.
I am using this query to print out a forum board and all it's sub forums. What happens, as may be expected, is that all posts in all threads belonging to that forum are displayed. What I would like to happen is only the first post from each thread is displayed along with the forum title.
Query:
SELECT tf_threads.*, tf_posts.*
FROM tf_threads INNER JOIN tf_posts
ON tf_threads.thread_id=tf_posts.thread_id
AND tf_threads.parent_id=54 ORDER BY tf_posts.date ASC
Please note the parent_id field is given a variable in the real query.
So. If I make sense, can anyone help me out as to what query to write to only select the first post from each thread?
If there are no simple(ish) answers, how could I do it if I used a post number field in the second table, for example, the first post in thread has number 1, second post has number 2, etc. If I use this method, I'd obviously only like to select posts with a count number field of 1. I could just expand the original query with a AND post_number=1 right?
Thanks for reading,
James
Something like this?
http://murrayhopkins.wordpress.com/2008/10/28/mysql-left-join-on-last-or-first-record-in-the-right-table/
Edit: I think that it has to be something like this, but I'm also not an SQL expert:
SELECT tf_threads.*, tf_posts_tmp.*
FROM tf_threads
LEFT JOIN (SELECT p1.*
FROM tf_posts as p1
LEFT JOIN tf_posts AS p2
ON p1.postid = p2.postid AND p1.date < p2.date) as tf_posts_tmp
ON (tf_threads.thread_id=tf_posts_tmp.thread_id)