常用的SQL查询思维/场景
前言
现在大多数开发工作中,已经可以使用一些组件或框架提供的强大的条件构造器来完成查询数据了,虽然强大而且方便,但也还是存在很多业务场景需要实打实的编写传统SQL语句。特别一些测试、维护、问题排查的时候,甚至有的时候系统没有功能,但是又需要某些数据报表,无疑得直接上SQL查询处理。
作为一名开发人员,即使有了便捷的工具可以在开发中直接使用,但是该有的知识技能不应该被遗忘,有的知识技能平时用的不多,但是往往能解决某些核心的重要的问题,这估计就是大多企业招聘面试谈造飞机,实际工作拧螺丝的原因吧,也就是虽然有的东西用的不多,但是必须得备得有,以备关键的时候能处理问题。
快捷工具用多了,基本的底层是会忘记的,今天就来回顾一些常见的查询业务,主要是要理解其中的思维方式。
环境
用于测试的表结构和数据:
DROP TABLE IF EXISTS `course`; CREATE TABLE `course` ( `cid` int(0) NULL DEFAULT NULL, `cname` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `tid` int(0) NULL DEFAULT NULL ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of course -- ---------------------------- INSERT INTO `course` VALUES (1, '语文', 1); INSERT INTO `course` VALUES (2, '数学', 2); INSERT INTO `course` VALUES (3, '英语', 3); INSERT INTO `course` VALUES (4, '物理', 4); -- ---------------------------- -- Table structure for sc -- ---------------------------- DROP TABLE IF EXISTS `sc`; CREATE TABLE `sc` ( `sid` int(0) NULL DEFAULT NULL, `cid` int(0) NULL DEFAULT NULL, `score` int(0) NULL DEFAULT NULL ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of sc -- ---------------------------- INSERT INTO `sc` VALUES (1, 1, 56); INSERT INTO `sc` VALUES (1, 2, 78); INSERT INTO `sc` VALUES (1, 3, 67); INSERT INTO `sc` VALUES (1, 4, 58); INSERT INTO `sc` VALUES (2, 1, 79); INSERT INTO `sc` VALUES (2, 2, 81); INSERT INTO `sc` VALUES (2, 3, 92); INSERT INTO `sc` VALUES (2, 4, 68); INSERT INTO `sc` VALUES (3, 1, 91); INSERT INTO `sc` VALUES (3, 2, 47); INSERT INTO `sc` VALUES (3, 3, 88); INSERT INTO `sc` VALUES (3, 4, 56); INSERT INTO `sc` VALUES (4, 2, 88); INSERT INTO `sc` VALUES (4, 3, 90); INSERT INTO `sc` VALUES (4, 4, 93); INSERT INTO `sc` VALUES (5, 1, 46); INSERT INTO `sc` VALUES (5, 3, 78); INSERT INTO `sc` VALUES (5, 4, 53); INSERT INTO `sc` VALUES (6, 1, 35); INSERT INTO `sc` VALUES (6, 2, 68); INSERT INTO `sc` VALUES (6, 4, 71); -- ---------------------------- -- Table structure for student -- ---------------------------- DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `sid` int(0) NULL DEFAULT NULL, `sname` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `sage` int(0) NULL DEFAULT NULL, `ssex` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of student -- ---------------------------- INSERT INTO `student` VALUES (1, '刘一', 18, '男'); INSERT INTO `student` VALUES (2, '钱二', 19, '女'); INSERT INTO `student` VALUES (3, '张三', 17, '男'); INSERT INTO `student` VALUES (4, '李四', 18, '女'); INSERT INTO `student` VALUES (5, '王五', 17, '男'); INSERT INTO `student` VALUES (6, '赵六', 19, '女'); -- ---------------------------- -- Table structure for teacher -- ---------------------------- DROP TABLE IF EXISTS `teacher`; CREATE TABLE `teacher` ( `tid` int(0) NULL DEFAULT NULL, `tname` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of teacher -- ---------------------------- INSERT INTO `teacher` VALUES (1, '叶平'); INSERT INTO `teacher` VALUES (2, '贺高'); INSERT INTO `teacher` VALUES (3, '杨艳'); INSERT INTO `teacher` VALUES (4, '周磊');
预览
查询
--- 查询“001”课程比“002”课程成绩高的所有学生的学号 SELECT t1.sid FROM (select sid,score from SC where Cid='001') t1, (select sid,score from SC where Cid='002') t2 where t1.sid = t2.sid and t1.score > t2.score
-- 查询平均成绩大于60分的同学的学号和平均成绩 SELECT sid, AVG(score) FROM sc GROUP BY sid HAVING AVG(score) > 60
-- 查询所有同学的学号、姓名、选课数、总成绩; SELECT st.sid,st.sname,count(sc.cid) as '选课数' ,SUM(sc.score) as '总成绩' from student st,sc where st.sid = sc.sid GROUP BY st.sid,st.sname; -- 或 SELECT st.sid,st.sname,count(sc.cid) as '选课数' ,SUM(sc.score) as '总成绩' from student st left join sc on st.sid = sc.sid GROUP BY st.sid,st.sname
-- 查询没学过“叶平”老师课的同学的学号、姓名 select Student.Sid,Student.Sname from Student where Sid not in( SELECT sc.sid FROM sc GROUP BY sc.sid,sc.cid HAVING sc.cid in( SELECT c.cid FROM course c,teacher t where c.tid = t.tid AND t.tname = '叶平' ) ) -- 或 select Student.Sid,Student.Sname from Student where Sid not in ( select distinct SC.Sid from SC,Course,Teacher where SC.Cid=Course.Cid and Teacher.Tid=Course.Tid and Teacher.Tname='叶平');
-- 查询学过“001”并且也学过编号“002”课程的同学的学号、姓名 SELECT student.sid,student.sname FROM student WHERE student.sid in( SELECT a.sid FROM (SELECT * from sc WHERE sc.cid = '001') a, (SELECT * from sc WHERE sc.cid = '002') b where a.sid = b.sid ) -- 或 select Student.Sid,Student.Sname from Student,SC where Student.Sid=SC.Sid and SC.Cid='001' and exists( Select * from SC as SC_2 where SC_2.Sid=SC.Sid and SC_2.Cid='002');
-- 查询学过“叶平”老师所教的所有课的同学的学号、姓名 SELECT st.sid,st.sname FROM student st WHERE st.sid in ( SELECT sc.sid FROM course c,teacher t,sc where c.cid = sc.cid and t.tid = c.tid and t.tname = '叶平' )
-- 查询课程编号“002”的成绩比课程编号“001”课程低的所有同学的学号、姓名 SELECT st.sid,st.sname FROM student st where st.sid in( SELECT a.sid FROM ( SELECT * from sc where sc.cid = '001' ) a, ( SELECT * from sc where sc.cid = '002' ) b WHERE a.sid = b.sid AND b.score < a.score )
-- 查询所有课程成绩小于60分的同学的学号、姓名 select Sid,Sname from Student where Sid not in ( select S.Sid from Student AS S,SC where S.Sid=SC.Sid and score>60 );
-- 查询没有学全所有课的同学的学号、姓名SELECT st.sid,st.sname
from student st where st.sid in ( SELECT sc.sid FROM sc GROUP BY sc.sid HAVING count(sc.cid) < (SELECT count(c.cid) FROM course c) ) -- 或 select Student.Sid,Student.Sname from Student,SC where Student.Sid=SC.Sid group by Student.Sid,Student.Sname having count(Cid) < (select count(Cid) from Course);
-- 查询至少有一门课与学号为“001”的同学所学相同的同学的学号和姓名; SELECT st.sid,st.sname FROM student st left join sc on sc.cid in ( SELECT sc.cid FROM sc where sc.sid = '001') GROUP BY st.sid,st.sname
-- “叶平”老师教的课的平均成绩;
SELECT avg(sc.score) FROM sc where cid = ( SELECT course.cid from course where course.tid in( SELECT teacher.tid from teacher where teacher.tname = '叶平' ) )
-- 查询和“002”号的同学学习的课程完全相同的其他同学学号和姓名
SELECT st.sid,st.sname from student st WHERE st.sid in( SELECT sc.sid from sc where sc.cid in ( SELECT sc.cid from sc WHERE sc.sid = '002') GROUP BY sc.sid HAVING count(sc.cid) = ( SELECT count(sc2.cid) from sc sc2 WHERE sc2.sid = '002') )
-- 平均成绩从高到低显示所有学生的“语文”、“数学”、“英语”三门的课程成绩,按如下形式显示: 学生ID,,数据库,企业管理,英语,有效课程数,有效平均分 SELECT Sid as 学生ID ,(SELECT score FROM SC WHERE SC.Sid=t.Sid AND Cid='001') AS '语文' ,(SELECT score FROM SC WHERE SC.Sid=t.Sid AND Cid='002') AS '数学' ,(SELECT score FROM SC WHERE SC.Sid=t.Sid AND Cid='003') AS '英语' ,COUNT(*) AS 有效课程数, AVG(t.score) AS 平均成绩 FROM SC AS t GROUP BY Sid ORDER BY sid
形式 :竖变横
-- 查询各科成绩最高和最低的分:以如下形式显示:课程ID,最高分,最低分
SELECT sc.cid,max(sc.score) as '最高分',min(sc.score) as '最低分' from sc GROUP BY sc.cid ORDER BY sc.cid
-- 查询各科成绩最高和最低的分:以如下形式显示:课程号,课程名,最高分,最低分 SELECT t.cid as '课程号',cname as '课程名',maxsc as '最高分', minsc as '最低分' from course c RIGHT JOIN ( SELECT sc.cid,max(sc.score) as maxsc, min(sc.score) as minsc from sc GROUP BY sc.cid ) t ON t.cid = c.cid
结语
查询效率:达到一个查询目标,可以有不同的查询方法,当然不同的查询方法也就往往意味着SQL执行的效率不同,在数据量小的时候可能赶紧不明显,但是数据量大的时候就明显了,这意味着我们必须尽量选择高效率的查询方法。
拆解思维:一个复杂的SQL,可以拆解成多个子模块来理解处理。
逆向思维:有时候反着条件查能或者更好的效果,比如上面的查询例子里面的“所有课程成绩都小于60分的同学”时,先把“存在60分以上的课程的同学”这个“补集”查处理,再用not in就可以便捷的达到查询目的。