How to order by from third table (related to Max(), join) - mysql

Let me explain the question with the following example: (long but easy to understand)
This is how my MySQL database looks like:
table name: general_info
Movie_ID movie_title
1 Iron Man
2 Superman
3 Batman
table name: cast
Movie_ID Cast_Name
1 Robert Downey Jr.
1 Gwyneth Paltrow
2 Henry Cavill
2 Amy Adams
3 Christian Bale
3 Heath Ledger
Table name production_companies
Movie_ID Production_name
1 Marvel
1 Paramount
2 Legendary Pictures
2 DC Entertainment
3 Snycopy
table name user_cast_preference
user_id user_cast_name user_cast_rating
1 Robert Downey Jr. 95
1 Gwyneth Paltrow 45
1 Christian Bale 80
1 Heath Ledger 90
table name user_production_preference
user_id user_production_name user_production_rating
1 Marvel 85
1 Paraamount 70
1 Syncopy 65
Now, I am able to fetch all movies that are in user's preferred cast + preferred production company, using this query
select general_info.movie_id,movie_title from general_info
inner join cast on general_info.movie_id = cast.movie_id
inner join production_companies on general_info.movie_id = production_companies.movie_id
where cast.cast_name in (select user_cast_name from user_cast_preference)
or production_companies.production_companie_name in (select user_production_name from user_production_preference)
group by movie_title
Current Result:
movie_id moive_title
3 Batman
1 Iron Man
Only batman and Ironman got fetched because at least one of the actor or production company was involved in it (which is also in user's preferred list)
Till now everything is just fine. But I want to do this:
I want to order movies by this algorithem.
I will compare all fetched movies with the given rating in my tables and order them by Top to bottom.
In my case, let's compare Batman and Iron Man.
This is how, I decided to compare.
Take the top rated cast from Ironman + Top rated production company from iron man
Take the top rated cast from Batman + Top rated production company from batman
that is:
95 (iron man) + 85 (marvel) = 180
90 (heath ledger) + 65 (syncopy) = 155
Now Iron man has more rating than Batman, so expected result will be:
movie_id moive_title total_rating
1 Iron Man 180
3 BatMan 155
I hope, I made myself clear.

You want all movies where the user either have an actor or a company (or both) in their preferences. Then you want to sort by the sum of top preferences.
I'd look up the maximum points of user-rated casts and maximum points of user-rated companies per movie first and then outer join these aggregation results to the movie table:
select gi.*
from general_info
left join
(
select c.movie_id, max(ucp.user_cast_rating) as points
from cast c
join user_cast_preference ucp on ucp.user_cast_name = c.cast_name
where ucp.user_id = 1
group by c.movie_id
) p1 on p1.movie_id = gi.movie_id
left join
(
select pc.movie_id, max(upp.user_cast_rating) as points
from production_companies pc
join user_production_preference upp on upp.user_cast_name = pc.cast_name
where upp.user_id = 1
group by pc.movie_id
) p2 on p1.movie_id = gi.movie_id
where p1.points > 0 or p2.points > 0
order by coalesce(p1.points, 0) + coalesce(p2.points, 0) desc;

Not sure where you are getting "production_companies.production_companie_name" in your original code
SELECT general_info.Movie_ID,
general_info.movie_title,
MAX(user_cast_preference.user_cast_rating + user_production_preference.user_production_rating) AS total_rating
FROM ((((general_info
INNER JOIN CAST ON general_info.Movie_ID = cast.Movie_ID)
INNER JOIN production_companies ON cast.Movie_ID = production_companies.Movie_ID)
INNER JOIN user_production_preference ON production_companies.Production_name = user_production_preference.user_production_name)
INNER JOIN user_cast_preference ON cast.Cast_Name = user_cast_preference.user_cast_name)
GROUP BY movie_title
ORDER BY total_rating DESC;

Related

How can i count all rows without miss them?

SELECT t1.s_name, count(*) FROM tvSeries AS t1, subTitles AS t2, votes as t3
WHERE
t1.s_id IN (SELECT t2.s_id WHERE sLang='English') AND
t1.s_id IN (SELECT t3.s_id WHERE pts=5) AND
t1.s_id IN (SELECT t3.s_id WHERE uid='britney');
My tvSeries table is like:
s_id s_cat s_name
1 comedy a
2 comedy b
3 drama c
4 comedy d
5 drama e
My subTitles table is like:
s_id sLang
1 English
1 Spanish
2 French
2 English
3 English
1 French
4 German
4 English
5 English
My votes table is like:
s_id uid pts
1 john 4
1 mia 3
1 britney 5
2 rock 5
3 anna 1
3 britney 5
4 megan 3
5 britney 5
I want to select total number of tvSeries and name of tvSeries in this conditions;
which tvSeries gets 5 star from user 'britney' with English subtitles.
When I use my code, I get only one row with number of tvSeries but i want to see many rows with total value. Can anyone help me?
You can do this with simple JOINs (see this answer for an explanation of JOIN vs ,), and then your conditionals are clean and easy to understand.
SELECT
t.s_id,
t.s_name
FROM
tvSeries t
JOIN subTitles s ON s.s_id = t.s_id
JOIN votes v ON v.s_id = t.s_id
WHERE
s.sLang = 'English'
AND v.pts = 5
AND v.uid = 'britney';
If you want just the count of shows instead, you can do:
SELECT
COUNT(*) as count
FROM
...
You can't easily get both the names of the series as well as the count in the same row (because COUNT is an aggregating function), but if you need it you can do:
SELECT
GROUP_CONCAT(t.s_name) as series_names,
COUNT(*) as count
FROM
...
though that returns a single row with concatenated series names (a,c,e) rather than rows which are able to be iterated over.
See http://sqlfiddle.com/#!9/2c252c/13 for a working example.

Selecting everything from table 1 with average AND distinct values from table 2 and table 3

I am not entirely sure even how to name this post, because I do not know exactly how to ask it.
I have three tables. One with users, one with foods and one with the users rating of the foods, like such (simplified example):
Foods
id name type
---------------------
1 Apple fruit
2 Banana fruit
3 Steak meat
Users
id username
-----------------
1 Mark
2 Harrison
3 Carrie
Scores (fid = food id, uid = user id)
fid uid score
---------------------
1 1 3
1 2 5
2 1 2
3 2 3
Now, I have this query, which works perfectly:
SELECT fn.name as Food, ROUND(AVG(s.score),1) AS AvgScore FROM Foods fn LEFT JOIN Scores s ON fn.id = s.fid GROUP BY fn.id ORDER BY fn.name ASC
As you can tell, it lists all names from Foods including an average from all users ratings of the food.
I also want to add the unique users own score. (Assume that when Mark is logged in, his uid is set in a session variable or whatever)
I need the following output, if you are logged in as Mark:
Food AvgScore Your Score
Apple 4 3
I have made several attempts to make this happen, but I cannot find the solution. I have learned that if you have a question, it is very likely that someone else has asked it before you do, but I do not quite know how to phrase the question, so I get no answers when googling. A pointer in the right direction would be much appreciated.
You can try with case:
SELECT fn.name as Food,
ROUND(AVG(s.score),1) AS AvgScore,
sum(case s.uid = $uid when s.score else 0 end) as YourScore
FROM Foods fn
LEFT JOIN Scores s ON fn.id = s.fid
GROUP BY fn.id
ORDER BY fn.name ASC
$uid is variable off course.

how can combine two columns in the same table and count its unique occurrences

I have been struggling and would really appreciate some assistance:
I have two tables cars and rides
cars
car_id car_manuf car_model
1 Honda CRV
2 Honda Accord
3 Toyota Corolla
4 Toyota Camry
5 Ford Fusion
rides
ride_id car_id ride_destination
1 3 Boston
2 5 New York
3 5 Washington DC
4 1 California
5 2 Dallas
6 5 Canada
I would like to count the number of rides by each car type which will have the combination of car_manuf and car_model and should be sorted from most to fewest number of rides.
Output should be:
CarType-NumberofRides
Honda_CRV-1
Honda_Accord-1
Toyota_Corolla-1
Toyota_Camry-0
Ford_Fusion-3
Sorted output with most-few rides
CarType-NumberofRides
Toyota_Camry-0
Honda_Accord-1
Toyota_Corolla-1
Honda_CRV-1
Ford_Fusion-3
mycode:
select
c.car_manuf + '_' + c.car_model AS 'Car Type',
(select count(*) from rides r where r.car_id = c.car_id) AS 'Number of Rides'
from cars c;
I am kinda stuck here and not sure which direction I should go in regards to getting the correct output.
You have to use GROUP BY and an ORDER BY when COUNTing the occurences. I use CONCAT to concatenate the strings instead of a + sign. Makes clearer what is going on, and is not mistaken as an arithmetic operation.
SELECT CONCAT(c.car_manuf, '_', c.car_model) AS CarType, COUNT(r.car_id) AS NumberOfRides
FROM rides r
LEFT JOIN cars c ON (r.car_id = c.car_id)
GROUP BY CarType
ORDER BY NumberOfRides ASC
However this omits the 0 occurences.
If you want to see the 0s as well swap the table order to:
SELECT CONCAT(c.car_manuf, '_', c.car_model) AS CarType, COUNT(r.car_id) AS NumberOfRides
FROM cars c
LEFT JOIN rides r ON (r.car_id = c.car_id)
GROUP BY CarType
ORDER BY NumberOfRides ASC

MySQL query table filtering issue

I've been struggling on the following.
I have 3 tables: players, players_clothes, and teams_clothes.
Table players:
id user team_id
1 tom 4
2 robo 5
3 bob 4
So tom and bob are both on the same team
Table players_clothes:
id clothes_id p_id
1 13 1
2 35 3
3 45 3
Bob has clothing article 35 and 45, robo has none.
Table teams_clothes:
id clothes_id team_id
1 35 4
2 45 4
3 55 4
4 65 5
This shows which teams have rights to which articles of clothing. The problem: tom is wearing an article of clothing that does no belong to his team... Let's assume this is illegal.
I'm having trouble figuring out how to capture all those who are wearing illegal clothes for a particular team.
SELECT pc.clothes_id FROM players AS p
JOIN players_clothes AS pc
ON p.id = pc.p_id
AND p.team_id = 4 GROUP BY pc.clothes_id
(I group by players_clothes.clothes_id because believe it or not, two players can be assigned the same piece of clothing)
I think this results the following set (13, 35, 45)
Now I would like to check against the actual set of clothes that team 4 owns.
SELECT clothes_id FROM teams_clothes WHERE team_id = 4 and this return (35, 45, 55)
How can I create a query so that it returns (13)? I've tried things like NOT EXISTS IN but I think the GROUP BY players_clothes.clothes_id part gets in the way
I suggest
select * from A where team_id = $team_id join B on B.a_id = A.id
where not exists
(
select 1 from C where C.clothes_id = B.clothes_id and team_id = $team_id
)
Basically, we find all As who are on their team and for each A join to all clothing they wear, and then only return the row IF we can't find indication in table C that the clothing is on our team (this covers not existent in C and exists but in the wrong team on C)
This should do the trick:
SELECT b.a_id, b.clothes_id
FROM
b INNER JOIN a
ON b.a_id = a.id
LEFT OUTER JOIN c
ON a.team_id = c.team_id
WHERE
c.clothes_id = NULL
The thought is to do an outer join on the combination of tables A/B against table C. And then only look for the cases where c.clothes_id is NULL, which would represent those cases where there is no relational match on the outer join (i.e. the clothes item is not approved for that user's team).
Not sure if this is too late for you, but I'd change the database model itself to make this situation impossible in the first place:
("Unimportant" fields omitted for brevity, including surrogate keys such as PLAYER_ID.)
Note how TEAM_ID migrates through the identifying relationship from TEAM to PLAYER, and then to the PLAYER_ARTICLE, where it merges with the same field migrated through the TEAM_ARTICLE. Since there is only one physical TEAM_ID field in the PLAYER_ARTICLE table, you can never insert a row that would reference different teams.
To put it in more abstract terms: this is a diamond-shaped dependency, where TEAM is at the top and PLAYER_ARTICLE at the bottom of the diamond. The merger at the bottom (enabled by the usage of identifying relationships) ensures sides must always point to the same top.
Your example data would be represented like this...
PLAYER:
TEAM_ID PLAYER_NO
4 1 -- Tom
5 1 -- Robo
4 2 -- Bob
TEAM_ATRICLE:
TEAM_ID ARTICLE_ID
4 35
4 45
4 55
5 65
PLAYER_ARTICLE:
TEAM_ID PLAYER_NO ATRICLE_ID
4 1 13 -- Tom: this is impossible (FK violation).
4 2 35 -- Bob
4 2 45 -- Bob

MySQL Join Multiple (More than 2) Tables with Conditions

Assume I have 4 tables:
Table 1: Task
ID Task Schedule
1 Cut Grass Mon
2 Sweep Floor Fri
3 Wash Dishes Fri
Table 2: Assigned
ID TaskID (FK) PersonID (FK)
1 1 1
2 1 2
3 2 3
4 3 2
Table 3: Person
ID Name
1 Tom
2 Dick
3 Harry
Table 4: Mobile
ID PersonID (FK) CountryCode MobileNumber
1 1 1 555-555-5555
2 2 44 555-555-1234
3 3 81 555-555-5678
4 3 81 555-555-0000
I'm trying to display the
Task on a certain day
Name of person assigned to task
Phone numbers of said person
I think it should be something like the following, but I'm not sure how to set up the conditions so that the results are limited correctly:
SELECT T.ID, T.Task, P.Name, M.MobileNumber
FROM Task AS T
LEFT JOIN Assigned AS A
ON T.ID = A.TaskID
LEFT JOIN Person AS P
ON A.PersonID = P.ID
LEFT JOIN Mobile AS M
ON M.PersonID = P.ID
WHERE T.Schedule = Fri
My goal is to fetch the following information (it will be displayed differently):
Tasks Name MobileNumber
Sweep Floor, Wash Dishes Dick, Harry 44-555-555-1234, 81-555-555-5678, 81-555-555-0000
Of course, if JOIN is the wrong way to do this, please say so.
It's unclear what you want to do with duplicate data in this case, but you should be looking at using inner joins instead of outer joins, and using something like group_concat() to combine the phone numbers.