SQL-EX.ru Exercise #36 - mysql

I am trying to solve exercise #36 which is List the names of head ships in the database (taking into account Outcomes table).
Head ships are the names of ships that were first in line for that class (i.e. 2 ships but 1 build before another so the first ship is the "head ship"). My result says I did says the following:
Your query returned the correct dataset on the first (available) database, but it returned incorrect dataset on the second checking database.
* Wrong number of records (less by 1)
My query is as follows:
SELECT A.name FROM Ships A
WHERE A.name = A.class
AND A.launched = (SELECT MIN(B.launched) FROM Ships B
WHERE A.name = B.name
AND A.class = B.class)
UNION ALL
SELECT A.ship
FROM Outcomes A
WHERE A.ship IN (SELECT C.ship FROM Outcomes C, Classes D
WHERE C.ship IN (SELECT ship FROM Outcomes)
AND C.ship NOT IN (SELECT name FROM Ships)
AND C.ship = D.class
)
Database is setup as follows:
The database of naval ships that took part in World War II is under consideration.
The database has the following relations:
Classes(class, type, country, numGuns, bore, displacement)
Ships(name, class, launched)
Battles(name, date)
Outcomes(ship, battle, result)
Ships in classes are arranged to a single project. A class is normally assigned the name of the first ship in the class under consideration (head ship); otherwise, the class name does not coincide with any ship name in the database.
The Classes relation includes the class name, type (bb for a battle ship, or bc for a battle cruiser), country where the ship was built, number of main guns, gun caliber (diameter of the gun barrel, in inches), and displacement (weight in tons).
The Ships relation includes the ship name, its class name, and launch year.
The Battles relation covers the name and date of a battle the ships participated; while the result of their participation in the battle (sunk, damaged, or unharmed - OK) is in the Outcomes relation.
Notes:
Outcomes relation may include the ships not included in the Ships relation.
Sunk ship can not participate in battles after that.
Now my query returns the correct answer but on the union it is not returning the correct value? My understanding is that one of the ship names does not exist in the Ships table so I picked it up from Outcomes to resolve the issue. But even that is not correct. Is there anything I am missing?

Head ships are the ships that have the same name as the class they belong too, I worked wit that and it worked:
WITH union1 (name) AS
(SELECT name FROM ships WHERE name = class
UNION SELECT ship AS name FROM outcomes
INNER JOIN classes ON classes.class = outcomes.ship)
SELECT DISTINCT name FROM union1

A simpler solution:
select name from ships where name IN (select class from classes)
UNION
select ship from outcomes where ship IN (select class from classes)

Select name from ships where name = class
union
select ship from outcomes join classes on outcomes.ship = classes.class

Related

Access SQL query for sorting

enter image description here - Relationships
Find employees who took some kind of management class (i.e., a class whose name ends with management). List employees’ last and first name, class name, and the class date. Sort the query output by last name in ascending.
I did the following, But the query is not working. Cannot find the ClassName that ends with management.
SELECT E.Last, E.First, C.ClassName, C.Date
FROM EMPLOYEES AS E, EMPLOYEE_TRAINING AS ET, CLASSES AS C
WHERE E.EmployeeID = ET.EmployeeID AND C.ClassID = ET.ClassID AND C.ClassName LIKE "Management*"
ORDER BY E.Last ASC;
Replace "Management*" with "*Management" or "%Management" because you want to find ClassName that ends with Management, not start with Management

Multiple Nested Joins in SQL Query

I have a fairly simple database for a European style School system. A School can have multiple classes, Teachers can have assigned one or more classes from any School. Each student can have assigned a single class, and can have multiple grades from each teacher assigned to the class.
The checks for weather or not the teacher is assigned to the student class is done inside the software itself.
Here is the database diagram:
Database Diagram
I have to do get back the following reports using SQL:
Report Grades by Class: Select a class and have a table with every students grades, their average and the teacher who assigned the grade. (Table Sturcture: Teacher, Student, Grade_01, Grade_02, Grade_03, Grade_04, Average Grade)
Report Average Teacher Grade by School: Display a table with the teacher name, his class, and the average grade for the entire class. (Table Structure: Teacher, Class, Average Grade)
I managed to do the first "Report Grades by Class" Table by using the following SQL Code:
SELECT grades.grade_id, teachers.teacher_name, students.student_name, grades.grade_01, grades.grade_02, grades.grade_03, grades.grade_04
FROM grades
JOIN teachers
ON teachers.teacher_id = grades.teacher_id
JOIN students
ON students.student_id = grades.student_id
WHERE students.class_id = ?
But I don't know how to go about generating the second report, I have tried countless times...
So, how do I display a table with the teacher name, his class, and the average grade for the entire class?
Any help would be greatly appreciated.
select
teacher_name as 'Teacher',
class_name as 'Class',
avg(grade_01 + grade_02 + grade_03 + grade_04) as 'Average Grade' from teachers
join class_teacher on teachers.teacher_id = class_teacher.teacher_id
join clases on clases.class_id = class_teacher.class_id
join grades on grades.teacher_id = teachers.teacher_id
group by teacher_name, class_name
Thanks for your answer AnumR
I have modified the code you provided to make it so you can select the teachers and classes by the school ID.
But the code outputs the average by teacher and not by class.
SELECT teacher_name AS 'Teacher', class_name AS 'Class',
AVG(grade_01 + grade_02 + grade_03 + grade_04)/4 AS 'Average Grade' FROM teachers
JOIN class_teacher ON teachers.teacher_id = class_teacher.teacher_id
JOIN clases ON clases.class_id = class_teacher.class_id
JOIN grades ON grades.teacher_id = teachers.teacher_id
WHERE clases.school_id = 2
GROUP BY teacher_name, class_name
The fundamental problem here (and one I suspect you're reluctant to address) is one of poor design.
While by no means a definitive solution, a valid design might be as follows:
Teacher, Student, Grade, Score
Average grade would then be something calculated on the fly.

ALL sql with subquery, and NOT exist

Im doing a sql problem and the question is this:
Get all the ship's classes of Russia. If there are no classes of Russia in the database, get all the classes in the database.
Result set: country, class
3 answers (#3 is wrong):
select country, class from Classes
where country = ALL(select country from Classes
where country='Russia')
select country, class
from classes
where not exists (select country
from classes
where country='Russia')
or country='Russia'
select distinct country, class
from classes
where country not in(select country
from classes
where country='Russia')
or country='Russia'
Can anyone explain how the "ALL example works", I dont fully understand how this covers all the cases above: finding only russian classes and if none all the others.
Can anoyone also explain how ans2 and wrong ans3 is different, I think it might have something to do with null values but im not too sure..im quite new to sql
The all is a comparison to all values in the subquery and I don't think the way you are using it is very useful.
It would be much more useful for a numerical comparison like greater than, less than, etc.
From the MySQL site:
SELECT s1 FROM t1 WHERE s1 > ALL (SELECT s1 FROM t2);
This would select all the s1's from t1 that are greater than all of the s1's from t2.
Your example
Your example: select country, class from Classes where country = ALL(select country from Classes where country='Russia') this subquery is only returning a bunch of rows of 'Russia' as you're only selecting country where country equals Russia.
However, ALL, as documented in Oracle (not documented in MySQL) will return a true condition if no values are present allowing you to select all from Russia unless no Russia rows exist.

Multiple SQL Join

I have to write a query that retrieves data from multiple tables, I have tried all my best to get it but keeps getting stuck.
I have the following tables:
Teacher:
teacher_code
initial
name
Student:
stud_no
initial
insertions
name
birth_date
address
city
class_code
Class:
class_code
classname
Module:
module_code
modulename
Teacher_module:
teacher_code
module_code
class_code
Grade:
stud_no
module_code
date
grade
And this is the query I need to write:
Show the names of the students with associated grades for the Databases examination of 21-11-1001.
I really appreciate your time.
I'm guessing "Databases" is a modulename.
I'm hoping 21-11-1001 is the grade.date, not some sort of code.
I don't know why you listed sql-server and mysql. I'm going to assume mysql, because I never get to use natural joins in real life. I'd be assuming any schema/references anyway, so I'll just let the engine figure it out.
select s.name, g.grade
from grades g
natural join student s
natural join module m
where m.modulename='Databases'
and g.date='1001-11-21'
there you go, grades from a thousand years ago.
Something like this?:
select s.name, g.grade
from student s
join grade g on s.stud_no = g.stud_no
join module m on m.module_code = g.module_code
where m.modulename = '21-11-1001'
It depends on which column contains 21-11-1001. If it is in the module table in the modulename column, then that should work.

MYSQL Tuple in IN CLause with wildcard matching

I have the following problem. suppose I have a table containing an inventory of cars:
VIN, MAKE, MODEL, PRICE
AND I have another table containing various car classes( eg, compact, mid-sized, suv, etc)
MAKE, MODEL, CLASS
JEEP, NULL , SUV
FORD, EXPLORER, SUV
TOYOTA, YARIS, COMPACT
NULL, CUBE, COMPACT
...
I am wondering how can I select all cars in the database of a certain class? Perhaps, I want to know how many cars of each class exists?
I can write a query like this.
SELECT COUNT(*) FROM CARS WHERE (MAKE, MODEL) IN (SELECT MAKE, MODEL FROM CLASSES WHERE CLASS = 'SUV')
The problem here, I wont actually be able to deal with NULLs in my data. I want to say that all models of JEEP are SUVs, or any MAKE car that's called CUBE would be a compact car.
Could this be done assuming there are no conflicts? Can I have a priority set up, like All Porches are CARS except the Cayenne which is an SUV by doing something like this:
MAKE, MODEL, CLASS
PORCHE, NULL , CAR
PORCHE, CAYENNE, SUV
If this isn't possible with MySQL, is there a better DB technology out there that would be good at this. Lets assume that The CLASSES Table would Contain 50K+ Rows and The CARS Table would contain 5M+ Rows. I am looking for a fast way of performing such a query in the database, and not needing to fetch millions of rows to process in a script? Also What if its not just Make and Model, but also sub-model, engine, etc..
Also, I simplified the data quite a bit, there are 4 levels of hierarchy.
Assuming that make and model cannot be both null for a given class. You can use the ifnull() function in MySQL:
select cl.class, count(*)
from cars c inner join class cl
on c.make = ifnull(cl.make,c.make)
and c.model=ifnull(cl.model,c.model)
group by cl.class
You may want to add an index on columns makes and model for faster access. Since classes have fewer rows using an inner join (as above) will restrict the number of rows returned.
You can write your query like this:
SELECT COUNT(*)
FROM CARS c join
classes cl
on (c.make = cl.make or cl.make is null) and
(c.model = cl.model or cl.model is null) and
cl.class = 'SUV'
The problem is that you might get duplicates. So the better count starts with:
select count(distinct c.vin)
. . .
This should work for the two cases you gave, for JEEP and CUBE.
SELECT s.class, COUNT(*)
FROM car c, car_class s
WHERE c.make = s.make
AND c.model = s.model
GROUP BY s.class
should give you something like
SUV 14
COMPACT 32
TRUCK 11
etc.