@[目录]
这是超经典SQL练习题,做完这些你的SQL就过关了的题目在PostgreSQL14上的解答。
CREATE DATABASE edudb_test;
CREATE TABLE student(
s_id VARCHAR(10),
s_name VARCHAR(20),
s_age DATE,
s_sex CHAR(1)
);
INSERT INTO student(s_id, s_name, s_age, s_sex) VALUES
('01' , '赵雷' , '19900101' , '男'),
('02' , '钱电' , '19901221' , '男'),
('03' , '孙风' , '19900520' , '男'),
('04' , '李云' , '19900806' , '男'),
('05' , '周梅' , '19911201' , '女'),
('06' , '吴兰' , '19920301' , '女'),
('07' , '郑竹' , '19890701' , '女'),
('08' , '王菊' , '19900120' , '女')
;
CREATE TABLE course(
c_id VARCHAR(10),
c_name VARCHAR(20),
t_id VARCHAR(10)
);
INSERT INTO course VALUES
('01' , '语文' , '02'),
('02' , '数学' , '01'),
('03' , '英语' , '03')
;
CREATE TABLE teacher(
t_id VARCHAR(10),
t_name VARCHAR(20)
);
INSERT INTO teacher VALUES
('01' , '张三'),
('02' , '李四'),
('03' , '王五')
;
CREATE TABLE score(
s_id VARCHAR(10),
c_id VARCHAR(10),
sc_score DECIMAL(3,1)
);
INSERT INTO score VALUES
('01' , '01' , 80),
('01' , '02' , 90),
('01' , '03' , 99),
('02' , '01' , 70),
('02' , '02' , 60),
('02' , '03' , 80),
('03' , '01' , 80),
('03' , '02' , 80),
('03' , '03' , 80),
('04' , '01' , 50),
('04' , '02' , 30),
('04' , '03' , 20),
('05' , '01' , 76),
('05' , '02' , 87),
('06' , '01' , 31),
('06' , '03' , 34),
('07' , '02' , 89),
('07' , '03' , 98)
;
SELECT st.*, sc1.sc_score FROM student AS st INNER JOIN score AS sc1
ON st.s_id = sc1.s_id
WHERE c_id = '02'
AND sc1.sc_score > (SELECT sc2.sc_score FROM score AS sc2 WHERE c_id='01' AND sc1.s_id = sc2.s_id);
/*原博主的方法*/
SELECT sc1.*, sc2.c_id, sc2.sc_score
FROM (SELECT * FROM score WHERE c_id='01') AS sc1
LEFT JOIN
(SELECT * FROM score WHERE c_id='02') AS sc2
ON sc1.s_id = sc2.s_id
WHERE sc1.sc_score > sc2.sc_score;
SELECT sc1.*, sc2.c_id, sc2.sc_score
FROM (SELECT * FROM score WHERE c_id='01') AS sc1
INNER JOIN
(SELECT * FROM score WHERE c_id='02') AS sc2
ON sc1.s_id = sc2.s_id;
SELECT sc1.*, sc2.c_id, sc2.sc_score
FROM (SELECT * FROM score WHERE c_id='01') AS sc1
LEFT JOIN
(SELECT * FROM score WHERE c_id='02') AS sc2
ON sc1.s_id = sc2.s_id;
SELECT sc2.*
FROM (SELECT * FROM score WHERE c_id='01') AS sc1
RIGHT JOIN
(SELECT * FROM score WHERE c_id='02') AS sc2
ON sc1.s_id = sc2.s_id
WHERE sc1.sc_score is null;
/*原博主的方法*/
SELECT sc2.*
FROM score AS sc2
WHERE sc2.c_id = '02' and sc2.s_id not in (SELECT s_id FROM score WHERE c_id = '01');
SELECT st.s_id, st.s_name, sc.avg_score
FROM student AS st
INNER JOIN
(SELECT s_id, AVG(sc_score) AS avg_score FROM score GROUP BY s_id) AS sc
ON st.s_id = sc.s_id
WHERE sc.avg_score >= 60;
SELECT st.*
FROM student AS st
INNER JOIN
(SELECT DISTINCT s_id FROM score) AS sc
ON st.s_id = sc.s_id;
/*原博主的方法
因为没有连接查询,原博主的效率更高。*/
SELECT * FROM student WHERE s_id in (SELECT DISTINCT s_id FROM score);
SELECT st.s_id, st.s_name, sc.course_count, sc.sum_score
FROM student AS st
LEFT JOIN
(SELECT s_id, count(c_id) AS course_count, sum(sc_score) as sum_score FROM score GROUP BY s_id) AS sc
ON st.s_id = sc.s_id;
SELECT st.*
FROM student AS st
INNER JOIN
(SELECT DISTINCT s_id FROM score) AS sc
ON st.s_id = sc.s_id;
SELECT count(t_id) FROM teacher WHERE t_name like '李%';
SELECT st.* FROM
student AS st LEFT JOIN score AS sc ON st.s_id = sc.s_id
WHERE sc.c_id = (
SELECT c_id FROM course WHERE t_id = (
SELECT t_id FROM teacher WHERE t_name = '张三'
)
)
;
/*原博主的方法*/
SELECT * FROM student WHERE s_id in (
SELECT DISTINCT s_id FROM score
WHERE c_id = (SELECT c_id FROM Course
WHERE t_id = (SELECT t_id FROM teacher
WHERE t_name = '张三'
)
)
);
/*这里添加了IS NULL的条件。如果删除此条件,则不会显示一门课没考的同学信息*/
SELECT st.*, course_count FROM
student AS st
LEFT JOIN (
SELECT s_id, count(c_id) AS course_count FROM score GROUP BY s_id
) AS sc
ON st.s_id = sc.s_id
WHERE sc.course_count<3 OR sc.course_count IS NULL
;
/*原博主的方法*/
SELECT * FROM student WHERE s_id IN (
SELECT s_id FROM score
GROUP BY s_id HAVING count(c_id)<3
);
SELECT * FROM student WHERE s_id NOT IN (
SELECT s_id FROM score
GROUP BY s_id HAVING count(c_id)=3
);
SELECT st.* FROM
student AS st
INNER JOIN
(
SELECT DISTINCT s_id FROM score
WHERE c_id IN (SELECT c_id FROM score WHERE s_id='01') AND s_id!='01'
) AS sc
ON st.s_id = sc.s_id
;
/*原博主的方法*/
SELECT * FROM student WHERE s_id IN (
SELECT s_id FROM score
WHERE c_id in (SELECT DISTINCT c_id FROM score
WHERE s_id='01')
AND s_id!='01'
);
SELECT st.*
FROM student AS st
INNER JOIN (
SELECT s_id, count(c_id) AS count_c_id FROM score GROUP BY s_id
) AS sc
ON st.s_id = sc.s_id
WHERE sc.count_c_id = (SELECT count(c_id) FROM score WHERE s_id='01')
;
/*原博主的方法*/
SELECT * FROM student
WHERE s_id IN (
SELECT s_id FROM score
GROUP BY s_id HAVING count(c_id)=(
SELECT count(c_id) FROM score
WHERE s_id='01')
);
SELECT st.* FROM student AS st
WHERE st.s_id NOT IN (
SELECT s_id FROM score WHERE c_id IN (
SELECT c_id FROM course WHERE t_id = (
SELECT t_id FROM teacher WHERE t_name = '张三'
)
));
;
SELECT st.s_id, st.s_name, sc.avg_sc
FROM student AS st
INNER JOIN (
SELECT DISTINCT s_id, count(sc_score) AS count_sc, avg(sc_score) AS avg_sc FROM score GROUP BY s_id
) AS sc
ON st.s_id = sc.s_id
WHERE sc.count_sc >=2 AND sc.avg_sc < 60
;
SELECT st.s_id, st.s_name, sc.sc_score
FROM student AS st
RIGHT JOIN score AS sc
ON st.s_id=sc.s_id
WHERE sc.c_id='01' AND sc.sc_score < 60
ORDER BY sc.sc_score DESC;
SELECT st.* FROM
student AS st
LEFT JOIN (
SELECT s_id, sc_score FROM score WHERE c_id='01'
) AS sc
ON st.s_id = sc.s_id
WHERE sc.sc_score<60
ORDER BY sc.sc_score DESC
;
以如下形式显示:课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率
及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90
要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列
WITH RECURSIVE sc1(c_id, score, jige, zhongdeng, youliang,youxiu) AS (
SELECT c_id, sc_score,
CASE WHEN sc_score>=60 THEN 1 ELSE 0 END AS jige,
CASE WHEN sc_score>=70 THEN 1 ELSE 0 END AS zhongdeng,
CASE WHEN sc_score>=80 THEN 1 ELSE 0 END AS youliang,
CASE WHEN sc_score>=90 THEN 1 ELSE 0 END AS youxiu
FROM score
)
SELECT c_id,
count(score),
max(score) AS max_sc,
min(score) AS min_sc,
round(avg(score),2) AS avg_sc,
round(avg(jige)*100,2) AS per_jige,
round(avg(zhongdeng)*100,2) AS per_zhongdeng,
round(avg(youliang)*100,2) AS per_youliang,
round(avg(youxiu)*100,2) AS per_youxiug
FROM sc1
GROUP BY c_id
ORDER BY count(score) DESC, c_id
;
SELECT st.s_id, st.s_name, RANK() OVER(ORDER BY sc_score DESC) AS rank1, sc.sc_score
FROM (
SELECT s_id, sc_score
FROM score WHERE c_id='01'
) AS sc
INNER JOIN student AS st
ON sc.s_id=st.s_id
;
SELECT st.s_id, st.s_name, DENSE_RANK() OVER(ORDER BY sc_score DESC) AS rank1, sc.sc_score
FROM (
SELECT s_id, sc_score
FROM score WHERE c_id='01'
) AS sc
INNER JOIN student AS st
ON sc.s_id=st.s_id
;
SELECT st.s_id, st.s_name, RANK() OVER(ORDER BY sum_score DESC) AS rank1, sc.sum_score
FROM (
SELECT s_id, sum(sc_score) AS sum_score
FROM score GROUP BY s_id
) AS sc
INNER JOIN student AS st
ON sc.s_id=st.s_id;
SELECT st.s_id, st.s_name, DENSE_RANK() OVER(ORDER BY sum_score DESC) AS rank1, sc.sum_score
FROM (
SELECT s_id, sum(sc_score) AS sum_score
FROM score GROUP BY s_id
) AS sc
INNER JOIN student AS st
ON sc.s_id=st.s_id;
注:题干中分数段[100-85]用的是闭区间,会导致85、70和60被重复计算。
所以改为半开区间(100-85],(85-70],(70-60],(60-0]。
WITH RECURSIVE sc1(c_id, score) AS (
SELECT c_id, sc_score,
CASE WHEN sc_score< 60 THEN 1 ELSE 0 END AS scale1,
CASE WHEN sc_score>=60 AND sc_score<70 THEN 1 ELSE 0 END AS scale2,
CASE WHEN sc_score>=70 AND sc_score<85 THEN 1 ELSE 0 END AS scale3,
CASE WHEN sc_score>=85 THEN 1 ELSE 0 END AS scale4
FROM score
)
SELECT sc1.c_id, course.c_name,
sum(sc1.scale1) AS scale1,
sum(sc1.scale2) AS scale2,
sum(sc1.scale3) AS scale3,
sum(sc1.scale4) AS scale4,
round(avg(scale1)*100,2) AS per_scale1,
round(avg(scale2)*100,2) AS per_scale2,
round(avg(scale3)*100,2) AS per_scale3,
round(avg(scale4)*100,2) AS per_scale4
FROM sc1 INNER JOIN course ON sc1.c_id=course.c_id
GROUP BY sc1.c_id, course.c_name
;
SELECT * FROM (
SELECT *, RANK() OVER(PARTITION BY c_id order by sc_score DESC) AS rank1
FROM score) AS sc
WHERE rank1<=3
;
SELECT cr.c_name, count(sc.s_id)
FROM score AS sc RIGHT JOIN course AS cr
ON sc.c_id = cr.c_id
GROUP BY cr.c_name;
SELECT st.s_id, st.s_name
FROM student AS st
INNER JOIN (
SELECT s_id, count(c_id) AS count_c_id
FROM score
GROUP BY s_id) AS sc
ON st.s_id = sc.s_id
WHERE sc.count_c_id = 2
;
/*原博主的方法*/
SELECT s_id, s_name FROM student
WHERE s_id IN (
SELECT s_id FROM (
SELECT s_id, count(c_id) AS count_c_id FROM score GROUP BY s_id
) AS sc
WHERE count_c_id = 2
);
SELECT s_sex, count(s_id) FROM student GROUP BY s_sex;
/*使用了正则表达式*/
SELECT student.* FROM student WHERE s_name~'.*风.*';
SELECT student.* FROM student WHERE s_name LIKE '%风%';
WITH RECURSIVE st1 AS (
SELECT s_name, count(s_name) AS count_name FROM student GROUP BY s_name, s_sex
)
SELECT student.s_name FROM student LEFT JOIN st1 ON student.s_name = st1.s_name WHERE st1.count_name>1;
SELECT * FROM student WHERE EXTRACT(YEAR FROM s_age)=1990;
SELECT c_id, avg(sc_score) AS avgsc
FROM score
GROUP BY c_id
ORDER BY avgsc DESC, c_id
;
SELECT st.s_id, st.s_name, sc.avg_score
FROM student AS st INNER JOIN (SELECT s_id, avg(sc_score) AS avg_score FROM score GROUP BY s_id) AS sc
ON st.s_id = sc.s_id
WHERE sc.avg_score>=85;
SELECT s_id, sc_score
FROM score LEFT JOIN course
ON score.c_id = course.c_id
WHERE course.c_name='数学' AND score.sc_score<60;
SELECT st.s_name, sc2.c_name, sc2.sc_score FROM (
SELECT sc.s_id, course.c_name, sc.sc_score
FROM score AS sc LEFT JOIN course
ON sc.c_id = course.c_id)
AS sc2 RIGHT JOIN student AS st
ON sc2.s_id = st.s_id;
SELECT st.s_name, sc2.c_name, sc2.sc_score FROM (
SELECT sc.s_id, course.c_name, sc.sc_score
FROM score AS sc LEFT JOIN course
ON sc.c_id = course.c_id)
AS sc2 RIGHT JOIN student AS st
ON sc2.s_id = st.s_id
WHERE sc2.sc_score>=70;
SELECT st.s_name, sc2.c_name, sc2.sc_score FROM (
SELECT sc.s_id, course.c_name, sc.sc_score
FROM score AS sc LEFT JOIN course
ON sc.c_id = course.c_id)
AS sc2 RIGHT JOIN student AS st
ON sc2.s_id = st.s_id
WHERE sc2.sc_score<60;
SELECT st.s_id, st.s_name
FROM student AS st RIGHT JOIN score AS sc
ON st.s_id=sc.s_id
WHERE sc.c_id = '01' AND sc.sc_score>=80;
SELECT c_id, count(s_id) FROM score GROUP BY c_id;
SELECT st.*, sc.sc_score
FROM student AS st RIGHT JOIN score AS sc
ON st.s_id = sc.s_id
WHERE sc.c_id = (SELECT c_id
FROM course INNER JOIN teacher
ON course.t_id = teacher.t_id
WHERE teacher.t_name = '张三')
ORDER BY sc_score DESC
LIMIT 1;
成绩有重复,意味着成绩最高的可能是多个
SELECT st.*, sc.sc_score
FROM student AS st RIGHT JOIN score AS sc
ON st.s_id = sc.s_id
WHERE sc.sc_score = (SELECT max(sc2.sc_score)
FROM score AS sc2
WHERE c_id = (SELECT c_id
FROM course INNER JOIN teacher
ON course.t_id = teacher.t_id
WHERE teacher.t_name = '张三')
)
;
WITH RECURSIVE sc2 AS (
SELECT c_id, count(s_id) as count_s_id, sc_score
FROM score
GROUP BY c_id, sc_score)
SELECT sc.s_id, sc.c_id, sc.sc_score
FROM score AS sc INNER JOIN sc2
ON sc.sc_score = sc2.sc_score AND sc.c_id = sc2.c_id
WHERE sc2.count_s_id >1;
SELECT st.s_id, st.s_name, sc.c_id, sc.rank1
FROM student AS st
RIGHT JOIN (
SELECT s_id, c_id, ROW_NUMBER() over(PARTITION BY c_id order by sc_score DESC) AS rank1
FROM score) AS sc
ON st.s_id = sc.s_id
WHERE sc.rank1<3
ORDER BY sc.c_id, sc.rank1
;
SELECT c_id, count_s_id
FROM (
SELECT c_id, count(s_id) AS count_s_id
FROM score
GROUP BY c_id) AS sc
WHERE count_s_id>5;
SELECT s_id
FROM (
SELECT s_id, count(c_id) AS count_c_id
FROM score
GROUP BY s_id) AS sc
WHERE count_c_id>1;
SELECT s_id
FROM (
SELECT s_id, count(c_id) AS count_c_id
FROM score
GROUP BY s_id) AS sc
WHERE count_c_id=3;
SELECT s_id, EXTRACT(YEAR FROM NOW())-EXTRACT(YEAR FROM s_age) AS age
FROM student;
SELECT s_id, date_part('year',age(s_age)) AS age
FROM student;
SELECT s_id,s_name,s_age FROM (
SELECT s_id,s_name,s_age, EXTRACT(week FROM s_age) AS num_week
FROM student) AS st
WHERE num_week = EXTRACT(week FROM now());
SELECT s_id,s_name,s_age FROM (
SELECT s_id,s_name,s_age, EXTRACT(week FROM (s_age - interval '1 week')) AS num_week
FROM student) AS st
WHERE num_week = EXTRACT(week FROM now());
SELECT s_id,s_name,s_age FROM (
SELECT s_id,s_name,s_age, date_part('month', s_age) AS birth_month
FROM student) AS st
WHERE birth_month = EXTRACT(month FROM now());
SELECT s_id,s_name,s_age FROM (
SELECT s_id,s_name,s_age, EXTRACT(month FROM (s_age - interval '1 month')) AS num_month
FROM student) AS st
WHERE num_month = EXTRACT(month FROM now());`