How to query DB table with additional fields from another table - mysql

I'm working on a HN-style database, and I'm trying to query a list of 'posts', that contain a 'userHasVoted' property for each one (this checks to see if the user currently logged in has voted for that specific post). (Note: the userHasVoted field does not exist - but needs to be dynamically created if the user has voted for a specific post.)
The 'posts' live in a separate table as the 'votes', joined on post.id and votes.postId.
How can I query the DB to show every post with this property, not limited to just the posts the user has voted for?

It's not entirely clear to me what you are asking, but I think the most likely scenario is you used an INNER JOIN (perhaps expressed as just JOIN), which will only return results where a record from both tables actually exists. What you want is an outer join, which will let you keep records from the first table if no record matches in the second:
SELECT p.*, case when v.postid is not null then 1 else 0 end as UserHasVoted
FROM posts p
LEFT JOIN votes v ON v.postid = p.id and v.userid = #UserID

Related

Combine data of three tables in SQL

I'm working on a SQL-statement which will involve 3 different tables. Below you can see the structure of them with one example data each.
FOLLOWERS-TABLE: This table contains information about the current user ('user') following another user ('following')
MESSAGES: This table contains information about messages of a certain user
LIKES: This table contains information about who liked other messages. 'idmessage' is the same as the 'id' in the Messages-table
First of all, I wanted to display all the messages of the persons who the user follows. I used this query:
SELECT *
FROM Messages
LEFT JOIN Followers ON Followers.following=Messages.user
WHERE Followers.user = 'Peter Jackson'
This resulted in this:
It is working. However, in the next step I should add a custom column to it, this must be 'alreadyLiked'. This should check if the user already liked the message. The value must be 'yes' or 'no'. Therefore, information about the Likes-table is needed. I guess I should make another join on 'Likes.idmessage' and 'Messages.id'. But I don't know how to give it a value 'yes' or 'no'. In the end, it should be like this:
On the server-side I use Node.js and the client-side AngularJS.
If I understood correctly then this should work:
SELECT Messages.*, Followers.*,
CASE WHEN Likes.id IS NOT NULL THEN 'yes' ELSE 'no' END AS alreadyLiked
FROM Messages
LEFT JOIN Followers ON Followers.following=Messages.user
LEFT JOIN Likes ON Likes.idmessage = Messages.id
WHERE Followers.user = 'Peter Jackson'
The way this works is simply by left joining the Likes table. Now if there is a no link found between the message table and the like table then the fields from the Like table will be null. Then we use the case operator to check each row if any field from the like table is or isn't null.

How can I filter out results based on another table. (A reverse join I guess?)

Basically, I have a table which contains two fields: [id, other] which have user tokens stored in them. The goal of my query is to select a random user that has not been selected before. Once the user is selected it is stored in the table shown above. So if Jack selects Jim randomly, Jack cannot select Jim again, and on the flip side, Jim cannot select Jack.
Something like this is what comes to mind:
SELECT * FROM users
WHERE (SELECT * FROM selected WHERE (id=? AND other=?) OR (id=? AND other=?));
Well, first of all I've read that uses sub-queries like this is extremely inneficient, and I'm not even sure if I used the correct syntax, the problem is however, that I have numerous tables in my scenario which I need to filter by, so it would look more like this.
SELECT * FROM users u
WHERE (SELECT * FROM selected WHERE (id=? AND other=?) OR (id=? AND other=?))
AND (SELECT * FROM other_table WHERE (id=? AND other=?) OR (id=? AND other=?))
AND (SELECT * FROM diff_table WHERE (id=? AND value=?))
AND u.type = 'BASIC'
LIMIT = 1
I feel like there's a much, much more efficient way of handling this.
Please note: I don't want a row returned at all if the users id is present in any of the nested queries. Returning "null" is not sufficient. The reason I have the OR clause is because the user's id can be stored in either the id or the other field, so we need to check both.
I am using Postgre 9.5.3, but I added the MySQL tag as the code is mostly backwards comptable, Fancy Postgre only solutions are accepted(if any)
You can left join to another table, which produces nulls where no record is found:
Select u.* from users u
left selected s on s.id = u.id or s.other = u.other
where s.id is null
The or in a join is different, but should work. Example is kinda silly...but as long as you understand the logic. Left join first table to second table, where second table column is not null means there was atleast one record found that matched the join conditions. Where second table column is null means no record was found.
And you are right...avoid the where field = (select statement) logic when you can, poor performer there.
Use an outer join filtered on missed joins:
SELECT * FROM users u
LEFT JOIN selected s on u.id in (s.id, s.other) and ? in (s.id, s.other)
WHERE u.id != ?
AND s.id IN NULL
LIMIT 1

Filter out records with at least one association that doesn't meet given conditions

Let's say I have posts and categorizations.
Post(id)
Categorization(post_id, topic_id)
I'd like to fetch posts that don't belong to a specific topic id.
In my case I have to use an inner join when joining Post to Categorizations as i have other filters to execute.
How do I go about this?
I have tried the following:
Post.joins(:categorizations).where("categorizations.topic_id != ?", doomed_topic_id)
But this returns posts that still have OTHER topics. it only works with posts with just one single topic that happens to be the unwanted one.
For instance, if I have a post with 2 categories (the doomed topic_id AND another topic) this query fails and actually fetches it, instead of filtering it out.
Try:
Posts.where('not exists
(select * from categorizations
where post_id = posts.id and
topic_id = ?)', doomed_id)

How to make a selection of posts from the database with the condition?

How to choose the posts from the database with the condition. Already 2 days of head scratching do not understand how to win the query) I have 3 tables:
I need to choose the posts as shown on the picture:
I get to make a selection of all positions whose pivot based table has a status of checked without conditions. But you must choose the post as shown in the picture, for example a post with id 39 if his column cheked status is checked in all rows. This means that all users have approved the post and it should show. Please tell me how to do such a condition in the query?
SELECT p.*
FROM posts p
WHERE NOT EXISTS( SELECT 'a'
FROM post_user pu
WHERE pu.post_id = p.id
AND pu.checked = 'notChecked'
)

Turn two queries into one

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