MySQL query with specific constraints? - mysql

I'm trying to create MySQL queries to answer some questions after creating the following database:
create table teachers (
teacher_id int primary key auto_increment,
name varchar(50),
surname varchar(50),
dob date,
employment_day date
);
create table students (
student_id int primary key auto_increment,
name varchar(50),
surname varchar(50),
dob date,
no_of_lessons_left int
);
create table aircrafts (
aircraft_id int primary key auto_increment,
manufacturer varchar(50),
serial_number varchar(50), -- or int?
cycles int
);
create table schools (
school_id int primary key auto_increment,
address varchar(50),
opening_date date
);
create table lessons (
lesson_id int primary key auto_increment,
teacher_id int not null,
student_id int not null,
aircraft_id int not null,
school_id int not null,
lesson_date date,
constraint lessons_teacher foreign key(teacher_id) references teachers(teacher_id),
constraint lessons_student foreign key(student_id) references students(student_id),
constraint lessons_aircraft foreign key(aircraft_id) references aircrafts(aircraft_id),
constraint lessons_school foreign key(school_id) references schools(school_id)
);
Below some queries, I tried for the questions using double join:
Questions are :
• How many aircraft are there per each school at some point in time?
I can't figure out how to answer these conditions " per each school at some point of time"
• How many students are attending one particular school?
select count(*) from students s
inner join lessons l on l.student_id = s.student_id
inner join schools sc sc.teacher_id= l.teacher_id
where sc.name = "PilotingSchoolName";
• How many students does each teacher have at some particular moment
select count(*) from students s
inner join lessons l on l.student_id = s.student_id
inner join schools sc sc sc.teacher_id= l.teacher_id;
However, I'm missing the condition " at some particular moment " in the query I wrote.

You have a lessons.lesson_date column, and can limit to one point in time with something like AND lesson_date = '2019-12-08' or a date range with AND lesson_date BETWEEN '2018-01-01' AND '2019-12-08'. If the requirements did not ask you to find a specific exact date, it sounds like they'd just like you to be able to demonstrate how to use lesson_date in WHERE.
In your response to "how many students does each teacher have" problem, you are missing a crucial GROUP BY clause. The query you have now is close, but will instead show the total number of enrolled students. Grouping by teachers.name will complete the query (along with a lesson_date constraint)
select
t.name,
-- count each student only once per teacher
count(DISTINCT s.student_id) AS
from teachers t
-- left join so teachers with no enrolled
-- students will list as zero
left join lessons l on t.teacher_id = l.teacher_id
left join students s ON l.student_id = s.student_id
WHERE
-- A specific date
l.lesson_date = '2019-12-08'
GROUP BY t.name
To get the aircrafts per school at a particular time, the query is very similar, but with different joins.
select
-- List all schools, and count of aircraft per school
s.school_id,
count(DISTINCT l.aircraft_id)
from
schools s
-- left join, so that schools with zero current aircraft
-- are include in the results
left join lessons l ON l.school_id = s.school_id
where
l.lesson_date = '2019-12-08'
-- GROUP BY necessary to get the count per school
GROUP BY s.school_id

Related

Mysql Selecting the average value of a column from things that are stored in 3 tables

I am fairly new with databases and I am starting with mysql.
I have 4 tables (movie, genre, movieGenre and movieRating):
movie:
CREATE TABLE `movie` (
`movieId` INT NOT NULL,
`title` VARCHAR(155) NOT NULL,
PRIMARY KEY (`movieId`)
);
genre
CREATE TABLE `genre` (
`code` INT NOT NULL AUTO_INCREMENT,
`genre` VARCHAR(20) NOT NULL,
PRIMARY KEY (`code`)
);
movieGenre
CREATE TABLE `movieGenre` (
`movieId` INT,
`genreId` INT,
CONSTRAINT `fk_movieGenre_movie` FOREIGN KEY (`movieId`) REFERENCES `movie`(`movieId`),
CONSTRAINT `fk_movieGenre_genre` FOREIGN KEY (`genreId`) references `genre`(`code`)
);
and movieRating
CREATE TABLE `movieRating` (
`userId` INT NOT NULL AUTO_INCREMENT,
`movieId` INT NOT NULL,
`rating` FLOAT NOT NULL,
`date` DATE,
CONSTRAINT `fk_movieRating_user` FOREIGN KEY (`userId`) REFERENCES `user`(`userId`),
CONSTRAINT `fk_movieRating_movie` FOREIGN KEY (`movieId`) REFERENCES `movie`(`movieId`)
);
I need to find the average rate for each movie genre, sorted in descended average rating value and if a genre does not have any associated rating, it should be reported with 0 ratings value
I am lost. I don't know how to achieve this result. Could you please help me?
I have figured out how to find the avg rate for each movie but I don't know how to change this so I find for each genre:
SELECT `movie`.`movieId`, AVG(`movieRating`.`rating`) FROM `movie`
INNER JOIN `movieRating` ON `movie`.`movieId` = `movieRating`.`movieId`
GROUP BY `movieRating`.`movieId`
ORDER BY AVG(`movieRating`.`rating`) DESC;
Well, I have put an ID in your genre table, otherwise I can't make this work. So, it has become:
CREATE TABLE `genre` (
`genreId` INT,
`code` INT NOT NULL AUTO_INCREMENT,
`genre` VARCHAR(20) NOT NULL,
PRIMARY KEY (`code`)
);
And I have to make the assumption that all the genres are defined in this table. I'll take this table as a base, and, as you suggested use a subquery:
SELECT
genre.code,
genre.genre,
(<my sub select comes here>)
FROM
genre;
This basically gets you a list of all genres. Now it is up to the subquery to give the average rate for the movies in each genre. That subquery could look something like this:
SELECT AVG(movieRating.rating)
FROM movieRating
JOIN movie ON movie.movieId = movieRating.movieId
JOIN movieGenre ON movieGenre.movieId = M.movieId
WHERE movieGenre.genreId = genre.genreId;
I kept it very simple. We start with the average we want, from movieRating, and work through the movie and movieGenre tables to get to the genreId in that last table. Notice the genre.genreId which comes from the main query. We are implicitly grouping by genreId.
Now you can put this subselect in the main query, but that still doesn't solve the situation in which there is not rating to take an average from. It would result in NULL, meaning: no result. That is almost good enough, but you could put a IFNULL() around it to get a proper zero result.
The total query would then become this:
SELECT
genre.code,
genre.genre,
IFNULL((SELECT AVG(movieRating.rating)
FROM movieRating
JOIN movie ON movie.movieId = movieRating.movieId
JOIN movieGenre ON movieGenre.movieId = M.movieId
WHERE movieGenre.genreId = genre.genreId), 0) AS Average
FROM
genre;
I can't guarantee this will work since I cannot test it, and testing is everything when writing queries.
You should left join all tables and then group by gerne
SELECT `genre`,AVG(IFNULL(`rating`,0)) avgrate
FROM `movie` m
LEFT JOIN `movieRating` mr ON m.`movieId` = mr.`movieId`
LEFT JOIN movieGenre mg ON mg.`movieId` = m.`movieId`
LEFT JOIN `genre` g ON g.`code` = mg.`genreId`
GROUP BY `genre`
I general produce data for your tables, and then start by joing the tables, and see f you get the result you want, if not change the joins to LET Join one by one till you get the result you want, of course you need ro calculate teh avg from 3 or 4 movies

mysql subquery COUNT with WHERE clause confusion

I been mashing buttons all day but cant get this query to work. I have 3 tables students, courses and enrollment table that shows which classes the students have enrolled in
The query needs to retrieve all courses having at least 2 students enrolled which is ordered by course with the greatest number of students
I worked out how to retrieve the count of enrollments per class but having trouble filtering enrollments to >= 2 students
-- coursetable -----------------------------
CREATE TABLE StudentTable(
studentID VARCHAR(255) NOT NULL,
firstName VARCHAR(255) NOT NULL,
LastName VARCHAR(255) NOT NULL,
DOB DATE NULL,
CONSTRAINT pk_studentTable PRIMARY KEY(studentID)
);
-- coursetable -----------------------
CREATE TABLE CourseTable(
courseID VARCHAR(255) NOT NULL,
courseName VARCHAR(255) NOT NULL,
hoursPerWeek int(11) NULL,
startDate DATE NULL,
CONSTRAINT pk_courseTable PRIMARY KEY(courseID)
);
-- enrolment table --
CREATE TABLE EnrolmentTable(
studentID VARCHAR(255) NOT NULL,
CourseID VARCHAR(255) NOT NULL,
CONSTRAINT pk_enrolmentTable PRIMARY KEY(studentID, CourseID)
);
this is the query i can do showing enrollments of all classes but it shows one class having only 1 student enrolled. I need it to only display classes with => 2 enrollments
SELECT ct.CourseName AS Course_Name, COUNT(st.studentID) AS Students_Enrolled
FROM EnrolmentTable et
INNER JOIN courseTable ct ON ct.courseID = et.courseID
INNER JOIN studentTable st ON st.studentID = et.studentID
GROUP BY et.courseID;
I need to use a subquery right? but not sure how
You can use HAVING to filter the result
SELECT ct.CourseName AS Course_Name, COUNT(st.studentID) AS Students_Enrolled
FROM EnrolmentTable et
INNER JOIN courseTable ct ON ct.courseID = et.courseID
INNER JOIN studentTable st ON st.studentID = et.studentID
GROUP BY et.courseID
HAVING Students_Enrolled> 1
ORDER BY Students_Enrolled DESC

Create view from 3 different tables?

I'm trying to create a view in MySQL to include from 3 different tables.
CREATE TABLE ACTOR (
ACTOR_ID DECIMAL (2,0) PRIMARY KEY NOT NULL,
ACTOR_FIRST CHAR(25),
ACTOR_LAST CHAR(30),
DOB DATE
);
CREATE TABLE DIRECTOR (
DIRECTOR_ID DECIMAL(2,0) PRIMARY KEY NOT NULL,
DIRECTOR_FIRST CHAR(25),
DIRECTOR_LAST CHAR(30)
);
CREATE TABLE FILM (
FILM_ID DECIMAL(2,0) PRIMARY KEY NOT NULL,
TITLE CHAR(50),
RELEASE_DATE DATE,
DIRECTOR_ID DECIMAL(2,0),
GENRE_ID CHAR(10),
PRODUCTION_ID CHAR(3)
);
The VIEW I'm trying to create:
create view film_production as
select
title,
actor_first,
actor_last,
director_first,
director_last,
Production_id,
release_date
from film,
actor,
director
;
This gives me like 192 results. But I only have 3 actors, with 8 movies with 8 directors, and 6 production_id's. Another thing to note is 1 movie has 2 of the 3 actors in it, so I should have at least 9 movies that show not 192. I don't know what I'm doing wrong. I just cant figure this out. I'm thinking maybe a join or some kind.
Yes, your view should use joins.
BUT, it looks like you are missing a table, something like film_actor.
Using the missing table, and assuming you are trying to get the cast and directors of each movie it would be
SELECT
FILM.*
,ACTOR_FIRST + ' ' + ACTOR_LAST AS actor
,DIRECTOR_FIRST + ' ' + DIRECTOR_LAST AS director
FROM FILM
INNER JOIN FILM_ACTOR fa ON fa.FILM_ID = FILM.FILM_ID
INNER JOIN ACTOR ON ACTOR.ACTOR_ID = FILM_ACTOR.ACTOR_ID
INNER JOIN DIRECTOR d ON d.DIRECTOR_ID = FILM.DIRECTOR_ID
Like I mentioned, you are missing a table that would hold multiple actors and associate them to one movie: a one to many relationship.
Have you thought of adding referential keys between actor and film, and director and film tables?
Actor
CREATE TABLE ACTOR (
ID INT PRIMARY KEY NOT NULL,
FIRSTNAME VARCHAR(25),
LASTNAME VARCHAR(30),
DOB DATE
);
Director
CREATE TABLE DIRECTOR (
ID INT PRIMARY KEY NOT NULL,
FIRSTNAME VARCHAR(25),
LASTNAME VARCHAR(30)
);
Film with connections to actor and director
CREATE TABLE FILM (
FILM_ID INT PRIMARY KEY NOT NULL,
TITLE VARCHAR(50),
RELEASE_DATE DATE,
DIRECTOR_ID INT,
GENRE_ID INT,
PRODUCTION_ID INT,
ACTOR_ID INT,
CONSTRAINT FK_FILM_DIRECTOR_ID FOREIGN KEY (DIRECTOR_ID)
REFERENCES DIRECTOR(ID),
CONSTRAINT FK_FILM_ACTOR_ID FOREIGN KEY (ACTOR_ID)
REFERENCES ACTOR(ID)
);
View combining the 3 tables
create view film_production as
select
title,
a.firstname as actor_firstname,
a.lastname as actor_lastname,
d.firstname as director_firstname,
d.lastname as directory_lastname,
Production_id,
release_date
from film f
left join actor a on f.actor_id = a.id
left join director d on f.director_id = d.id;
Example: http://sqlfiddle.com/#!9/9fe1e
Note that I have used INT instead of DECIMAL for ID fields and VARCHAR instead of CHAR fields. This may help in allocating the right space for most general purpose uses.

Joining 2 tables, need to display all fields based on highest price, with no duplicates from a different field

I need to show all fields on the most expensive car of each manufacturer.
The two tables being used are:
CREATE TABLE CARS
(
Vehicle_Identification_Number int(10) NOT NULL UNIQUE,
Manufacturers_ID int(5),
Owner_ID int(10),
Model varchar(25),
Manufaturer_Year int(4),
Mileage int(10),
Price int(10),
PRIMARY KEY (Vehicle_Identification_Number),
FOREIGN KEY (Manufacturers_ID) REFERENCES MANUFACTURERS (Manufacturers_ID),
FOREIGN KEY (Owner_ID) REFERENCES OWNERS (Owner_ID)
)
ENGINE= innodb;
CREATE TABLE MANUFACTURERS
(
Manufacturers_ID int(5) UNIQUE,
Name varchar(15) UNIQUE,
City varchar(30),
State char(2),
Zip char(5),
Phone char(10),
PRIMARY KEY (Manufacturers_ID)
)
ENGINE= innodb;
What I have working so far is:
SELECT *
FROM MANUFACTURERS
LEFT JOIN CARS
ON MANUFACTURERS.Manufacturers_ID = CARS.Manufacturers_ID
UNION
SELECT *
FROM MANUFACTURERS
RIGHT JOIN CARS
ON MANUFACTURERS.Manufacturers_ID = CARS.Manufacturers_ID
ORDER BY Price DESC;
Here is where I am stuck, everything I have tried left me with an error message. Any help would be appreciated.
I'm not sure what you mean by "no duplicates from a different field".
The highest price for each manufacturer is this set. (There might be more than one car at the highest price.)
select Manufacturers_ID, max(Price)
from cars
group by Manufacturers_ID;
Join on both columns in that set. (Second inner join, below.)
select MANUFACTURERS.*, CARS.*
from CARS
inner join MANUFACTURERS
on MANUFACTURERS.Manufacturers_ID = CARS.Manufacturers_ID
inner join (select Manufacturers_ID, max(Price) as Price
from cars
group by Manufacturers_ID) as MOST_EXPENSIVE
on CARS.Manufacturers_ID = MOST_EXPENSIVE.Manufacturers_ID
and CARS.Price = MOST_EXPENSIVE.Price;

How to write an SQL SELECT statement that returns values from multiple tables?

I've got four tables: person, coach, player, games. I need to write an SQL SELECT statement that will return: my id, name, date of birth, join date, the name of the coach with whom I am registered, and the date, time and duration of the game.
The person table consists of: person id, name, date of birth, and some other unnecessary values.
The coach table consists of: coach id, and some other unnecessary values.
The player table consists of: player id, join date, joined with.
The game table consists of: coach id, player id, game date, game time, game duration.
The person table basically stores all the names and main details both of the coach and of the players. The tables are linked correctly.
I've tried various statements but I totally confused myself. The example is kind of lame, I had something else but not really sure how to go about it anymore.
e.g.
SELECT person_id, full_name, date_of_birth, join_date
FROM (person JOIN player ON player_id = person_id)
WHERE person_id='100' AND person_id, full_name
FROM (person JOIN coach ON coach_id = person_id);
These are the CREATE statements:
CREATE TABLE person (
person_id CHAR(10) NOT NULL,
full_name VARCHAR(54) NOT NULL,
date_of_birth DATE,
sex CHAR(1),
PRIMARY KEY (person_id)
) engine innodb;
CREATE TABLE coach (
coach_id CHAR(10) NOT NULL,
phone_no CHAR(10) NOT NULL,
hall_no CHAR(4) NOT NULL,
PRIMARY KEY (coach_id),
FOREIGN KEY (coach_id) REFERENCES person (person_id)
) engine innodb;
CREATE TABLE player (
player_id CHAR(10) NOT NULL,
join_date DATE NOT NULL,
joined_with CHAR(10),
PRIMARY KEY (player_id),
FOREIGN KEY (player_id) REFERENCES person (person_id),
FOREIGN KEY (joined_with) REFERENCES coach (coach_id)
) engine innodb;
CREATE TABLE game (
coach_id CHAR(10) NOT NULL,
player_id CHAR(10) NOT NULL,
game_date DATE NOT NULL,
game_time TIME NOT NULL,
game_duration INTEGER DEFAULT 10,
PRIMARY KEY (coach_id, player_id, game_date, game_time),
FOREIGN KEY (coach_id) REFERENCES coach (coach_id),
FOREIGN KEY (player_id) REFERENCES player (player_id)
) engine innodb;
Your SQL is WAY off. It looks like you're trying for the following just using several INNER JOINs:
SELECT p.person_id, p.full_name, p.date_of_birth, pl.join_date,
c.coach_id, p2.full_name as coach_name, g.*
FROM person p
JOIN player pl ON p.person_id = pl.player_id
JOIN game g ON p.person_id = g.player_id
JOIN coach c ON c.coach_id = g.coach_id
JOIN person p2 on c.coach_id= p2.person_id
WHERE p.person_id='1'
ORDER BY p.person_id, p.full_name
You won't need to rejoin on person a second time if you don't need the coach's name -- I just presumed that would be useful information.
SQL Fiddle Demo
I hope this helps.
SELECT person.person_id, person.full_name, person.date_of_birth, player.join_date
FROM person
INNER JOIN player
ON player.player_id = person.person_id
WHERE person.person_id = '100'