Mysql classic 50 SQL exercises, answers and detailed analysis

Posted by twistedmando on Mon, 10 Jan 2022 23:52:21 +0100

Data preparation

  • 1. Student form
    Student(SId,Sname,Sage,Ssex)
    – SId student number, Sname student name, Sage date of birth, Ssex student gender
create table Student(SId varchar(10),Sname varchar(10),Sage datetime,Ssex varchar(10));
insert into Student values('01' , 'Lei Zhao' , '1990-01-01' , 'male');
insert into Student values('02' , 'Qian Dian' , '1990-12-21' , 'male');
insert into Student values('03' , 'Sun Feng' , '1990-12-20' , 'male');
insert into Student values('04' , 'Li Yun' , '1990-12-06' , 'male');
insert into Student values('05' , 'Zhou Mei' , '1991-12-01' , 'female');
insert into Student values('06' , 'Wu Lan' , '1992-01-01' , 'female');
insert into Student values('07' , 'Zheng Zhu' , '1989-01-01' , 'female');
insert into Student values('09' , 'Zhang San' , '2017-12-20' , 'female');
insert into Student values('10' , 'Li Si' , '2017-12-25' , 'female');
insert into Student values('11' , 'Li Si' , '2012-06-06' , 'female');
insert into Student values('12' , 'Zhao Liu' , '2013-06-13' , 'female');
insert into Student values('13' , 'Sun Qi' , '2014-06-01' , 'female');
  • 2. Curriculum
    Course(CId,Cname,TId)
    – CId course number, Cname course name, TId teacher number
create table Course(CId varchar(10),Cname nvarchar(10),TId varchar(10));
insert into Course values('01' , 'language' , '02');
insert into Course values('02' , 'mathematics' , '01');
insert into Course values('03' , 'English' , '03');
  • 3. Teacher list
    Teacher(TId,Tname)
    – TId teacher number, Tname teacher name
create table Teacher(TId varchar(10),Tname varchar(10));
insert into Teacher values('01' , 'Zhang San');
insert into Teacher values('02' , 'Li Si');
insert into Teacher values('03' , 'Wang Wu');
  • 4. Transcript
    SC(SId,CId,score)
    – SId student number, CId course number, score
create table SC(SId varchar(10),CId varchar(10),score decimal(18,1));
insert into SC values('01' , '01' , 80);
insert into SC values('01' , '02' , 90);
insert into SC values('01' , '03' , 99);
insert into SC values('02' , '01' , 70);
insert into SC values('02' , '02' , 60);
insert into SC values('02' , '03' , 80);
insert into SC values('03' , '01' , 80);
insert into SC values('03' , '02' , 80);
insert into SC values('03' , '03' , 80);
insert into SC values('04' , '01' , 50);
insert into SC values('04' , '02' , 30);
insert into SC values('04' , '03' , 20);
insert into SC values('05' , '01' , 76);
insert into SC values('05' , '02' , 87);
insert into SC values('06' , '01' , 31);
insert into SC values('06' , '03' , 34);
insert into SC values('07' , '02' , 89);
insert into SC values('07' , '03' , 98);

Exercise topic

Query the information and course scores of students with higher scores in "01" course than "02" course

Because you need all the student information, you need to get the qualified SId in the sc table and join it with the student table, either left join or right join

select * from Student RIGHT JOIN (
    select t1.SId, class1, class2 from
          (select SId, score as class1 from sc where sc.CId = '01')as t1, 
          (select SId, score as class2 from sc where sc.CId = '02')as t2
    where t1.SId = t2.SId AND t1.class1 > t2.class2
)r 
on Student.SId = r.SId;
select * from  (
    select t1.SId, class1, class2 
    from
        (SELECT SId, score as class1 FROM sc WHERE sc.CId = '01') AS t1, 
        (SELECT SId, score as class2 FROM sc WHERE sc.CId = '02') AS t2
    where t1.SId = t2.SId and t1.class1 > t2.class2
) r 
LEFT JOIN Student
ON Student.SId = r.SId;

Query the results of "01" and "02" courses at the same time

select * from 
    (select * from sc where sc.CId = '01') as t1, 
    (select * from sc where sc.CId = '02') as t2
where t1.SId = t2.SId;

Query the score of "01" course but may not exist "02" course (null if it does not exist)

This is the obvious need to use join. 02 may not exist, that is, it is the right side of left join or the left side of right join

select * from 
(select * from sc where sc.CId = '01') as t1
left join 
(select * from sc where sc.CId = '02') as t2
on t1.SId = t2.SId;
select * from 
(select * from sc where sc.CId = '02') as t2
right join 
(select * from sc where sc.CId = '01') as t1
on t1.SId = t2.SId;

Query the results of "01" but "02" courses that do not exist

select * from sc
where sc.SId not in (
    select SId from sc 
    where sc.CId = '01'
) 
AND sc.CId= '02';

Query the student number, student name and average score of students whose average score is greater than or equal to 60

Here, we can only group the scores according to the student ID, average the scores in the group, and finally select those with AVG greater than 60 in the results Note that an alias must be given to the calculated AVG result (AS ss)
When getting student information, you can use either join or general joint search

select student.SId,sname,ss from student,(
    select SId, AVG(score) as ss from sc  
    GROUP BY SId 
    HAVING AVG(score)> 60
    )r
where student.sid = r.sid;
select Student.SId, Student.Sname, r.ss from Student right join(
      select SId, AVG(score) AS ss from sc
      GROUP BY SId
      HAVING AVG(score)> 60
)r on Student.SId = r.SId;
select s.SId,ss,Sname from(
select SId, AVG(score) as ss from sc  
GROUP BY SId 
HAVING AVG(score)> 60
)r left join 
(select Student.SId, Student.Sname from
Student)s on s.SId = r.SId;

Query the information of students with scores in SC table

select DISTINCT student.*
from student,sc
where student.SId=sc.SId

Query the student number, student name, total number of courses selected and the total score of all courses of all students (null if there is no score)

Joint query will not display students who have not selected courses:

select student.sid, student.sname,r.coursenumber,r.scoresum
from student,
(select sc.sid, sum(sc.score) as scoresum, count(sc.cid) as coursenumber from sc 
group by sc.sid)r
where student.sid = r.sid;

To display students who have not selected a course (displayed as NULL), you need to use left join:

select s.sid, s.sname,r.coursenumber,r.scoresum
from (
    (select student.sid,student.sname 
    from student
    )s 
    left join 
    (select 
        sc.sid, sum(sc.score) as scoresum, count(sc.cid) as coursenumber
        from sc 
        group by sc.sid
    )r 
   on s.sid = r.sid
);

Check the information of students with grades

This question involves the usage of in and exists. In this small table, the efficiency of the two methods is similar. Please refer to the specific difference analysis between in and exists https://www.jianshu.com/p/f212527d76ff
When the number of records in Table 2 is very large, using EXISTS is much more efficient than in EXISTS is used to check whether a subquery will return at least one row of data. The subquery does not actually return any data, but returns the value True or False
Conclusion: IN() is suitable for the situation that the data in table B is smaller than that in table A
Conclusion: EXISTS() is suitable for the situation that the data of table B is larger than that of table A

select * from student 
where exists (select sc.sid from sc where student.sid = sc.sid);
#perhaps
select * from student 
where exists (select 1 from sc where student.sid = sc.sid);
select * from student
where student.sid in (select sc.sid from sc);

Query the number of teachers surnamed "Li"

select count(*)
from teacher
where tname like 'Lee%';

Query the information of students who have learned "Zhang San"

Multi table joint query

select student.* from student,teacher,course,sc
where 
    student.sid = sc.sid 
    and course.cid=sc.cid 
    and course.tid = teacher.tid 
    and tname = 'Zhang San';

Query the information of students who have not studied all courses

Because some students didn't choose any courses, think backwards. First query the students who chose all courses, and then select the students other than these people

select * from student
where student.sid not in (
  select sc.sid from sc
  group by sc.sid
  having count(sc.cid)= (select count(cid) from course)
);

Query the information of at least one course that is the same as the student with student number "01"

This joint query is OK, but the logic is not clear. I think the clearer logic is as follows: query all the course cid of 01 students from the sc table – query all the students' Sid from the sc table if their cid is in the previous results – query all the students' information from the student table if the sid is in the previous results

select * from student 
where student.sid in (
    select sc.sid from sc 
    where sc.cid in(
        select sc.cid from sc 
        where sc.sid = '01'
    )
);

Query the information of other students who have exactly the same courses as the students of "01"

Query the names of students who have not studied any course taught by "Zhang San"

It is still nested, three-tier nested, or multi table joint query

select * from student
    where student.sid not in(
        select sc.sid from sc where sc.cid in(
            select course.cid from course where course.tid in(
                select teacher.tid from teacher where tname = "Zhang San"
            )
        )
    );
select * from student
where student.sid not in(
    select sc.sid from sc,course,teacher 
    where
        sc.cid = course.cid
        and course.tid = teacher.tid
        and teacher.tname= "Zhang San"
);

Query the student number, name and average score of students who fail two or more courses

select student.SId, student.Sname,b.avg
from student RIGHT JOIN
(select sid, AVG(score) as avg from sc
    where sid in (
              select sid from sc 
              where score<60 
              GROUP BY sid 
              HAVING count(score)>1)
    GROUP BY sid) b on student.sid=b.sid;

Retrieve the student information in descending order of "01" course score less than 60

select student.*, sc.score from student, sc
where student.sid = sc.sid
and sc.score < 60
and cid = "01"
ORDER BY sc.score DESC;

Displays the scores of all courses and average scores of all students from high to low

select *  from sc 
left join (
    select sid,avg(score) as avscore from sc 
    group by sid
    )r 
on sc.sid = r.sid
order by avscore desc;

Query the highest score, lowest score and average score of each subject:

It is displayed in the following form: course ID, course name, highest score, lowest score, average score, pass rate, medium rate, excellent rate and excellent rate

Pass is > = 60, medium is 70-80, excellent is 80-90, excellent is > = 90

It is required to output the course number and the number of electives. The query results are arranged in descending order by the number of people. If the number of people is the same, they are arranged in ascending order by the course number

select 
sc.CId ,
max(sc.score)as Highest score,
min(sc.score)as Lowest score,
AVG(sc.score)as average,
count(*)as Number of electives,
sum(case when sc.score>=60 then 1 else 0 end )/count(*)as pass rate,
sum(case when sc.score>=70 and sc.score<80 then 1 else 0 end )/count(*)as Medium rate,
sum(case when sc.score>=80 and sc.score<90 then 1 else 0 end )/count(*)as Excellent rate,
sum(case when sc.score>=90 then 1 else 0 end )/count(*)as Excellent rate 
from sc
GROUP BY sc.CId
ORDER BY count(*)DESC, sc.CId ASC

Sort according to the scores of each subject and display the ranking. If the Score is repeated, the ranking vacancy will be reserved

This problem is a little tricky. You can use variables, but there is also a simpler method, that is, self intersection (left intersection)
Compare the score in sc with yourself to calculate "how many scores are higher than the current score".
Sort according to the scores of each subject and display the ranking. When Score is repeated, the ranking will be merged

select a.cid, a.sid, a.score, count(b.score)+1 as rank
from sc as a 
left join sc as b 
on a.score<b.score and a.cid = b.cid
group by a.cid, a.sid,a.score
order by a.cid, rank ASC;

Query the total score of students and rank them. If the total score is repeated, the ranking vacancy will be reserved

Here we mainly learn how to use variables. In SQL, variables are identified with @.

set @crank=0;
select q.sid, total, @crank := @crank +1 as rank from(
select sc.sid, sum(sc.score) as total from sc
group by sc.sid
order by total desc)q;

Query the total score of students and rank them. If the total score is repeated, the ranking vacancy will not be reserved

Count the number of students in each score segment of each subject: course number, course name, [100-85], [85-70], [70-60], [60-0] and percentage

Sometimes I feel like a dead brain. The alias cannot be used for the query results after group by, so don't think about calculating the results from a single table group by and then adding the course information from the second table. Instead, join the two tables together to get all the desired attributes, and then perform statistical calculation on the total table. There is no percentage here. The reason is the same.
Note that the statistics after case when returns 1 are not count but sum

select course.cname, course.cid,
sum(case when sc.score<=100 and sc.score>85 then 1 else 0 end) as "[100-85]",
sum(case when sc.score<=85 and sc.score>70 then 1 else 0 end) as "[85-70]",
sum(case when sc.score<=70 and sc.score>60 then 1 else 0 end) as "[70-60]",
sum(case when sc.score<=60 and sc.score>0 then 1 else 0 end) as "[60-0]"
from sc left join course
on sc.cid = course.cid
group by sc.cid;

Query the top three records of each subject

There are several records whose scores are greater than your own. If it is less than 3, select, because there will be no more than 3 scores greater than your own for the top three. Finally, rank all the selected results according to the scores and course number.

select * from sc
where (
select count(*) from sc as a 
where sc.cid = a.cid and sc.score<a.score 
)< 3
order by cid asc, sc.score desc;

Query the number of students selected for each course

select cid, count(sid) from sc 
group by cid;

Find out the student number and name of students who only take two courses

select student.sid, student.sname from student
where student.sid in
(select sc.sid from sc
group by sc.sid
having count(sc.cid)=2
);

Or joint query

select student.SId,student.Sname
from sc,student
where student.SId=sc.SId  
GROUP BY sc.SId
HAVING count(*)=2;

Query the number of boys and girls

select ssex, count(*) from student
group by ssex

Query the student information with the word "wind" in the name

select *
from student 
where student.Sname like '%wind%'

Query the list of same-sex students with the same name and count the number of students with the same name

select sname, count(*) from student
group by sname
having count(*)>1;

Nested query lists the information of all students with the same name

select * from student
where sname in (
select sname from student
group by sname
having count(*)>1
);

Check the list of students born in 1990

select *
from student
where YEAR(student.Sage)=1990;

Query the average score of each course. The results are arranged in descending order according to the average score. If the average score is the same, they are arranged in ascending order according to the course number

select sc.cid, course.cname, AVG(SC.SCORE) as average from sc, course
where sc.cid = course.cid
group by sc.cid 
order by average desc,cid asc;

Query the student number, name and average score of all students whose average score is greater than or equal to 85

select student.sid, student.sname, AVG(sc.score) as aver from student, sc
where student.sid = sc.sid
group by sc.sid
having aver > 85;

Query the names and scores of students whose course name is "Mathematics" and whose score is less than 60

select student.sname, sc.score from student, sc, course
where student.sid = sc.sid
and course.cid = sc.cid
and course.cname = "mathematics"
and sc.score < 60;

Query the courses and scores of all students (there are cases where students do not have grades and do not choose courses)

select student.sname, cid, score from student
left join sc
on student.sid = sc.sid;

Query the name, course name and score of any course with a score of more than 70

select student.sname, course.cname,sc.score from student,course,sc
where sc.score>70
and student.sid = sc.sid
and sc.cid = course.cid;

Query failed courses

group by or distinct can be used to get unique

select cid from sc
where score< 60
group by cid;
select DISTINCT sc.CId
from sc
where sc.score <60;

Query the student number and name of students with course number 01 and course score above 80

select student.sid,student.sname 
from student,sc
where cid="01"
and score>=80
and student.sid = sc.sid;

Number of students per course

select sc.CId,count(*) as Number of students
from sc
GROUP BY sc.CId;

The results are not repeated. Query the information and results of the students with the highest scores among the students taking the courses taught by teacher Zhang San

Using having max() is also theoretically correct, but the following sort by score and then take limit 1 is more intuitive and reliable

select student.*, sc.score, sc.cid from student, teacher, course,sc 
where teacher.tid = course.tid
and sc.sid = student.sid
and sc.cid = course.cid
and teacher.tname = "Zhang San"
having max(sc.score);

Taking limit 1 is more intuitive and reliable

select student.*, sc.score, sc.cid from student, teacher, course,sc 
where teacher.tid = course.tid
and sc.sid = student.sid
and sc.cid = course.cid
and teacher.tname = "Zhang San"
order by score desc
limit 1;

In case of repeated scores, query the information of the students with the highest scores and their scores among the students taking the courses taught by teacher Zhang San

In order to verify this problem, modify the original data first

UPDATE sc SET score=90
where sid = "07"
and cid ="02";

In this way, two students in class 02 taught by Mr. Zhang San got the highest score of 90 at the same time.
The idea of this question continues from the previous question. We have found the highest score that meets the limited conditions. At this time, just compare this table and find the record that all scores are equal to the highest score. It seems a little complicated.

select student.*, sc.score, sc.cid from student, teacher, course,sc 
where teacher.tid = course.tid
and sc.sid = student.sid
and sc.cid = course.cid
and teacher.tname = "Zhang San"
and sc.score = (
    select Max(sc.score) 
    from sc,student, teacher, course
    where teacher.tid = course.tid
    and sc.sid = student.sid
    and sc.cid = course.cid
    and teacher.tname = "Zhang San"
);

Query the student number, course number and student score of students with the same score in different courses

The same as above. After using inner join here, there will be a duplicate record of the concept: "01 and 03" = "03 and 01", so the only one here can be used directly

select  a.cid, a.sid,  a.score from sc as a
inner join 
sc as b
on a.sid = b.sid
and a.cid != b.cid
and a.score = b.score
group by cid, sid;

Query the top two with the best scores in each skill

select a.sid,a.cid,a.score from sc as a 
left join sc as b 
on a.cid = b.cid and a.score<b.score
group by a.cid, a.sid
having count(b.cid)<2
order by a.cid;

Count the number of students in each course (only courses with more than 5 people are counted).

select sc.cid, count(sid) as cc from sc
group by cid
having cc >5;

Retrieve the student number of at least two courses

select sid, count(cid) as cc from sc
group by sid
having cc>=2;

Query the student information of all courses

select student.*
from sc ,student 
where sc.SId=student.SId
GROUP BY sc.SId
HAVING count(*) = (select DISTINCT count(*) from course )

Query the age of each student, calculated by year only

According to the date of birth, if the current month day is less than the month day of the year of birth, the age will be reduced by one

select student.SId as Student number,student.Sname  as  Student name,
TIMESTAMPDIFF(YEAR,student.Sage,CURDATE()) as Student age
from student

Inquire about the students whose birthday is this week

select *
from student 
where WEEKOFYEAR(student.Sage)=WEEKOFYEAR(CURDATE());

Check the students whose birthday is next week

select *
from student 
where WEEKOFYEAR(student.Sage)=WEEKOFYEAR(CURDATE())+1;

Query students whose birthdays are this month

select *
from student 
where MONTH(student.Sage)=MONTH(CURDATE());

Query the students whose birthday is next month

select *
from student 
where MONTH(student.Sage)=MONTH(CURDATE())+1;

Original text: https://www.jianshu.com/p/476b52ee4f1b

Topics: Database