With the following tables
posts
id
post_id
user_id
comments
id
post_id
comment_id
user_id
deleted
replies
id
post_id
reply_id
user_id
deleted
I am trying to get every comment and reply from each post.post_id and post.user_id='x' and the comment or reply is not deleted(0)
this is what i have tried
SELECT *
FROM posts p
LEFT JOIN comments c ON p.id=c.post_id
LEFT JOIN replies r ON p.id=r.post_id
WHERE p.user_id=$user_id
&& c.deleted='0' && r.deleted='0'
which does not work...
You need to put the deleted check into the join clause. This should do it:
SELECT *
FROM posts p
LEFT JOIN comments c ON c.post_id = p.post_id AND NOT c.deleted
LEFT JOIN replies r ON r.post_id = p.post_id AND NOT r.deleted
WHERE p.user_id = $user_id
Note: Not sure if c.post_id joins to p.id or p.post_id - chenge the on clause as required
A post may have comments or not. Use LEFT JOIN instead of INNER JOIN.
A post may have replies or not. Use LEFT JOIN instead of INNER JOIN in that join too.
When LEFT JOIN is used, a condition like WHERE comments.deleted = 0 that includes a field (from the right table (comments) in the LEFT JOIN), the LEFT JOIN is cancelled. So, we should put this condition in the ON clause and not in the WHERE.
SELECT *
FROM posts p
LEFT JOIN comments c
ON p.post_id = c.post_id
AND c.deleted = 0
LEFT JOIN replies r
ON p.post_id = r.post_id
AND r.deleted = 0
WHERE p.user_id = $user_id
Thinking more clearly, the above will show what the question describes but in cases with say, 4 comments and 3 replies, 12 rows will be returned (3x4). Which is probably not wanted. The following 2nd try does not have such issue.
I don't see a post.text or comment.text or reply.text in the tables but anyway, you'll get the idea. You can remove the 3 text lines if not appropriate.
( SELECT p.post_id AS post_id
, 0 AS type
, p.post_id AS id
, p.text AS text
FROM posts p
WHERE p.user_id = $user_id
)
UNION ALL
( SELECT p.post_id AS post_id
, 1 AS type
, c.comment_id AS id
, c.text AS text
FROM posts p
JOIN comments c
ON p.post_id = c.post_id
WHERE p.user_id = $user_id
AND c.deleted = 0
)
UNION ALL
( SELECT p.post_id AS post_id
, 2 AS type
, r.reply_id AS id
, r.text AS text
FROM posts p
JOIN replies r
ON p.post_id = r.post_id
WHERE p.user_id = $user_id
AND r.deleted = 0
)
ORDER BY post_id
, post_type
, id
The 0,1,2 stand for post, comment, reply.
Related
I'm working on a project where I need to check if the user liked the post and then use COUNT() on it, if it gives 0 they haven't if it says 1 they have liked it
I tried using this query
SELECT P.id AS id
, U.username AS username
, P.body AS body
, P.timestamp AS timestamp
, COUNT(L.user_id) AS likes
, COUNT(LD.post_id) AS liked
FROM posts AS P
LEFT JOIN users AS U ON U.id = P.user_id
LEFT JOIN followers AS F ON F.user_id = 'user1'
LEFT JOIN likes AS L ON L.post_id = P.id
LEFT JOIN likes AS LD ON LD.post_id = P.id
AND LD.user_id = 'user1'
WHERE F.following_id = P.user_id
OR P.user_id = 'user1'
GROUP BY P.id
My entrys in my likes table are
UserId|PostId|timestamp
user1 |post1 |time
user2 |post1 |time
My problem is it keeps giving a 2 for the count of LD which shouldn't be possible
*Note: In my code I use :user through PDO I don't actually type the id like that
Edit:
$sql = "SELECT P.id AS id, P.user_id AS userid, U.username AS username, U.name AS name, U.verified AS verified, P.body AS body, P.data AS data, P.timestamp AS timestamp, P.type AS type, P.users AS users, COUNT(L.user_id) AS likes, COUNT(DISTINCT LD.post_id) AS liked FROM posts AS P LEFT JOIN users AS U ON U.id = P.user_id LEFT JOIN followers AS F ON F.user_id = :userid LEFT JOIN likes AS L ON L.post_id = P.id LEFT JOIN likes AS LD ON LD.post_id = P.id AND LD.user_id = :userid WHERE F.following_id = P.user_id OR P.user_id = :userid GROUP BY P.id";
$results = DB::query($sql, array(':userid' => $user_id));
I then loop through the results and format them into json
Can you try adding a DISTINCT keyword on the COUNT function for liked column?
COUNT(DISTINCT LD.post_id) AS liked
Most likely the joins are causing the likes table to be duplicated. Thus, we'll only count the unique posts (by post_id) using DISTINCT.
I have the following tables: users, posts, and likes
I want to receive a total count of 'likes' for that post but also the id of the like if it exists.
Do I need the 2 JOINS for the same table? Or is there a more efficient way
SELECT p.id, u.username, p.message, count(DISTINCT l.id) AS likeCount, l2.id AS likedID
FROM posts p
INNER JOIN users u ON p.userID = u.id
LEFT JOIN likes l ON p.id = l.postID
LEFT JOIN likes l2 ON l2.userID = ? AND l2.postID = p.id
WHERE p.status = 0
So I've got myself in a massive confused mess.
Basically, I have a little social network app and I have 4 different tables I need to combine into one view.
Table 1 - User posts
Table 2 - Users
Table 3 - Likes
Table 4 - Comments
I then need to return a list of posts, with the user details and then add columns for the number of likes and number of comments for each post respectively.
If a post doesn't have any likes or comments, then ideally we should show a Zero.
The query below joins everything up, but returns multiple rows of everything, as it is returning 1 row for each comment or like as well.
Anyone able to help me combine these together?
SELECT *
FROM app_posts AS p
LEFT JOIN app_comments AS c ON c.post_id = p.post_id
LEFT JOIN app_user AS u ON u.user_id = p.user_id
LEFT JOIN app_likes AS l ON l.post_id = p.post_id
WHERE u.user_banned = 0
AND p.post_public = 1
ORDER BY p.post_date DESC
Any help would be greatly appreciated!
Table columns are as follows;
app_likes
like_id
post_id
user_id
liked_date
app_comments
comment_id
comment_user_id
post_id
comment_body
comment_date
app_posts
post_id
user_id
post_content
post_date
post_public
app_user
user_id
user_first
user_last
user_avatar
user_banned
An example of what is returned currently is as follows (chopped down for easiness)
You'll see the post_id is repeated multiple times.
What I want it to return is the post_id just once, and with the count of 'likes' and 'comments' in new columns (I don't know how to do this).
Simon
You might be missing GROUP BY...
SELECT p.*,u.*,count(distinct c.comment_id),count(distinct l.like_id)
FROM app_posts AS p
LEFT JOIN app_comments AS c ON c.post_id = p.post_id
LEFT JOIN app_user AS u ON u.user_id = p.user_id
LEFT JOIN app_likes AS l ON l.post_id = p.post_id
WHERE u.user_banned = 0
AND p.post_public = 1
GROUP BY p.post_id
ORDER BY p.post_date DESC
Note that MySQL lets you be sloppy with GROUP BY like this, but a lot of other databases would require you to break out the "p.*" into explicit MAX(p.post_id),MAX(p.post_content), etc.
SELECT p.post_id, COUNT(c.post_id) as num_comments, COUNT(l.like_id) as num_likes
FROM app_posts AS p
LEFT JOIN app_comments AS c ON c.post_id = p.post_id
LEFT JOIN app_user AS u ON u.user_id = p.user_id
LEFT JOIN app_likes AS l ON l.post_id = p.post_id
WHERE u.user_banned = 0
AND p.post_public = 1
GROUP BY p.post_id
ORDER BY p.post_date DESC
Try to add Group by at the end of your query, like this
SELECT *
FROM app_posts AS p
LEFT JOIN app_comments AS c ON c.post_id = p.post_id
LEFT JOIN app_user AS u ON u.user_id = p.user_id
LEFT JOIN app_likes AS l ON l.post_id = p.post_id
WHERE u.user_banned = 0
AND p.post_public = 1
GROUP BY p.post_id
ORDER BY p.post_date DESC
So I have 3 tables called comments, users and posts.
I want to get
the "score" from comments
the "user reputation" for users by doing the left join between comments and users with c.user_id = u.id
get the "tags" from posts table by doing a left join between comments and posts on c.post_id = p.id.
BUT there is a trick here the tags should be based on the type of the posts (p.post_type_id).
So if the id = 1 then that means we have a "question" as a post and simply retrieve tag
Else if the id = 2 then that means we have an answer and to get the tag we have to look at its parent_id from posts table.
I tried to use WHERE, CASE, nested IF, and nested SELECT but all threw syntax errors
Finally, I tried to do the following but I got an empty result
SELECT c.score,
COALESCE (u.reputation) reputation,
COALESCE (p.tags) tags
FROM comments c
LEFT JOIN users u
ON c.user_id = u.id
LEFT JOIN posts p
ON (c.post_id = p.id AND p.post_type_id = 1) OR (c.post_id = p.id AND p.post_type_id = 2 )
WHERE (p.id = p.parent_id)
So how can I have the tags based on the two types ?
Just quick guess:
http://sqlfiddle.com/#!9/a1cc3/1
SELECT c.score,
u.reputation reputation,
IF(p.post_type_id=1,p.tags,
IF(p.post_type_id=2,parents.tags,'UNKNOWN POST TYPE')
) tags
FROM comments c
LEFT JOIN users u
ON c.user_id = u.id
LEFT JOIN posts p
ON c.post_id = p.id
LEFT JOIN posts parents
ON parents.id = p.parent_id
UPDATE Here is Postgres variant:
http://sqlfiddle.com/#!15/a1cc3/2
SELECT c.score,
u.reputation,
CASE p.post_type_id
WHEN 1 THEN p.tags
WHEN 2 THEN parents.tags
ELSE 'UNKNOWN POST TYPE'
END tags
FROM comments c
LEFT JOIN users u
ON c.user_id = u.id
LEFT JOIN posts p
ON c.post_id = p.id
LEFT JOIN posts parents
ON parents.id = p.parent_id
I've 3 tables to query. I make a select on the first one, depending on the two others. I must have only distinct id from the 1st table, but my query is returning some duplicates... http://sqlfiddle.com/#!2/3e3d6/1
My query:
SELECT p.*
FROM posts p, blogs_subscribed s
WHERE (p.user_id = s.user_id OR p.user_id = 1)
AND p.id NOT IN (
SELECT post_id
FROM posts_unsubscribed u
WHERE u.post_id = p.id
AND u.user_id = p.user_id);
SELECT p.*
FROM posts p, blogs_subscribed s
WHERE (p.user_id = s.user_id OR p.user_id = 1)
AND NOT EXISTS(
SELECT null
FROM posts_unsubscribed u
WHERE u.post_id = p.id
AND u.user_id = p.user_id);
Any idea please?
not entirely sure I understand what you are looking for, but I think this is what you want...
SELECT p.*
FROM posts p, blogs_subscribed s
WHERE (p.user_id = s.user_id OR p.user_id = 1)
AND p.id NOT IN (
SELECT post_id
FROM posts_unsubscribed u
WHERE u.post_id = p.id
AND u.user_id = p.user_id)
GROUP BY p.id;
SELECT p.*
FROM posts p, blogs_subscribed s
WHERE (p.user_id = s.user_id OR p.user_id = 1)
AND NOT EXISTS(
SELECT null
FROM posts_unsubscribed u
WHERE u.post_id = p.id
AND u.user_id = p.user_id)
GROUP BY p.id;