How to get data from table by intermediaries - mysql

I have three table:
// users
+----+------+------------+
| id | name | reputation |
+----+------+------------+
// posts
+----+-------+---------+
| id | title | user_id |
+----+-------+---------+
// votes
+----+---------+---------+
| id | user_id | post_id |
+----+---------+---------+
Note: user_id in votes is belong to who gives a vote. But user_id in posts table is belong to who wrote that post.
So I want to give +5 rep to the owner of post when his comment get a upvote.
Example: when userA gives a upvote to the post (wroted by userB), I want to run this:
update users set reputatuin=reputation+5 where id = {how get the id of userB}
Now I want to know, how should I obtain the userB (post owner who wrote it) id ?

In an UPDATE statement, you must join through posts to the votes table using MySQL's multi-table UPDATE syntax.
If you want to update by targeting the votes.id of the new vote, use that in the WHERE clause.
UPDATE
users
INNER JOIN posts ON users.id = posts.user_id
INNER JOIN votes ON votes.post_id = posts.id
SET
users.reputation = users.reputation + 5
WHERE votes.id = {vote id to update}
If your code is already aware of the posts.id of the voted post, then the votes table need not be joined and you can use users and posts.
UPDATE
users
INNER JOIN posts ON users.id = posts.user_id
SET
users.reputation = users.reputation + 5
WHERE posts.id = {post id of new vote}
This query can be done just as easily with a subquery in the WHERE clause.
UPDATE users
SET reputation = reputation + 5
WHERE
id = (SELECT user_id FROM posts WHERE post_id = {post id of new vote})

Related

Unable to write a proper query to join two tables and fetch what I need

I'm trying to fetch data from table1 which doesn't have a column with a specific value I include
Consider a social media site:
I have a "posts" table where I save all the posts by the user with their User id,
and I have the "follow" table where I save all the data like who's following who.
Now I'm trying to get all the data from the posts table where the user isn't following them
Example:
posts table
| u_id | Post |
|:---- |:----:|
| 1 |post1 |
| 2 |post2 |
| 1 |post3 |
| 3 |post4 |
follow table:
| u_id | following |
|:---- |:---------:|
| 2 | 1 |
| 1 | 3 |
Now the scenario: Let's say I'm the logged-in user with user id: 2, as per the requirement I should only see the posts of users that I'm not following, i.e., user #2 (which is me) and user #3 only.
So far the query I tried is:
$query = "SELECT posts.id, posts.u_id, posts.title, posts.date
FROM posts
LEFT JOIN follows
ON posts.u_id=follows.u_id
WHERE (follows.u_id != '$u_id')";
The $u_id is the logged-in user's id which is in the above scenario #2.
Now I m trying to get all the data from the posts table where the user isn't following them
SELECT p.id, p.u_id, p.title, p.date
FROM posts p LEFT JOIN
follows f
ON p.u_id = f.following AND
f.u_id = ?
WHERE u.u_id IS NULL;
LEFT JOIN is a fine approach. However, this is looking at what posts match following not u_id. Then you want to return the rows where there are no matches.
You ask to find posts of users which you are not following. Use NOT IN, NOT EXISTS, or LEFT JOIN:
SELECT *
FROM posts
WHERE posts.u_id NOT IN (SELECT following FROM follows WHERE u_id = 'me')
AND posts.u_id <> 'me' -- Remove this line if you want to see your posts.
;
I assume you wouldn't follow yourself. Just remove the noted line in the WHERE clause to see your posts.
This is similar to the LEFT JOIN approach.

Joining 3 tables and using SUM to calculate ratings of forum posts belonging to one thread

I'm doing a simple discussion board for my school project. I have these tables to store users, posts and ratings for those posts (I'll leave out columns and tables that are insignificant for this question.
+------------+
| User |
+------------+
|PK| id_user|
+------------+
| username|
| profile_pic|
+------------+
+------------+
| Post |
+------------+
|PK| id_post|
+------------+
| id_user|
| id_thread|
| content|
| date_posted|
| deleted|
+------------+
+------------+
|Post_ratings|
+------------+
|PK| id_voter|
|PK| id_post|
+------------+
| rating|
+------------+
What I want to do is select all rows from the Post table with a specific id_thread, join it with the User table to select the username and profile_pic of the poster of each post, and a sum of ratings given to each post, so the columns of the result should be id_post, id_user, content, date_posted, deleted, username, profile_pic, and rating.
I managed to come up only with this sloppy query:
SELECT * FROM Post p LEFT JOIN
(SELECT id_user, username, profile_pic FROM User) u
ON p.id_user = u.id_user LEFT JOIN
(SELECT id_post redundant, SUM(rating) rating FROM Post_ratings) pr
ON p.id_post = pr.redundant
WHERE id_thread = 5 AND deleted = 0
ORDER BY date_posted
This does return all posts belonging to one thread, but it shows post's ID twice (the redundant column) and displays SUM of all ratings across all threads in a row with the lowest id_post, shows NULL in other rows.
If anyone can help, thank you in advance.
I would use a join to associate the users and the posts table, and a subquery to sum the ratings:
select p.*, u.username, u.profile_pic,
(select sum(pr.rating) from post_ratings pr where pr.id_post = p.id_post) as rating
from posts p
inner join users u on u.id_user = p.id_user
where p.id_thread = 5 and p.deleted = 0
Of course, you can also use outer aggregation:
select p.*, u.username, u.profile_pic, sum(pr.rating) as rating
from posts p
inner join users u on u.id_user = p.id_user
inner join post_ratings pr on pr.id_post = p.id_post
where p.id_thread = 5 and p.deleted = 0
group by p.id_post, u.user_id
MySQL understand functionally-dependent columns, so it is sufficient to put the primary key of the posts and users table in the group by clause.

MySQL joining table's maximum row contents

For a simple forum I would like to list the threads in order from most recent post. That I have completed with no issue with the following query:
SELECT t.thread_id, t.title, MAX(m.post_id) as latest_reply
FROM forum_thread as t
LEFT JOIN forum_post as n ON(latest_reply = m.thread_id)
WHERE t.forum_id=:forum_id AND t.sticky=0
GROUP BY t.thread_id
ORDER BY latest_reply DESC
LIMIT :limit_bottom, :limit_top
This works fine, until I want more details from the maximum post row. If I select more info from the post table, the results are random
I would like to also know, the post title, and then the username who posted.
The thread table [forum_thread] looks like the following:
thread_id | forum_id | title | sticky | post_count | view_count | closed
The post table [forum_post] looks like the following:
post_id | user_id | thread_id | timestamp | title | post_body_cache
And the user table [user] looks like the following:
id | username
I need to join the maximum post row to get the title, and than join the user table to get the username. What is the most efficient way of doing this? Everything I have tried has returned a fatal error.
Desired results would be:
[forum_thread] thread_id | [forum_thread] title | [forum_post] MAX post_id | [forum_post] title | [user] username
To get the entire row from the outer table, you have to move the Max to a subquery and use it in the joining criteria:
SELECT t.thread_id, t.title, p.author, p.post_date, p.whatever
FROM forum_thread as t
LEFT JOIN forum_post as p
ON p.thread_id = m.thread_id
and p.post_date =(
select Max( p.post_date )
from forum_post
where thread_id = p.thread_id )
WHERE t.forum_id = :forum_id
AND t.sticky= 0

MySQL database: Query table and join with "valid from" date column of other table

I cannot come up with a valid SQL Query for my problem, which I will describe in the scenario of a Blog. I have three tables: User, Blog – for blog entries, and UserStatus which holds the users' status he is assigned.
The tables look like that:
User
ID | Name
Blog
ID | User_ID | Date | Text
UserStatus
ID | User_ID | Valid_From_Date | Status
I guess you can imagine what entries of User and Blog would look like. Here is how UserStatus could look for one user:
ID | User_ID | Valid_From_Date | Status
34 |  7 | 2012-01-01 | Basic
35 |  7 | 2013-04-01 | Premium
36 |  7 | 2014-08-01 | Gold
The user's valid status at a certain date is the most recent one which satisfies Valid_From_Date<=Date. So on '2014-03-30' the valid status of this user is 'Premium'.
Now, what I want is to get all blog entries together with the users' names and valid status.
I have this approach:
SELECT User.Name, UserStatus.Status, Blog.Date, Blog.Text
FROM Blog
JOIN User ON User.ID = Blog.User_ID
JOIN UserStatus ON User.ID = UserStatus.User_ID
JOIN (Select User_ID, max(Valid_From_Date) AS date_for_most_recent_status FROM UserStatus
WHERE date_for_most_recent_status <= ??? GROUP BY User_ID) AS recent_user_status
ON recent_user_status.User_ID = UserStatus.User_ID
AND date_for_most_recent_status = UserStatus.Valid_From_Date
??? -> Can I relate to the particular Blog.Date of current entry when joining?
And that approach:
SELECT User.Name, UserStatus.Status, Blog.Date, Blog.Text, max(Valid_From)
FROM Blog
JOIN User ON User.ID = Blog.User_ID
JOIN UserStatus ON User.ID = UserStatus.User_ID
WHERE UserStatus.Valid_From_Date <= Blog.Date
GROUP BY Blog.Date, User.Name, Blog.Text
Here the good thing is that I can relate to the actual Blog.Date since it is just on Select-Statement. However, I don't know how to handle UserStatus.Status, which should be in the GROUP BY expression but cannot be, since I just want the most recent one.
Can anyone help me out here, please?
Correlated subquery taking the value of a column in a table and mapping it to a value in a sub table. In this case we know we want the max valid_from_Date for each user so we use the userID from an table outside the subequery and the userID on the table inside the subquery and return just the max and using that as the criteria to determine which user status record to limit by on the join.
SELECT User.Name, UserStatus.Status, Blog.Date, Blog.Text
FROM Blog
JOIN User
ON User.ID = Blog.User_ID
JOIN UserStatus
ON User.ID = UserStatus.User_ID
and Valid_from_date = (Select max(Valid_From_Date)
FROM UserStatus
where user_ID = User.ID
and UserStatus.Valid_from_Date <= Blog.Date)
If you want data user wise then use below:
SELECT User.Name, a.Status, Blog.Date, Blog.Text, a.valid_from_date
FROM Blog
JOIN USER ON User.ID = Blog.User_ID
JOIN
(SELECT user_id,`status`,valid_from_date FROM userstatus ORDER BY valid_from_date DESC) a
ON a.user_id=User.ID
WHERE a.Valid_From_Date <= Blog.Date
GROUP BY a.user_id;
If you want blog date, user, text wise then use:
SELECT User.Name, a.Status, Blog.Date, Blog.Text, a.valid_from_date
FROM Blog
JOIN USER ON User.ID = Blog.User_ID
JOIN
(SELECT user_id,`status`,valid_from_date FROM userstatus ORDER BY valid_from_date DESC) a
ON a.user_id=User.ID
WHERE a.Valid_From_Date <= Blog.Date
GROUP BY Blog.Date, a.user_id, Blog.Text;

selecting from multiple tables (mysql)

table 1
id | question
1 | who will win the election
table 2
id | answers | question id
1 | I will | 1
table 3
id | photo | question id
1 | xy.gif| 1
table 4 * users can both suggest questions and vote for others
id | username
1 | joe
table 5
id | vote | question_id | user_id
1 | He | 1 | 1
What is the query that will get me the following information in one query
t1.* (all the questions)
t2 all the answers connected to the questions
t3 all the photos related to the questions
t4 usernames of the author for each question
t5 the votes for the questions (it is possible that some questions will not have a vote of the logged in user)
my problem is the last point, getting the votes (while not all questions have votes by the specific logged user)
Here is how my query looks like:
SELECT
poll_questions.id,
poll_questions.question,
poll_questions.qmore,
poll_questions.total_votes,
poll_questions.active,
poll_questions.created_at,
poll_answers.answer,
poll_answers.votes,
poll_answers.id AS answer_id,
poll_photos.photo_name_a,
vote_history_raw.vote,
users.username
FROM poll_questions
LEFT JOIN (poll_answers, poll_photos)
ON (poll_answers.question_id = poll_questions.id AND
poll_photos.question_id = poll_questions.id
)
LEFT JOIN users ON poll_questions.author = users.id
INNER JOIN vote_history_raw ON users.id = vote_history_raw.user_id
WHERE poll_questions.active = 1
ORDER BY poll_questions.created_at DESC
thanks much!
Didn't try it, but does this work?
SELECT
poll_questions.id,
poll_questions.question,
poll_questions.qmore,
poll_questions.total_votes,
poll_questions.active,
poll_questions.created_at,
poll_answers.answer,
poll_answers.votes,
poll_answers.id AS answer_id,
poll_photos.photo_name_a,
vote_history_raw.vote,
users.username
FROM poll_questions
LEFT JOIN (poll_answers, poll_photos)
ON (poll_answers.question_id = poll_questions.id AND
poll_photos.question_id = poll_questions.id
)
LEFT JOIN users ON poll_questions.author = users.id
LEFT JOIN vote_history_raw ON (users.id = vote_history_raw.user_id OR users.id <> vote_history_raw.user_id AND vote_history_raw.user_id IS NOT NULL)
WHERE poll_questions.active = 1
ORDER BY poll_questions.created_at DESC