How to query multiple tables with a single query - mysql

I have three tables: user, friend_recommendation, and movie.
I would like to get the name of the person who recommended this movie to me, along with the movie information from the movie table, and the comment in the friend_recommendation table. How do I do this?
Assume that user table has name as an attribute, movie table has movie title as an attribute, and friend_recommendation has UID (user who recommends), FID (who is recommended), and COMMENT (what is the comment from UID to FID)
Is it even possible to do this in one query?

So three tables, essentially with these fields (greatly simplified):
users (userId, name)
movies (movieId, movieInfo)
recommendations (fromUserId, toUserId, movieId, comment)
SELECT fromUser.name, toUser.name, m.movieInfo, r.comment
FROM recommendations r
INNER JOIN movies m ON m.movieId = r.movieId
INNER JOIN users fromUser ON r.fromUserId = fromUser.userId
INNER JOIN users toUser ON r.toUserId = toUser.userId
WHERE r.toUserId = << my user id, or whatever your criteria are >>
Edit in response to the comments below: Also retrieving the name/details of the user the movie was recommended to is as simple as joining to the users table again. You can see in the JOINs that we select from users twice.

Related

How to combine multiple queries into same table but different columns?

So I have two tables that i'm working with.
Users which consists of:
ID, NAME, CITY
Follow which consists of:
User(id), Follow(id)
I am trying to write a query that returns to me a table of the name of the user, the name of the follower's name and the followers city. I have this query written which returns the correct information, but prints it out 8 times for each row and I have no idea why.
Here is a link to my code
https://www.db-fiddle.com/f/aDPgZFknC1ybteWM6hwoFJ/3
FROM
(SELECT user.name
from follow, user
where follow.user = user.id) as NAME,
(Select user.name
from follow, user
where follow.follow = user.id) as FOLLOWER,
(select city
from user
right join follow
on user.id = follow.follow) AS CITY;
When joining tables it would be better to use the JOIN syntax. You can use as your base the follow table and transform it through the use of JOIN to display the information that you need. On the following example, you join follow with the user table two times to get the user and follower information.
SELECT U.name, U2.name, U2.city
FROM follow AS f
JOIN user AS U
ON f.user=U.id
JOIN user AS U2
ON f.follow=U2.id;

Displaying profile_name from friends table

I am trying to display the profile name of a user's friend. I have a friends table (users table) and the myfriends table (friend table). Don't ask why the stupidly confusing names, it is what the assignment requires. Ill refer as the friendsID as user_id and myfriendsID as friends_ID just to eliminate confusion.
Now, so far I have got it to list the user_id and the friends associated with that ID.
I want it to be able to also display the friends names through a profile_name I have on the user_id table.
The SQL i have at the moment is:
SELECT friends.friend_id, myfriends.friend_id2, friends.profile_name
FROM friends
INNER JOIN myfriends
ON friends.friend_id = myfriends.friend_id1
WHERE friends.friend_id = '2'
Problem is, is that it lists the name of the USER_ID, not the friend_id. The profile names I am trying to obtain are also from the user_id table, but with their own unique user_id. If that makes sense?
Join back to friends/users again.
Inner join friends as f2
On f2.friend_id = myfriends.friend_id1

Searching multiple rows that are not in a relationship n:m

Similar question here
Very similar to the question above but with a slight difference, I need to find a list of users that haven't seen at least one film in a list of movies.
Assuming two tables 'movies' and 'users', there's an n:m relationship between those, and a table 'seen' describing that relationship.
I need to find out for any number of given users, and any number of given movies all the users, from that given list, that have not watched at least one of the given movies.
Is this achievable in a single query? I can't figure out a way of do that.
Edit: Here's a demo with an attempt to solve the problem, the issue with that is it returns users that not have seen all of the movies from the given list. What we need is a user that has not seen ANY of the movies from that list: http://rextester.com/DEIH39789
This query should give you your desired result. I'm assuming your basic structure is:
users (id int, name varchar(20));
movies (id int, title varchar(20));
seen (user_id int, movie_id int);
SELECT u.*
FROM users u
LEFT JOIN seen s
ON s.user_id = u.id AND s.movie_id IN (movielist)
WHERE s.user_id IS NULL AND u.id IN (userlist)
The WHERE s.user_id IS NULL condition means the LEFT JOIN gives you all the users who have not seen any of the movies in movielist, and the u.id IN (userlist) then restricts the results to only that set of users.
You would modify the IN clauses to match the list of movies and users you were interested in. I've made a small demo on Rextester.
Update
I had misinterpreted the question; the desired result is for users who have not seen one (or more) of the movies in the list. This query solves that problem:
SELECT u.*
FROM musers u
LEFT JOIN seen s
ON s.user_id = u.id AND s.movie_id IN (1, 2)
WHERE u.id IN (1, 2, 3)
GROUP BY u.id
HAVING COUNT(s.movie_id) < 2
The result of the JOIN and WHERE is users (1, 2, 3) and the movies they have seen. If they have seen all of the movies in the movie list (1, 2), the COUNT of movies in seen will be 2, otherwise, if they have not seen one (or more) it will be less than 2. Here's an updated demo. Note that when the length of the movie list changes, the 2 in the HAVING clause must change to match the length of the movie list.
considering a n:m relationship between Users and Movies table, with intermediate table Seen.
SELECT * FROM Users u WHERE NOT EXISTS (SELECT UserId FROM Seen s WHERE s.UserId = u.ID)
this query will return Users which does not have any related record in Seen table
I would say something like a left-joining the users-table to the seen-table (and join that table to the movies-table).
(edited the code due to a comment from MatBailie)
Add the list-restritctions in the JOIN-clause (and not the WHERE-clause as MatBailie pointed out to me) and you get something like (Code below should work on SQL-Server but something similar should work for MySql as well):
SELECT COUNT(Users.ID)
FROM Users
LEFT JOIN Seen ON Users.ID = Seen.UserID AND Users.something IN (list)
LEFT JOIN Movies ON Seen.MovieID = Movie.ID AND Movies.something IN (list)
WHERE Movies.ID IS NULL
GROUP BY User.ID -- <-- This is probably optional
But as there are usually multiple ways to get the same result, the adjusted version of my previous answer:
SELECT COUNT(Users.ID)
FROM Users
LEFT JOIN Seen ON Users.ID = Seen.UserID
LEFT JOIN Movies ON Seen.MovieID = Movie.ID
WHERE (Users.something IN (list) OR Users.something IS NULL)
AND (Movies.something IN (list) OR Movies.something IS NULL)
AND Movies.ID IS NULL
GROUP BY User.ID -- <-- This is probably optional
Third attempt: Get all user-ids in list that have seen one of the movies. Next, get all user-ids in list and subtract the user-ids that have seen one of these movies. Know that for large data sets (a couple of thousand is not large) this query might become slow. To test it I've removed "userid = 2 and movieid = 3" from the list of seen, else I would not get a result. Now I see that Nick has not seen any of the first three movies (referring to your rextester example)
SELECT *
FROM musers
WHERE musers.id IN (1,2,3)
AND musers.id NOT IN (
SELECT musers.id
FROM musers
JOIN Seen ON musers.ID = Seen.UserID
JOIN Movies ON Seen.MovieID = movies.ID
WHERE movies.id IN (1,2,3) )

Mysql query for fetching videos posted by friends of a particular user

Could anyone please help me to write a query in mysql to fetch the videos posted by friends of a particular user. I have 3 tables (users,friends and videos) and the table structure are as follows.
users - id
friends - id, user_id [from user table], friend_id [from user table]
videos - id, user_id [from user table], video_name
For ex: User with id 1 has 3 friends 2,3,and 4 mapped in friends table. Each friends has posted 2 videos, mapped in videos table. So when I login as user 1, i need to list the videos posted by 2,3 and 4 with their details fetched from user table.
Thanks in advance
JOIN is what you are looking for. This can be used to "link" multiple tables based on what is common between them. This is the backbone of Normalization
The first part will be to link the "Friends" table back to the "Users" table. I am going to use a local variable (#UserID) and set it to be yours. I really don't know what information you will want from the friends information, so I am going to use generics for last name and first name in all of the queries
SET #UserID = 1 -- Your UserID
SELECT u.LastName, u.FirstName
FROM Friends AS f
INNER JOIN Users AS u ON f.Friends_ID = u.User_ID
WHERE f.User_ID = #UserID
Then the second part is going to be linking the Users to the videos. This query will return the user name information along with the names of all the videos:
SELECT u.LastName, u.FirstName, v.video_name
FROM Users AS u
INNER JOIN Videos AS v ON u.User_ID = v.User_ID
So now we have the basics. For your example you want a user to get all of the videos from all of their friends. We can actually combine these 2 queries into one query to get this:
SET #UserID = 1 -- Your UserID
SELECT u.LastName, u.FirstName, v.video_name
FROM Friends AS f
INNER JOIN Users AS u ON f.Friends_ID = u.User_ID
INNER JOIN Videos AS v ON u.User_ID = v.User_ID
WHERE f.User_ID = #UserID

Better MySQL query?

Here's one example, I have a Car, User, Member, and Dealer tables. At the moment I'm trying to get the name of a dealer who owns a car by matching their userids up
Select userid, name FROM `User` WHERE userid IN
(SELECT userid FROM 'Car' WHERE userid in
(SELECT userid FROM `Member` WHERE userid IN
(SELECT userid FROM `Dealer`)))
This does what I want but I can't help feel there's a better way of doing it? Currently the userid in Car is a FK of the userid in Dealer which is a FK of the userid in Member which is a FK of the userid in User which stores the name.
Can I go straight to getting all the userid's and names of dealers who's id is in the Car table, whilst making sure they're actually a Dealer?
Basically your schema is a downstream schema
Users -> Members -> Dealer -> Car
Good thing is you made all the possible keys that you need here.
So to selct anything in any table just go down stream from Users for example for the data you want
Select * from `USER` u
where
dealer.user_id = car.user_id and
member.user_id = dealer.user_id and
u.user_id = member.user_id
The reason i went upstream in matching records is because we want to make as few matching operations as possible. As you can see user table is supposed to contain the maximum records. So i match with car table and get all the user_id where there is a match in dealer. similarly i go from dealer to member and then to user. this means all the records of users will be matched with a lot fewer records that they would have been if i went from users to members to dealer to car.
But this is not fool proof solution. it will depend on your data. because it may be a case where one user may have multiple cars, then it would be better to go downstream.
Use JOIN instead of subqueries to fetch the data.
Try this:
SELECT U.userid, U.NAME
FROM `User` U
INNER JOIN Car C ON U.userid = C.userid
INNER JOIN Member M ON C.userid = M.userid
INNER JOIN Dealer D ON M.userid = D.userid;