Retrieving data from 3 Mysql tables - mysql

Suppose I have 3 different tables relationships as following
1st is tbl_users(id,gender,name)
2nd is tbl_feeds(id,user_id,feed_value)
3rd is tbl_favs(id,user_id,feed_id)
where id is primary key for every table.
Now suppose I want to get data where those feeds should come which is uploaded by Gender=Male users with one field in every row that should say either the user who is calling this query marked that particular feed as favourite or not.
So final data of result should be like following :
where lets say the person who is calling this query have user_id=2 then is_favourite column should contain 1 if that user marked favourite that particular feed otherwise is_favourite should contain 0.
user_id feed_id feed_value is_favourite gender
1 2 xyz 1 M
2 3 abc 0 M
3 4 mno 0 M
I hope you getting my question , I m able to get feeds as per gender but problem is I m facing problem to get is_favourite flag as per particular user for every feed entry.
I hope some one have these problem before and I can get help from those for sure.
I would be so thankful if some one can resolve my this issue.
Thanks

Something like this should work:
SELECT
u.id AS user_id.
fe.id AS feed_id,
fe.feed_value,
IFNULL(fa.is_favourite, 0),
u.gender
FROM
tbl_users u
JOIN
tbl_feeds fe ON (fe.user_id = u.id)
LEFT JOIN
tbl_favs fa ON (
fa.user_id = u.id
AND
fa.feed_id = fe.id
)

In order to link your tables, you need to find the most common link between them all. This link is user_id. You'll want to create a relationship between all tables with JOIN in order to make sure each and every user has data.
Now I don't know if you're planning on making sure all tables have data with the user_id. But I would use INNER JOIN as it will ONLY show records of that user_id without nulls. If the other tables could POSSIBLY (Not always guaranteed) you should use a LEFT JOIN based on the tables that is it possible with.
Here is an SQLFiddle as an example. However, I recommend you name your ID fields as appropriate to your table's name so that way, there is no confusion!
To get your isFavorite I would use a subquery in order to validate and verify if the user has it selected as a favorite.
SELECT
u.userid,
u.gender,
f.feedsid,
f.feedvalue,
(
SELECT
COUNT(*)
FROM
tbl_favs a
WHERE
a.userid = u.userid AND
a.feedsid = f.feedsid
) as isFavorite
FROM
tbl_users u
INNER JOIN
tbl_feeds f
ON
u.userid = f.userid
~~~~EDIT 1~~~~
In response to your comment, I have updated the SQLFiddle and the query. I don't believe you really need a join now based on the information given. If you were to do a join you would get unexpected results since you would be trying to make a common link between two tables that you do not want. Instead you'll want to just combine the tables together and do a subquery to determine from the favs if it is a favorite of the user's.
SQLFiddle:
SELECT
u.userid,
f.feedsid,
u.name,
u.gender,
f.feedvalue,
(
SELECT
COUNT(*)
FROM
tbl_favs a
WHERE
a.userid = u.userid AND
a.feedsid = f.feedsid
) as isFavorite
FROM
tbl_users u,
tbl_feeds f
ORDER BY
u.userid,
f.feedsid

Related

How to Merge Two Select Query in Mysql

I have below table structure in my users table where user and driver are saved with a unique id and separated with user_type column. user ratings are being saved in rider_avg_ratings column and driver ratings are being saved in driver_avg_ratings column.
when a user submit a request it is being saved in requesttable with both userid and nearby driver id. now I have to return both driver ratings and user ratings from users table in a single query.Problem is when I join request.userid=users.id it is returning rider_avg_ratings to get the driver_avg_ratings i need to join users.id=request.driver_id how can I get both user and driver ratings from a single query
From above two table by joinning request.user_id=users.id I need to return driver_avg_ratings=4.38 and rider_avg_ratings=1.25
SELECT r.user_id as userId, u.rider_avg_ratings as ratings
FROM user as u
INNER JOIN request as r on u.id = r.user_id
UNION
SELECT r1.driver_id as userId, u1.driver_avg_ratings as ratings
FROM user as u1
INNER JOIN request as r1 on u1.id = r1.driver_id
This query will fetch the desired result.
Your union query should produce a The used SELECT statements have a different number of columns error. You need to figure out why your code isn't checking for errors and make it do so. Also, if this is the entire statement, the parentheses around it don't belong.
You need to make the two unioned selects return the same number of parameters. Note that as in the second select's select columns will be ignored and result column names will come from the first select only. Minimally, to make the query run, you would need to add ,NULL,NULL,... to your second select, but it seems likely you want more information to be able to identify which user the driver_avg_ratings is for, like the id, name, etc the first query is returning.
It's often helpful when processing the results to add something indicating which select a row came from, too, e.g. SELECT "pending_request_users" AS type, ... UNION ALL SELECT "all_users_avg_rating", ....
Note that the different SELECTs unioned together return entire rows. If your only goal is to add driver ratings to the rows already returned, you don't want a union, you may just want to add:
SELECT ..., driver.driver_avg_ratings
FROM user u
...
INNER JOIN user AS driver ON driver.id=req.driver_id
(or left join if there may not be a driver_id). But it's hard to tell for sure if that's what you want. Provide sample data and your desired results from it. In any case, do make your code detect errors properly.
for example:
Department
1 A
2 B
3 A
4 C
5 B
6 D
7 E
8 F
You could do something like
SELECT
1 AS Deptnumber
, Dept
FROM tbl_students
WHERE Dept IN ('A', 'B', 'C')
UNION
SELECT
2 AS DeptNumber
, Dept
FROM tbl_students
WHERE Dept IN ('D', 'E')
UNION
SELECT
3 AS Deptnumber
, Dept
FROM tbl_students
WHERE Dept IN ('F')

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) )

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;

LEFT JOIN but with WHERE criteria, rows getting lost

I have a simple database with three tables. In the database I have a table for users of my system, a table for applications to a competition, and an intermediary table that allows me to track which users have selected which applications to view.
Table 1 = users (user_id, username, first, last, etc...)
Table 2 = applications (application_id, company_name, url, etc...)
Table 3 = picks (pick_id, user_id, application_id, picked)
I am trying to write an SQL query that will show all the applications that have been submitted and if any individual application has been selected by a user will show that it has been "picked" (1=picked, 0=not picked).
So for user_id = 1 I'd like to see:
Column Names (application_id, company_name, picked)
1, Foo, 1
2, Bar, 1
3, Alpha, Null
4, Beta, Null
I tried it with the following query:
SELECT applications.application_id, applications.company_name, picks.picked
FROM applications
LEFT JOIN picks ON applications.application_id = picks.application_id
ORDER BY applications.application_id ASC
Which is returning this:
1, Foo, 1
1, Foo, 1
2, Bar, null
3, Alpha, null
4, Beta, null
I have a second user (user_id = 2) that also picked application 1 ("Foo") which I know is returning the second row.
Then I tried to limit the scope by specifying user_id = 1 here:
SELECT applications.application_id, applications.company_name, picks.picked
FROM applications
LEFT JOIN picks ON applications.application_id = picks.application_id
WHERE user_id = 1
ORDER BY applications.application_id ASC
Now I'm only getting:
1, Foo, 1
Any suggestions on how I can get what I'm looking for? Again, ideally for a single user I'd like to see:
Column Names (application_id, company_name, picked)
1, Foo, 1
2, Bar, 1
3, Alpha, Null
4, Beta, Null
You have a so-called join table in your database schema. In your case it's called picks. This allows you to create a many-to-many relationship between your users and applications.
To use that join table correctly you need to join all three tables. These queries are easier to write if you use table aliases (applications AS a, etc.)
SELECT a.application_id, a.company_name, p.picked, u.user_id, u.username
FROM applications AS a
LEFT JOIN picks AS p ON a.application_id = p.application_id
LEFT JOIN users AS u ON p.user_id = u.user_id
ORDER BY a.application_id, u.user_id
This will give you a list of all applications with the users who have made them. If no users are related to an application, the LEFT JOIN operations will retain the application row and you'll see NULL values for columns from the picks and users table.
Now, if you add a WHERE p.something = something or u.something = something clause to this query in an attempt to narrow down the presentation, it has the effect of converting the LEFT JOIN clauses into INNER JOIN clauses. That is, you won't retain the applications rows that don't have matching rows in the other tables.
If you want to retain those unmatched rows in your result set, put the condition in the first ON clause instead of the WHERE clause, like so.
SELECT a.application_id, a.company_name, p.picked, u.user_id, u.username
FROM applications AS a
LEFT JOIN picks AS p ON a.application_id = p.application_id AND p.user_id = 1
LEFT JOIN users AS u ON p.user_id = u.user_id
ORDER BY a.application_id, u.user_id
Edit Many join tables like your picks table are set up with a composite primary key, in your example (application_id, user_id). That ensures just one row per possible relationship between the tables being joined. In your case you have the potential for multiple such rows.
To use only the most recent of those rows (the one with the highest pick_id) takes a little more work. You need a subquery (virtual table) to extract it, and to retrieve the appropriate value of picked so your query works. So now things get interesting.
SELECT MAX(pick_id) AS pick_id,
application_id, user_id
FROM picks
GROUP BY application_id, user_id
retrieves the unique relationship pair. That is good. But next we have to fetch the picked column detail value from those rows. That takes another join, using the MAX value of pick_id, like so
SELECT q.application_id, q.user_id, r.picked
FROM (
SELECT MAX(pick_id) AS pick_id,
application_id, user_id
FROM picks
GROUP BY application_id, user_id
) AS q
JOIN picks AS r ON q.pick_id = r.pick_id
So, we need to substitute this little virtual table (subquery) in place of the pick AS p table in the original query. That looks like this.
SELECT a.application_id, a.company_name, p.picked, u.user_id, u.username
FROM applications AS a
LEFT JOIN (
SELECT q.application_id, q.user_id, r.picked
FROM (
SELECT MAX(pick_id) AS pick_id,
application_id, user_id
FROM picks
GROUP BY application_id, user_id
) AS q
JOIN picks AS r ON q.pick_id = r.pick_id
) AS p ON a.application_id = p.application_id AND p.user_id = 1
LEFT JOIN users AS u ON p.user_id = u.user_id
ORDER BY a.application_id, u.user_id
Some developers prefer to create VIEW objects for subqueries like the one here, rather than creating a club sandwich of a query like this one. It's not called Structured Query Language on a foolish whim, eh? These subqueries sometimes can be elements of a structure.

Using left join for the same table or is there a better way?

I have a table called bans where I have the follow fields:
room_id, banned_user_id, banned_by_id, reason, ts_start, ts_end
The users data come from the table called users, now I wanted to query the bans to retrive the name of who was banned and by who along with reason, time the ban was placed and time it ends.
So I have this query:
SELECT u.username, us.username, b.reason, b.ts_start, b.ts_end
FROM `bans` b
LEFT JOIN users us ON b.banned_by_uid = us.uid
LEFT JOIN users u ON b.banned_uid = u.uid
WHERE room_id = 3
My question here is wether my query is ok by using the LEFT JOIN for the 2 data I have to grab from the table users or there is a different approach for this kinda of scenario ?
Your query is perfectly acceptable. Each join to users is on a specific ID, which translates into a simple lookup, with minimal overhead.