A simple MySQL query help - mysql

Been stuck with this fairly simple MySQL query for a day now! Can't believe how quickly I've forgotten the basics. I have 3 tables - user, post and favourite_post. The post table has a user_id that is a FK to user table.
The favourite_post table has
user_id REFERENCES user(id)
post_id REFERENCES post(id)
timestamp
When a user favourites a post, his user_id, the post_id and timestamp are inserted into the favourite_post table.
I use the following query to retrieve the 15 more recent posts
SELECT post.id, post.text, post.timestamp, post.user_id, user.username
FROM post
INNER JOIN user
ON post.user_id = user.id
ORDER BY post.id
DESC LIMIT 15;
What I need to do along with that is check if each post has been favourited by the current user(say user_id = 1) by joining with the favourite_post table.

SELECT p.id, p.text, p.timestamp, p.user_id, u.username,
IF(ISNULL(fp.post_id), 'No', 'Yes') has_favourite
FROM post p
INNER JOIN user u
ON p.user_id = u.id
LEFT JOIN favourite_post fp
ON p.id = fp.post_id
AND u.id = fp.user_id
ORDER BY p.id DESC
LIMIT 15;

Try this:
SELECT p.id, p.text, fp.timestamp, p.user_id, u.username
FROM post p INNER JOIN user u
ON p.user_id = u.id
LEFT JOIN favourite_post fp
ON fp.user_id = p.user_id AND fp.post_id = p.id
WHERE p.user_id = your_user_id
ORDER BY p.id DESC
LIMIT 15;

Related

SQL Query to select posts from user and followed users is not including own posts

I'm building a query that should return the last 10 posts (default ordering) by $userId and the users it is following.
SQL Fiddle with a minimal example of the query: https://www.db-fiddle.com/f/i5ByFAXwADj5pfjCTn1g1m/2
The database structure is pretty simple:
posts (id, root, user, content)
users (id, username)
following (user_id, followed)
This is the query I'm currently using to get all posts:
SELECT posts.id, posts.content, users.id AS user_id, users.username
FROM posts
LEFT JOIN users ON posts.user = users.id
WHERE LIMIT 10
The query is working but it is listing posts from every user without distinction.
This is the query I built to exclude posts from users that $userId is not following, but it doesn't include $userId's own posts:
SELECT posts.id, posts.content, users.id AS user_id, users.username
FROM following
LEFT JOIN posts ON posts.user = '$userId' OR posts.user = following.followed
LEFT JOIN users ON posts.user = users.id
WHERE (following.user_id = '$userId' OR following.user_id = NULL) LIMIT 10
I've tried replacing the LEFT JOIN posts with an INNER JOIN and a RIGHT JOIN with no success whatsoever. I'm not able to find the error, why isn't the query including posts made by $userId?
I have also tried selecting from posts and joining followers, but it is returning duplicated content:
SELECT posts.id, posts.content, users.id AS user_id, users.username
FROM posts
LEFT JOIN following ON following.user_id = '$userId'
LEFT JOIN users ON posts.user = users.id
WHERE (posts.user = '$userId' OR posts.user = following.followed)
LIMIT 10;
I was about to post a UNION solution
SELECT
post_id,
content,
user_id,
username
FROM
(SELECT
posts.id post_id,
content,
users.id user_id,
username
FROM
posts INNER JOIN
users
ON user = users.id
UNION SELECT
posts.id,
content,
users.id,
username
FROM
posts INNER JOIN (
following INNER JOIN
users
ON user_id = users.id
) ON user = followed
) p
WHERE
user_id = 1
LIMIT 10;
Then I saw #Gordon Linoff's solution which might be better - more concise, at least - but I don't think it works as posted.
SELECT
posts.id,
content,
users.id,
username
FROM
posts INNER JOIN
users
ON user = users.id
WHERE
users.id = 1
OR EXISTS (
SELECT
*
FROM
following
WHERE
followed = user
AND user_id = 1
)
LIMIT 10;
Get the posts from the table posts under your conditions and join to users:
select p.id, p.content, u.id AS user_id, u.username
from (
select *
from posts
where user = '$user_id'
or user in (select user_id from following where followed = '$user_id')
) p inner join users u on u.id = p.user
order by p.id desc limit 10
Note that as it is your requirement the results may not contain posts by the user '$user_id' if the last 10 posts are from the users that this user follows.
See the demo.
I'm building a query that should return the last 10 posts by $userId and the users it is following.
So, there are two tasks here:
Get first N records per group
Apply query to given user PLUS the same for the related users
I would do something like this (pseudo code):
ids = query('SELECT user_id FROM following WHERE followed = :id)', userId).pluck('user_id');
ids.push(userId);
SELECT x.id, x.user_id, x.content
FROM (
SELECT #num := IF(#id = user_id, #num + 1, 1) AS num,
#id := posts.user_id as x_user_id,
posts.*
FROM
(SELECT #num := null, #id := null) x,
posts
WHERE posts.user_id IN(:ids)
ORDER BY posts.id DESC
) AS x
WHERE x.num <= 10
(https://www.db-fiddle.com/f/aiBUwqDApJp6foyq13ZZ2u/1)
See:
S.O.: get first N records per group
S.O.: prepared statement with WHERE IN
If I understand correctly, you basically want an EXISTS clause:
SELECT p.id, p.content, u.id AS user_id, u.username
FROM posts p JOIN
users u
ON p.user = u.id
WHERE u.id <> ? -- ? is the userid AND
EXISTS (SELECT 1
FROM following f
WHERE f.followed = ? AND
f.user_id = u.id
)
LIMIT 10;

MySQL: Needing to return top 3 Users with the most votes. Results wanted in one column from the SUM of two subqueries. Java/Spring MVC

I have a Spring MVC blog with functionality for Post and Comment voting. I want to return the top 3 users based on number of votes they've received on all their posts and comments.
tables:
users u [id, username]
posts p [id, u.id]
comments c [id, p.id, u.id]
post_votes pv [p.id, u.id, type (1 or -1)]
comment_votes cv [c.id, u.id, type (1 or -1)]
The following statement gives me total votes per user by querying two separate voting tables and then adding the totals together:
SELECT
(SELECT SUM(type)
FROM posts_votes pv
JOIN posts p ON p.id = pv.post_id
JOIN users u ON u.id = p.user_id
WHERE u.id LIKE ?1)
+
(SELECT SUM(type)
FROM comments_votes cv
JOIN comments c ON c.id = cv.comment_id
JOIN users u ON u.id = c.user_id
WHERE u.id LIKE ?1)
That works fine with a WHERE clause per user id... But now I'm trying to find just the top 3 users that have the most votes and I'm having too much difficulty. This is what I have so far:
SELECT u.id, u.username, IFNULL(SUM(pv.type), 0) AS totalPostVotes
FROM posts_votes pv
JOIN posts p ON p.id = pv.post_id
JOIN users u ON u.id = p.user_id
GROUP BY u.id ORDER BY totalPostVotes DESC LIMIT 3
That above statement works by itself giving me: u.id, u.username, and totalPostVote in descending order. So does the one below for comments:
SELECT u.id, u.username, IFNULL(SUM(cv.type), 0) AS totalCommentVotes
FROM comment_votes cv
JOIN comments c ON c.id = cv.comment_id
JOIN users u ON u.id = c.user_id
GROUP BY u.id ORDER BY totalCommentVotes DESC LIMIT 3
Great! But I want that third column SUM result to be essentially "totalVotes" and contain the sum of both of those subqueries. Then I'll GROUP BY u.id ORDER BY totalVotes DESC LIMIT 3.
Something like this:
SELECT u.id, u.username, SUM(
(SELECT IFNULL(SUM(pv.type), 0) AS totalPostVotes
FROM posts_votes pv
JOIN posts p ON p.id = pv.post_id
JOIN users u ON u.id = p.user_id
GROUP BY u.id ORDER BY totalPostVotes DESC LIMIT 1)
+
(SELECT IFNULL(SUM(cv.type), 0) AS totalCommentVotes
FROM comments_votes cv
JOIN comments c ON c.id = cv.comment_id
JOIN users u ON u.id = c.user_id
GROUP BY u.id ORDER BY totalCommentVotes DESC LIMIT 1))
AS totalVotes from users u
GROUP BY u.id, u.username ORDER BY totalVotes DESC LIMIT 3
id | username | totalVotes
2 user2 11
1 user1 11
29 user29 11
What's happening is the result of totalVotes is indeed the correct vote count, 11, for the "top" user, but none of those users are the real top user, and the correct vote is being repeated 3 times in the guise of other users. I'm not even sure how users are being sorted at that point because they're not in an order I recognize.
The subqueries work separately (they give me the correct user) when I add SELECT "u.id, u.username " IFNULL(SUM()) but then if I run the whole block, I get the error "Operand should contain 1 column(s)" So I delete them and revert to only SELECT IFNULL(SUM())
I'm also noticing the subqueries are only allowed LIMIT 1. How would I get the top 3, then? Should I do a UNION somewhere or is "+" sufficient? This is rather confusing. Can someone please help me with this? Any help is appreciated. Thanks in advance!
Updated code, thank you Peter:
SELECT
u.username,
pv_sum.total AS postTotal,
cv_sum.total AS commentTotal,
IFNULL(pv_sum.total, 0) + IFNULL(cv_sum.total, 0) as totalVotes
FROM users u
LEFT JOIN (
SELECT p.user_id, IFNULL(SUM(pv.type), 0) AS total
FROM posts p
JOIN posts_votes pv ON pv.post_id = p.id
GROUP BY p.user_id
) pv_sum ON pv_sum.user_id = u.id
LEFT JOIN (
SELECT c.user_id, IFNULL(SUM(cv.type), 0) AS total
FROM comments c
JOIN comments_votes cv ON cv.comment_id = c.id
GROUP BY c.user_id
) cv_sum ON cv_sum.user_id = u.id
GROUP BY u.username, postTotal, commentTotal
ORDER BY totalVotes DESC LIMIT 3;
Don't place your subqueries in your SELECT-part, but join them on the users-table:
SELECT
u.username,
pv_sum.total AS postTotal,
cv_sum.total as commentTotal,
IFNULL(pv_sum.total, 0) + IFNULL(cv_sum.total, 0) as totalVotes
FROM users u
LEFT JOIN (
SELECT p.user_id, IFNULL(SUM(pv.type), 0) AS total
FROM posts p
JOIN post_votes pv ON pv.post_id = p.id
GROUP BY p.user_id
) pv_sum ON pv_sum.user_id = u.id
LEFT JOIN (
SELECT c.user_id, IFNULL(SUM(cv.type), 0) AS total
FROM comments c
JOIN comment_votes cv ON cv.comment_id = c.id
GROUP BY c.user_id
) cv_sum ON cv_sum.user_id = u.id
GROUP BY u.id
ORDER BY totalVotes DESC
LIMIT 3;
Fiddle: http://sqlfiddle.com/#!9/980cb2/11

Mysql query returning too many results

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.

How can I calculate the number of all rows when there are both LIMIT and JOIN clauses?

Here is my query:
select u.id, u.name,
(select count(*) from users where name = u.name) as total
from users u
where u.name = 'anything'
order by id
limit 1
As you know, my query returns 1 user which has anything name. And total contains the number of all users which have anything name. Ok all fine.
Now I want to do the same thing when there are three JOINs in the query. Please assume this:
select u.id, u.name, sum(r.reputation) rep
from users u
join reputation r on u.id = r.user_id
join posts_tags pt on r.post_id = pt.post_id
join tags t on pt.tag_id = t.id
where u.name = 'anything' and t.name = 'mytag'
group by u.id, u.name
order by rep desc, u.id
limit 1
Now I want to know, how can I implement total part in this ^ query?
Try this query ,hope this give you the right result.
select * , count(id) as total from (select u.id, u.name, sum(r.reputation) rep
from users u
join reputation r on u.id = r.user_id
join posts_tags pt on r.post_id = pt.post_id
join tags t on pt.tag_id = t.id
where u.name = 'anything' and t.name = 'mytag'
group by u.id, u.name
order by rep desc, u.id
limit 1 ) as result

Load three posts by user

I am looking to load all posts, sorted by newest first, but also limit them to three per user. I have no idea how to do that though! Here's the SQL I have currently to create the table and select the posts.
SELECT p.title, u.firstname, u.lastname
FROM post p
JOIN user u
ON p.user_id=u.id
ORDER
BY u.id, p.ctime DESC
#LIMIT TO 3 by user
;
This is what you can do
select
u.*,
p.*
from
user u
left join
(
select
p1.*
FROM
post p1
where
(
select
count(*)
from
post p2
WHERE
p1.user_id = p2.user_id
AND p1.id <= p2.id
) <= 3
order by p1.id desc
) p ON u.id = p.user_id
order by u.id
Took help from MySQL Limit LEFT JOIN Subquery after joining
Something like this might work
SELECT u.*,up.* FROM user u LEFT JOIN
(
SELECT `p`.`title`, `u`.`firstname`, `u`.`lastname`
FROM `post` `p`
ORDER
BY `p`.`ctime` DESC
LIMIT 3
)
up on up.user_id = u.id