Consider the three tables - actors, roles and movies
mysql> DESCRIBE actors;
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | 0 | |
| first_name | varchar(100) | YES | MUL | NULL | |
| last_name | varchar(100) | YES | MUL | NULL | |
| gender | char(1) | YES | | NULL | |
+------------+--------------+------+-----+---------+-------+
4 rows in set (0.17 sec)
mysql> DESCRIBE roles;
+----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| actor_id | int(11) | NO | PRI | NULL | |
| movie_id | int(11) | NO | PRI | NULL | |
| role | varchar(100) | NO | PRI | NULL | |
+----------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
mysql> DESCRIBE movies;
+-----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | 0 | |
| name | varchar(100) | YES | MUL | NULL | |
| year | int(11) | YES | | NULL | |
| rankscore | float | YES | | NULL | |
+-----------+--------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
The following query list the first_name and last_name of all actors acted in movie 'Schindler's List':
mysql> SELECT first_name, last_name FROM actors WHERE id IN
-> (SELECT actor_id FROM roles WHERE movie_id IN
-> (SELECT id FROM movies WHERE name='Schindler\'s List')
-> ) ORDER BY first_name;
Suppose if I want to list the role played by the actors along with their first and last names, what will the best query to fetch the desired result set or output? It may be noted that the role is a column in roles table, which is in the Sub Query.
SELECT DISTINCT a.first_name, a.last_name, r.role
FROM actors a, roles r, movies m
WHERE a.id = r.actor_id and r.movie_id = m.id and m.name='Schindler\'s List'
ORDER BY first_name;
or you can use by joins;
SELECT DISTINCT a.first_name, a.last_name, r.role
FROM roles r
INNER JOIN actors a ON a.id = r.actor_id
INNER JOIN movies m ON r.movie_id = m.id
WHERE m.name='Schindler\'s List'
ORDER BY first_name;
I am working on a project and wanted to have a quick look by you people if the schema or query needs some changes or does it totally needs to be changed.
The project is about creating a ranking system for the badminton teams where the ranking will be based on the scoring rules in a tournament
+2 Points/match would be awarded to the winning team.
An Extra +1 Point would be awarded to the winning team if the match was a Quarter-finals.
An Extra +2 Points would be awarded to the winning team if the match was a Semi-finals.
An Extra +5 Points would be awarded to the winning team if the match was a Final.
Winning all pool matches will add 4 points to your Team score.
Winning more than 3 tournaments will add 15 points to your team.
I started by creating the following tables
Players
+----------+-------------------------------------+------+-----+--------------
| Field | Type | Null | Key | +----------+-------------------------------------+------+-----+--------------
| id | int(11) | NO | PRI |
| name | varchar(250) | NO | |
| image | text | YES | |
| plays | enum('RH','LH') | NO | |
| added_on | datetime | NO | |
| status | enum('active','inactive','retired') | NO | | +----------+-------------------------------------+------+-----+--------------
Teams
+------------+----------------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------------------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(150) | NO | UNI | NULL | |
| image | text | YES | | NULL | |
| status | enum('active','in-active') | NO | | active | |
| added_on | datetime | NO | | CURRENT_TIMESTAMP | |
| updated_on | datetime | YES | | NULL | |
+------------+----------------------------+------+-----+-------------------+----------------+
Player To Teams
+-----------+---------------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------------------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| player_id | int(11) | NO | MUL | NULL | |
| team_id | int(11) | NO | | NULL | |
| status | enum('active','inactive') | NO | | NULL | |
| added_on | datetime | NO | | CURRENT_TIMESTAMP | |
+-----------+---------------------------+------+-----+-------------------+----------------+
Tournaments
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| year | int(4) | NO | | NULL | |
+-------+--------------+------+-----+---------+----------------+
Matches
+---------------+---------------------------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------------------------------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| team_one | int(11) | NO | MUL | NULL | |
| team_two | int(11) | NO | | NULL | |
| winner_id | int(11) | NO | | NULL | |
| tournament_id | int(11) | NO | MUL | 1 | |
| added_on | datetime | NO | | CURRENT_TIMESTAMP | |
| match_type | enum('pool','quarter','semi','final') | NO | | pool | |
| sets | smallint(2) | NO | | 1 | |
+---------------+---------------------------------------+------+-----+-------------------+----------------+
Match Score
+----------+-------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| match_id | int(11) | NO | MUL | NULL | |
| team_id | int(11) | NO | MUL | NULL | |
| set_num | enum('1','2','3') | NO | | NULL | |
| score | smallint(2) | NO | | NULL | |
| added_on | datetime | NO | | CURRENT_TIMESTAMP | |
+----------+-------------------+------+-----+-------------------+----------------+
Pools
+---------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(10) | NO | UNI | NULL | |
| tournament_id | int(11) | NO | MUL | NULL | |
+---------------+-------------+------+-----+---------+----------------+
One thing that you will notice that i have not saved rank scoring anywhere and i am calculating it on runtime using the following query
SELECT
T.id, T.name, T.status,
IFNULL(T.image,'no-image.png') as DP,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.team_one = T.id OR M.team_two = T.id)) as played,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE M.winner_id = T.id) as won,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE ((M.team_one = T.id OR M.team_two = T.id)
AND (M.winner_id != T.id))) as lost,
(
((SELECT COUNT(*)
FROM badminton_matches M
WHERE M.winner_id = T.id) * 2) +
(((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type = 'quarter'
AND M.winner_id = T.id)) * 2) + 1) +
(((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type = 'semi'
AND M.winner_id = T.id)) * 2) + 2) +
(((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type = 'final' AND M.winner_id = T.id)) * 2) + 5)) as Points
FROM
badminton_teams T
ORDER BY
(Points) DESC;
First
is it right to calculate the scoring on runtime using query or
should I save update it every time I save a match result in the database
or
should I schedule a cron job for this purpose
Edit:
Updated the query to the following
SELECT
T.id, T.name, T.status,
IFNULL(T.image,'no-image.png') as DP,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE ((M.team_one =T.id or M.team_two = T.id) and M.winner_id IS NOT NULL)) as played,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE M.winner_id=T.id) as won, (SELECT COUNT(*)
FROM badminton_matches M
WHERE ((M.team_one =T.id or M.team_two = T.id) AND (M.winner_id!=T.id))) as lost,
((SELECT (SUM(BMS.points_won)-SUM(BMS.points_lost))
FROM
badminton_match_score BMS
JOIN badminton_matches M ON (M.id=BMS.match_id)
where M.team_one=T.id OR M.team_two=T.id and M.winner_id is not null)/(SELECT COUNT(*)
FROM badminton_matches M
WHERE ((M.team_one =T.id or M.team_two = T.id) and M.winner_id IS NOT NULL))) AS AVG_SCORE,
(
((SELECT COUNT(*)
FROM badminton_matches M
WHERE M.winner_id=T.id)*2) +
(SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type='quarter' AND M.winner_id=T.id))
+
((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type='semi' AND M.winner_id=T.id))*2)
+
((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type='final' AND M.winner_id=T.id))*5)
)
as Points
FROM badminton_teams T
order by (Points) DESC, lost ASC, AVG_SCORE DESC
is it right to calculate the scoring on runtime using query
If you want it to be a real time application. In my opinion, yes if the points are not accumulated and will reset after a game. For Sql performance issues.
should i save update it every time i save a match result in the database
Updates are for modification only of data, saving the data after a match for history will be sufficient.
should i schedule a cron job for this purpose
If in terms of larger databases and productivity, yes.
I have the following tables:
MOVIES
+-------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | YES | MUL | NULL | |
| title | varchar(40) | NO | | NULL | |
| description | text | NO | | NULL | |
| created_at | datetime | YES | | NULL | |
| updated_at | datetime | YES | | NULL | |
+-------------+-------------+------+-----+---------+----------------+
MOVIE_VOTES
+------------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | YES | MUL | NULL | |
| movie_id | int(11) | YES | MUL | NULL | |
| like | tinyint(1) | YES | | NULL | |
| created_at | datetime | YES | | NULL | |
| updated_at | datetime | YES | | NULL | |
+------------+------------+------+-----+---------+----------------+
And I want to query all the movies sorted by the number of likes they have. I have this query:
SELECT *, COUNT(*) AS count_likes
FROM movie_votes
RIGHT JOIN movies
ON movies.id=movie_votes.movie_id
WHERE movie_votes.like = 1
GROUP BY movie_id
ORDER BY count_likes;
And this returning the movies sorted by the number of likes BUT not the movies that haven't be voted(liked) yet. For some reason the RIGHT join works as simple join.
I have searched for some relative answers but cant find anything helpful.
You should specify a column name when doing outer joins so that it will count only the non-null values
SELECT movies.id,
movies.user_id,
movies.title,
movies.description,
movies.created_at,
movies.updated_at,
COUNT(movie_votes.movie_id) AS count_likes
FROM movies
LEFT JOIN movie_votes
ON movies.id = movie_votes.movie_id
AND movie_votes.like = 1
GROUP BY movies.id,
movies.user_id,
movies.title,
movies.description,
movies.created_at,
movies.updated_at
ORDER BY count_likes
The condition movie_votes.like = 1 should be put in the ON clause so it will filter the rows in table movie_votes first before joining it to table movies.
Does anyone know if it's possible to convert this subquery into a JOIN?
SELECT DISTINCT
lastname,
c.fullname,
(SELECT COUNT(lg.action) FROM tbl_log AS lg WHERE lg.userid = u.id AND lg.course = c.id) AS 'Total Course Hits Per Student'
FROM tbl_user AS u
JOIN tbl_user_enrolments AS ents ON ents.userid = u.id
JOIN tbl_enrol AS en ON ents.enrolid = en.id
JOIN tbl_course AS C ON c.id = en.courseid
JOIN tbl_context AS ctx ON c.id = ctx.instanceid
JOIN tbl_role_assignments AS ra ON ra.contextid = ctx.id AND ra.userid = u.id
LOG TABLE
+-------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------------+------+-----+---------+----------------+
| id | | NO | PRI | NULL | auto_increment |
| time | | NO | | NULL | |
| userid | | NO | | NULL | |
| course | | NO | | NULL | |
| action | | NO | | NULL | |
+-------------+---------------------+------+-----+---------+----------------+
USER Table
+--------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+---------------------+------+-----+---------+----------------+
| id | | NO | PRI | NULL | auto_increment |
| username | | NO | | NULL | |
| userpassword | | NO | | NULL | |
| lastname | | NO | | NULL | |
| firstname | | NO | | NULL | |
+--------------+---------------------+------+-----+---------+----------------+
COURSE table
+--------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+---------------------+------+-----+---------+----------------+
| id | | NO | PRI | NULL | auto_increment |
| category | | NO | | NULL | |
| fullname | | NO | | NULL | |
| shortname | | NO | | NULL | |
+--------------+---------------------+------+-----+---------+----------------+
I link the users together via the enrolment and context tables.
Cant you take a query on the log_table
SELECT
COUNT(tbl_log.action)
lastname,
c.fullname,
FROM tbl_log
JOIN tbl_user ON tbl_log.userid = tbl_user.id
JOIN tbl_course ON tbl_log.course = tbl_course.id
GROUP BY tbl_log.userid, tbl_log.course
I don't know if you need the other tables? This would provide a count, the student name and the course name if I am not mistaken. However, you only get the actual logs, so no occurences for students that haven't done anything.
Otherwise an OUTER JOIN may suffice on the log-table. I have no time to check now in SQL fiddle. Hope this already helps you a bit on your way.
I have 4 tables:
mysql> describe solution_sections;
+---------------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+---------------+------+-----+---------+----------------+
| solution_section_id | int(10) | NO | PRI | NULL | auto_increment |
| display_order | int(10) | NO | | NULL | |
| section_name | varchar(1000) | YES | | NULL | |
+---------------------+---------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql> describe suggested_solution_comments;
+-----------------------+----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+----------------+------+-----+---------+----------------+
| comment_id | int(10) | NO | PRI | NULL | auto_increment |
| problem_id | int(10) | NO | | NULL | |
| suggested_solution_id | int(10) | NO | | NULL | |
| commenter_id | int(10) | NO | | NULL | |
| comment | varchar(10000) | YES | | NULL | |
| solution_part | int(3) | NO | | NULL | |
| date | date | NO | | NULL | |
+-----------------------+----------------+------+-----+---------+----------------+
mysql> describe users;
+--------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+---------------+------+-----+---------+----------------+
| user_id | int(10) | NO | PRI | NULL | auto_increment |
| first_name | varchar(100) | NO | | NULL | |
| last_name | varchar(100) | NO | | NULL | |
| email | varchar(150) | NO | | NULL | |
| user_pass | varchar(40) | NO | | NULL | |
| zip | varchar(100) | NO | | NULL | |
| country | varchar(100) | NO | | NULL | |
| city | varchar(100) | NO | | NULL | |
| state | varchar(100) | NO | | NULL | |
| lat | float(9,6) | YES | | NULL | |
| lng | float(9,6) | YES | | NULL | |
| agreed_terms | tinyint(1) | YES | | NULL | |
| join_date | date | NO | | NULL | |
| last_login | date | NO | | NULL | |
| bio_blurb | varchar(5000) | YES | | NULL | |
+--------------+---------------+------+-----+---------+----------------+
15 rows in set (0.03 sec)
mysql> describe member_photo;
+-------------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+---------------+------+-----+---------+----------------+
| photo_id | int(10) | NO | PRI | NULL | auto_increment |
| member_id | int(10) | NO | | NULL | |
| photo_description | varchar(3000) | YES | | NULL | |
| photo_path | varchar(1000) | NO | | NULL | |
| small_thumb | varchar(1000) | YES | | NULL | |
| mid_thumb | varchar(1000) | YES | | NULL | |
| is_main_photo | tinyint(1) | YES | | NULL | |
+-------------------+---------------+------+-----+---------+----------------+
And I have a query like this:
select comment_id,
commenter_id,
section_name,
comment,
solution_part,
display_order,
solution_section_id,
suggested_solution_id,
DAYOFMONTH(date),
DAYNAME(date),
YEAR(date),
MONTH(date),
first_name,
last_name,
email,
small_thumb,
mid_thumb
from solution_sections
left join suggested_solution_comments on
solution_sections.solution_section_id = suggested_solution_comments.solution_part
left join users on
suggested_solution_comments.commenter_id = users.user_id
left join member_photo on
suggested_solution_comments.commenter_id = member_photo.member_id
where suggested_solution_id = 61 OR
suggested_solution_id IS NULL
order by solution_section_id,
comment_id,
section_name,
comment,
solution_part,
display_order;
What its supposed to do is get each section_name from the solution_sections table, and then find the comments (and data about who commented). Sometimes there are no comments, but it should still return at least the row with section_name and all other things being null.
But for some reason it does not. And the weirdest part is that if I give it a different suggested_solution_id to match, it will return all of the rows of solution_sections.
Any ideas why such a thing might happen? Thank you!!
And I just realized one thing - if another comment has been made for any problem_id, this query won't return the row with that section.
You need a left outer join to view all records from your parent table when child records are not guaranteed to exits. I'd also avoid adding a where clause when using outer joins.. I think its more readable to keep your join in a subselect, and filter the results.. Try something like this:
select * from
(
select sc.comment_id,
sc.commenter_id,
ss.section_name,
sc.comment,
sc.solution_part,
ss.display_order,
ss.solution_section_id,
sc.suggested_solution_id,
DAYOFMONTH(sc.date),
DAYNAME(sc.date),
YEAR(sc.date),
MONTH(sc.date),
u.first_name,
u.last_name,
u.email,
mp.small_thumb,
mp.mid_thumb
from solution_sections ss
left outer join suggested_solution_comments sc on ss.solution_section_id = sc.solution_part
left outer join users u on sc.commenter_id = u.user_id
left outer join member_photo mp on sc.commenter_id = mp.member_id) a
where a.suggested_solution_id = 61 OR
a.suggested_solution_id IS NULL
order by a.solution_section_id,
a.comment_id,
a.section_name,
a.comment,
a.solution_part,
a.display_order;
EDIT:
select sc.comment_id,
sc.commenter_id,
ss.section_name,
sc.comment,
sc.solution_part,
ss.display_order,
ss.solution_section_id,
sc.suggested_solution_id,
DAYOFMONTH(sc.date),
DAYNAME(sc.date),
YEAR(sc.date),
MONTH(sc.date),
u.first_name,
u.last_name,
u.email,
mp.small_thumb,
mp.mid_thumb
from solution_sections ss
left outer join suggested_solution_comments sc on ss.solution_section_id = sc.solution_part
AND sc.suggested_solution_id = 61
left outer join users u on sc.commenter_id = u.user_id
left outer join member_photo mp on sc.commenter_id = mp.member_id
order by solution_section_id,
comment_id,
section_name,
comment,
solution_part,
display_order;
If you want to show solution_sections even if all the rest doesn't exist, you can use "left outer join":
select comment_id,commenter_id, section_name, comment, solution_part,
display_order, solution_section_id, suggested_solution_id,
DAYOFMONTH(date), DAYNAME(date), YEAR(date), MONTH(date),
first_name, last_name, email, small_thumb,mid_thumb
from solution_sections
left outer join suggested_solution_comments on solution_sections.solution_section_id = suggested_solution_comments.solution_part
and suggested_solution_id = 61
left outer join users on suggested_solution_comments.commenter_id = users.user_id
left outer join member_photo on suggested_solution_comments.commenter_id = member_photo.member_id
where solution_section_id = ????
order by solution_section_id, comment_id, section_name, comment, solution_part,display_order;
ps. try to use aliases for tables it's more readable :-)