I am making a movie rating application where a user can rate a movie as either 'like' or 'dislike'. So I have made 3 tables user, movie and rating. The vote table's example is :
userID movieID Vote
x a li
y a dli
y b li
w a li
The table's schema is :
userID - PrimaryKey
movieID - PrimaryKey
Vote - Enum('li','dli')
I have made userID and movieID as primary key so that if the user updates his/her preference that specific row gets updated if the record is there.
Edit : Here's the movie table's schema.
mID - PrimaryKey
mName - Varchar
mGenre - Varchar
mDesc - Text
mDateOfRelease - Date
My question is that is it possible to select all the columns from the movie table, and like and dislike count of that movie in one db call. If yes how can I do it?
Let' imagine that you have movie and it contains field id. Here is what you can do:
select m.*,sum(case when r.vote like 'li' then 1 else 0 end) likes,
sum(case when r.vote like 'dli' then 1 else 0 end) dislikes from movie m
left join rating r
on r.movieID = m.mID
group by m.mID;
Left join is basically for no-votes. If rating table will be empty for this movie - it will still show it.
Edit:
To explain this I will cut this query.
We need to understand what group by is doing. You can find docs here. In short we create groups of the data based on the different entries of the column from your group by statement.
select count(r.movieID) from rating r
group by r.movieID;
This will give us how many votes each movie got (here we list only movies that got any vote - line in rating table). So after this we can do "smart" count, and use conditional SUM function. Into the definition you can have an expression. That means case when ... then ... end works as well. So we just sum up all the 'li' and do not touch 'dli' for likes and opposite for dislikes.
The only one big drawback of this is we don't have all the movies (think about the case when there is no votes for a movie). Then this select will just skip this. And here we have LEFT JOIN statement. The idea is very simple - we include all the lines of table movie no matter what is going on in rating table. on we use to connect these to tables. Then we recall what we did before with summing up but change group by to mID (it always exists and non-null, so we always have something to group on). In this case you will have exactly what you want. Also play around with this query (resulting one). Try to understand what will happen if you leave your rating column in group by statement. (It's easier to see than read tons of text :) )
If something is not clear - please let me know, will try to improve it.
Hope it helps
try this:
select m.*,
sum(case when v.vote = li then 1 else 0 end) li_count,
sum(case when v.vote = dli then 1 else 0 end) dli_count
from movie m left join vote v on m.movieID = v.movieID
group by m.mId (which is enough in mySQL)
Try this:
SELECT
votes.movieID,
likes.like_count,
dislikes.dislike_count
FROM votes
LEFT join (
SELECT movieID, count(Vote) as like_count
FROM votes
WHERE Vote = 'li'
GROUP BY movieID
) likes ON likes.movieID = votes.movieID
LEFT join (
SELECT movieID, count(Vote) as dislike_count
FROM votes
WHERE Vote = 'dli'
GROUP BY movieID
) likes ON likes.movieID = votes.movieID
Related
EDIT: Better explanation
I have a page with a job. The job as an idea and three skills (skill_ids) and skill requirements (a user must have at least this skill value to be qualified).
I click on the job to find candidates, so I have the job_id and the three skill_ids and skill_id_requirements. So I can do this so far as the first answer proposed with joins. I find all users who have the three skills. The skills are saved in skill_ratings. So far it works as I use to find the skill_id's only.
But now I want the value and here I have my code where I compute the final value (called rating). The rating respects all given values, but isn't a simple average or the sum of all. That's why I need the long horrible code. In the long horrible code I usually insert a user's ID. But here I need all user_id's who have the skills mentioned above just to calculate if they are qualified. This is dynamic.
I'm having a table where I want to find people who are qualified for a position under some requirements. Here I work with one table called skill_ratings, but (as far as I see) need to add some subqueries. And here I have the problem. There are many subqueries and I've tried to address a parent query field. But it only seems to work in a first-grade subquery to a parent query.
Here's my structure:
SELECT * FROM table t
WHERE EXISTS (SELECT * FROM table d WHERE x > 1
AND b=t.id
AND y <= (SELECT a FROM (MAIN SUBQUERY WITH CALCULATIONS)))
GROUP BY xyx
But the error I get is: #1054 - Unknown column 'skra.usr_id_get' in 'where clause'. skra is the parent table in this case.
I want to get the following (pseudo-sql):
SELECT all FROM table t AS x
WHERE EXISTS (
SELECT all FROM table t AS y
WHERE y.skill_id = 1
AND y.usr_id_get = t.usr_id_get
AND y.value <= (my algorithm)
)
The main subquery is important so far as I want to get a computed number. Elsewhere the code works because I were able to work with predefined PHP-variables for a user's ID. But I can't do this here as I need to find the users within the boundaries of the where-clauses.
How can I solve this? Because addressing a parent-field in a subquery seems to be limited to a first-grade subquery.
EDIT: Code
Code removed due to project status.
Error: #1054 - Unknown column 'c.usr_id_get' in 'where clause'
We want users that have certain skills of certain levels. For example all users that have skill 1 with at least level 20 and skill 2 with at least level 70.
Here is an algorithm:
First of all we must get the skill levels. A user has several skill ratings and the average rating per skill is the level.
Then we want a table of criteria (skill 1 / level 20, skill 2 / level 70 in our example).
We collect all user skill levels that match the criteria (EXISTS clause) and then
keep the users that match all skill levels (count(*) = <desired number of skills>).
The query:
select
sr.usr_id_get
from
(
select usr_id_get, skill_id, avg(value) as level
from skill_ratings
group by usr_id_get, skill_id
) sr
where exists
(
select *
from
(
select 1 as skill_id, 20 as level
union all
select 2 as skill_id, 70 as level
) criteria
where sr.skill_id = criteria.skill_id
and sr.level >= criteria.level
)
group by usr_id_get
having count(*) = 2;
You can also make criteria a real (temporary) table. Then your query stays the same, no matter how many skills are requested. You'd have
where exists
(
select *
from criteria
where sr.skill_id = criteria.skill_id
and sr.level >= criteria.level
)
group by usr_id_get
having count(*) = (select count(*) from criteria);
then.
This looks like it could be done with a simple JOIN:
SELECT T.*
FROM your_table T
JOIN other_table Y ON (
T.usr_id_get = Y.usr_id_get
AND T.skill_id = 1
AND Y.value <= [...]
)
If you need to perform some sort of calculations before the join, then you could join with a subquery:
SELECT T.*
FROM your_table T
JOIN (
SELECT *
FROM other_table Y
WHERE Y.skill_id = 1
AND Y.value = [...]
) Y USING(usr_id_get)
If I understand correctly, you have a user, say user 123, and a skill, say skill 99. Now you want to get the avarage rating for user 123 and skill 99 and then find all users with an equal or better average rating on that skill.
This is how to get the avarage ratings for skill 99 per user:
select usr_id_get, avg(value)
from skill_ratings
where skill_id = 99
group by usr_id_get;
This is how to get all users with an equal or better avarage rating for skill 99 than user 123:
select usr_id_get
from skill_ratings
where skill_id = 99
group by usr_id_get
having avg(value) >=
(select avg(value) from skill_ratings where skill_id = 99 and usr_id_get = 123);
Add to this whatever other criteria you need.
I have 3 tables:
Student (Id, Name, Country)
Course (CrsCode, CrsName, Type, Instructor)
Results(Id, CrsCode, Grade)
I have to solve below q's by using SQL query. Id and CrsCode are key fields in every table. The Type field specifies the course type, e.g. MATH, STAT, SYSC, TTMG, ELEC, etc.
Find the Id of students who take TTMG or SYSC course.
Find the Id of students who take every course.
Find the id of students who take every TTMG course or every SYSC course.
Below are the sample data for part 3. 1st image is the Course Table and 2nd image is the Results table
I am able to solve the 1st question by using the following SQL query: SELECT R.ID FROM RESULTS R JOIN COURSE C ON C.CRSCODE = R.CRSCODE WHERE C.TYPE="TTMG" OR C.TYPE ='SYSC
For the 2nd question, I believe we have to again relate 2nd (Course) and 3rd (Results) table in order to get the result. We have to relate a specific case here. We have to consider a case that there is one specific student who is taking all courses(CrsCode) and we have to find the id of that student.
I believe the query will still be the same as in question 1 but this time there will be little bit changes:
SELECT R.ID FROM RESULTS R JOIN COURSE C
I am not including anything after COURSE C because I am not sure the answer after that. Any pointers will be highly appreciated.
Find the Id of students who take every course.
Assuming course table contains all the courses a student can take, you can group by the id column in the results table and check if the count is equal to the row count in course table.
SELECT ID
FROM RESULTS
GROUP BY ID
HAVING COUNT(DISTINCT CRSCODE) = (SELECT COUNT(*) FROM COURSE)
Edit: Based on OP's comment
Find the id of students who take every TTMG or SYSC course
SELECT r.id
FROM course c
JOIN RESULTS r on c.CRSCODE=r.CRSCODE
GROUP BY r.ID
HAVING COUNT(case when type = 'SYSC' then r.CRSCODE end) = (SELECT COUNT(*) FROM COURSE WHERE type = 'SYSC')
OR COUNT(case when type = 'TTMG' then r.CRSCODE end) = (SELECT COUNT(*) FROM COURSE WHERE type = 'TTMG')
Sample Demo
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
i have site like imdb and we provide movie information sin site..and our website have option to rate all movies for every users.
I have two tables
1 . imdb (its for store movie details)
id,name,actors,vote
2. ratings (its for store users rating details) id,rating_id(its same as id from first table),rating_num,IP
now what am doing is..when anyone rating a movie take the avg of that movie rating by using rating tables (total ratings/number of ratings) and insert that value into "vote" column in first table..my demands this..thats why done like this..
Now my problem is..i want to fetch top rated movies..i mean in vote column which movie have top rating which want to list and one more condition is that that movie should rated by 10 users(use ratings table for that)
thanks in advance
I don't quite understand how your tables are organized. Is there A) a new row for each rating given by a customer in the ratings table or B) is there only 1 row per movie which is updated?
I am gues it is A and rating_num is the rating given by the costumer.
In this case, a simple MySql solution could make use of aggregate functions such as COUNT and AVG. Untested example.
EDIT - To get the details from the imdb table you will just need to join them.
SELECT id as 'ID', COUNT(1) as 'Number of ratings', AVG(r.rating_num) as 'Average rating', i.name, i.actors, i.vote
FROM ratings r
INNER JOIN imbd i ON ( r.id = i.id )
GROUP BY r.id
HAVING `Number of ratings` >= 10
ORDER BY `Average rating` desc
LIMIT 10
I am trying to create a custom sort that involves the count of some records in another table. For example, if one record has no records associated with it in the other table, it should appear higher in the sort than if it had one or more records. Here's what I have so far:
SELECT People.*, Organizations.Name AS Organization_Name,
(CASE
WHEN Sent IS NULL AND COUNT(SELECT * FROM Graphics WHERE People.Organization_ID = Graphics.Organization_ID) = 0 THEN 0
ELSE 1
END) AS Status
FROM People
LEFT JOIN Organizations ON Organizations.ID = People.Organization_ID
ORDER BY Status ASC
The subquery within the COUNT is not working. What is the correct way to do something like this?
Update: I moved the case statement into the order by clause and added a join:
SELECT People.*, Organizations.Name AS Organization_Name
FROM People
LEFT JOIN Organizations ON Organizations.ID = People.Organization_ID
LEFT JOIN Graphics ON Graphics.Organization_ID = People.Organization_ID
GROUP BY People.ID
ORDER BY
CASE
WHEN Sent IS NULL AND Graphics.ID IS NULL THEN 0
ELSE 1
END ASC
So if if the People record does not have any graphics, Graphics.ID will be null. This achieves the immediate need.
If what you tried does not work, it can be done by joining against a subquery, and placing the CASE expression into ORDER BY as well:
SELECT
People.*,
orgcount.num
FROM People JOIN (
SELECT Organization_ID, COUNT(*) AS num FROM Graphics GROUP BY Organization_ID
) orgcount ON People.Organization_ID = orgcount.num
ORDER BY
CASE WHEN Sent IS NULL AND orgcount.num = 0 THEN 0 ELSE 1 END,
orgcount.num DESC
You could use an outer join to the Graphics table to get the data needed for your sort.
Since I don't know your schema, I made an assumption that the People table has a primary key column called ID. If the PK column has a different name, you should substitute that in the GROUP BY clause.
Something like this should work for you:
SELECT People.*, (count(Distinct Graphics.Organization_ID) > 0) as Status
FROM People
LEFT OUTER JOIN Graphics ON People.Organization_ID = Graphics.Organization_ID
GROUP BY People.ID
ORDER BY Status ASC
Fairly straight forward with a LEFT JOIN provided you have some kind of primary key in the People table to GROUP on;
SELECT p.*, sent IS NOT NULL or COUNT(g.Organization_ID) Status
FROM People p LEFT JOIN Graphics g ON g.Organization_ID = p.Organization_ID
GROUP BY p.primary_key
ORDER BY Status
Demo here.