MySQL查询实战6例及讲解

内容梗概

  本文将通过一组数据表及其数据查询案例,来讲解关于
(1)对表和字段使用别名
(2)联表查询
(3)聚合函数
(4)JOIN,
(5)HAVING
(6)INEXISTSNOT IN
(7)DISTINCT
(8)LIKE
...


数据准备

  为了更好的实战和理解本文内容,笔者准备了如下五张表,分别是学生表,班级表,课程表,教师表以及成绩表:

t_x_student 学生表
t_x_class 班级表
t_x_course 课程表
t_x_teacher 教师表
t_x_score 成绩表

数据库的模型设计

  模型设计遵循了三范式及设计原则,如下:

设计模型

案例需求

1、查询所有的课程的名称以及对应的任课老师姓名
2、查询平均成绩大于八十分的同学的姓名和平均成绩
3、查询没有报王佩佩老师课的学生姓名
4、查询选修自然课程和社会课程其中一门的学生姓名
5、查询挂科超过两门(包括两门)的学生姓名和班级
6、查询同时选了王佩佩老师所有课的学生班级和姓名


案例解答

(1)查询所有的课程的名称以及对应的任课老师姓名

  分析:我们需要用到t_x_courset_x_teacher表:既需要得到课程名称又要拿到老师姓名,然后看表结构模型,我们可以知道t_x_course有外键字段teacher_id指向t_x_teachert_id,那么我们就可以用内连接inner join将两张表拼接起来然后取其字段t_x_course.c_namet_x_teacher.t_name即可得到我们想要的数据,SQL语句如下:

SELECT
    cor.c_name AS course,
  tea.t_name AS teacher
FROM
  t_x_course cor
LEFT JOIN
    t_x_teacher tea
ON cor.teacher_id = tea.t_id;

  也可以直接进行多表查询

SELECT
    cor.c_name AS course,
    tea.t_name AS teacher
FROM
    t_x_course cor,
    t_x_teacher tea
WHERE
cor.teacher_id = tea.t_id;

知识点:
使用别名

使用别名是为了简化SQL或者语义化表名和字段名,本例中对查询结果使用了AScor.c_name创建了别名course,同样对标t_x_course使用空格创建了别名 cor,两种方式在使用上没有区别。

联表查询

联表查询 有三种类别(1)INNER JOIN:表示两个表同时存在数据时返回该记录;(2)LEFT JOIN:左表存在数据即返回该记录,即便在ON条件下,右表无数据;(3)RIGHT JOIN:右表存在即返回该记录

(2)查询平均成绩大于八十分的同学的姓名和平均成绩

  分析:需要用到t_x_score表和t_x_shtudent表,既要拿到学生姓名又要拿到成绩,我们理所当然需要将这两个表联表或者做子连接,然后需求中需要用到平均数,那么我们应想到用聚合函数avg(),但使用聚合函数的前提是分组即需要使用到GROUP BY
  首先在联表或子连接前可以通过t_x_score表分组得到student_id平均成绩

SELECT
    student_id,
    avg(score) as avg_score
FROM
    t_x_score
GROUP BY
    student_id
HAVING AVG(score) > 80;

  然后在以上虚拟表的基础上通过student_id拼接student表,取student.snameavg_score即可

SELECT
  stu.s_name AS student,
    t_avg_score.avg_score
FROM
  t_x_student stu
INNER JOIN 
    (
        SELECT
            student_id,
            avg(score) AS avg_score
        FROM
            t_x_score
        GROUP BY student_id
        HAVING AVG(score) > 80
    ) AS t_avg_score
ON
    t_avg_score.student_id = stu.s_id
ORDER BY
    avg_score DESC;

或直接使用多表查询如下:

SELECT
    stu.s_name AS student,
    t_avg_score.avg_score
FROM
    t_x_student stu,
    (
        SELECT 
            student_id,
            AVG(score) AS avg_score
        FROM
            t_x_score 
        GROUP BY student_id
        HAVING AVG(score) > 80
    ) t_avg_score
WHERE
    t_avg_score.student_id = stu.s_id
ORDER BY
    avg_score DESC;

知识点:
聚合函数

聚合函数(aggregate function)会对一组值执行计算并返回计算结果单个值。 所有聚合函数都是确定性的。
select 列表或 SELECT 语句的 HAVING 子句中允许使用它们。 可以将聚合与 GROUP BY 子句结合使用,来计算行类别的聚合。 使用 OVER 子句来计算特定范围内的值的聚合。
常见的聚合函数:
(1)求个数/记录数/项目数等:count( )包括空值/count(*)不包括空值;
(2)求某一列平均数 :avg();注意null行会被忽略,也可以使用isNull(score, 0)null行转换成0
(3)求总和,总分等:sum();必须为数字列
(4)求最大值,最高分,最高工资等:max()
(5)求最小值,最低分,最低工资等:min()
(6)求指定表达式中所有值的方差:var()

HAVINGWHERE

这两者都是对表中的数据进行过滤筛选,不同的是:
HAVING子句可以让我们在聚合后对组记录进行筛选。我们知道聚合函数可以将一组值进行计算后返回计算结果的单个值,而这个值并不是原表中的某个字段,只是一个虚拟的计算结果。当然HAVING也是允许用来过滤真实字段。
WHERE子句只能过滤表中存在的字段名称,当用来过滤虚拟字段时就会被提示错误。

(3)查询没有报王佩佩老师课的学生姓名

  分析:根据表结构我们发现并没有存在一个直接关联老师和学生的表,但是t_x_score表中标注了哪个同学某个科目的考试成绩,而每个课程有对应的授课教师,这样假设每个学生都参加了考试的情况下,我们就可以得到学生和老师的对应关系。也就是说,我们需要用到t_x_studentt_x_scoret_x_courset_x_teacher这4张表,直接得到没有报王佩佩老师课程的学生比较困难,那么我们就反过来想,哪些是报了王佩佩老师课程的,然后在学生表里剔除掉即可
  先找到王佩佩老师教了哪些课程

SELECT
    cor.c_id AS course_id,
    cor.c_name AS course_name,
    t_teacher.t_name AS teacher
FROM
    t_x_course cor
INNER JOIN
    (
        SELECT
            t_name,
            t_id
        FROM
            t_x_teacher
        WHERE
            t_name = '王佩佩'
    ) AS t_teacher
ON
    cor.teacher_id = t_teacher.t_id;

  本例中发现王佩佩老师只教授了一门“社会”课,如果是多门课,需要使用IN关键字查找,分析过程简化,找到选修21005课程的学生列表:

SELECT
    student_id,
    course_id
FROM
    t_x_score
WHERE
    course_id = '21005';


  这样,我们就得到了选修了王佩佩老师课程的学生id列表。然后在学生表中使用NOT IN做反向剔除即可。结合以上查找逻辑,有了完整的语句如下:

SELECT 
    stu.s_name AS student
FROM
    t_x_student stu
WHERE
    s_id NOT IN
    (
        SELECT
            student_id
        FROM
            t_x_score
        WHERE
            course_id IN
            (
                SELECT
                    cor.c_id
                FROM
                    t_x_course cor
                INNER JOIN
                (
                    SELECT
                        t_id
                    FROM
                        t_x_teacher
                    WHERE
                        t_name = '王佩佩'
                ) AS t_teacher
                ON
                    cor.teacher_id = t_teacher.t_id
            )
    );

知识点
INNOT IN

IN 操作符允许我们在 WHERE 子句中规定多个值,相当于多个 or 条件的叠加,比较好理解。in查询的子条件返回结果必须只有一个字段,因为它要从这个结果中进行合并查询。
SELECT col FROM table1 WHERE col_name IN (value1, value2, ...)
NOT IN 取的是 IN的对立面

EXISTSNOT EXISTS

EXISTS 运算符用于判断查询子句是否有记录,如果有一条或多条记录存在返回 True,否则返回 False
 它对外表用 loop 逐条查询,每次查询都会查看 exists 的条件语句。当EXISTS查询子语句的条件语句能够返回记录行时(无论返回多少记录行,只要能返回),条件就为真,就返回当前 loop 到的这条记录,反之如果条件语句不能返回记录行,则当前 loop 到的这条记录被丢弃。就像一个 bool 条件,当能返回结果集则为 true,不能返回结果集则为 false
当子查询为 select NULL 时, mysql 仍然认为它是 True
NOT EXISTSEXISTS的对立面

INEXISTS区别

in 语句:确定给定的值是否与子查询或列表中的值相匹配
exists 语句:执行n次(外表行数),指定一个子查询,检测行的存在,遍历循环外表,检查外表中的记录有没有和内表的的数据一致的,匹配得上就放入结果集
使用上:外层查询表小于子查询表,则用 exists,外层查询表大于子查询表,则用 in。但是无论哪个表大,not existsnot in 效率高

(4)查询选修自然课程和社会课程其中一门的学生姓名

  分析:没有选修课程与学生表之间的直接关系,但是成绩表中有对应关系,因此,需要使用到学生表,课程表,和成绩表
(1)因为成绩表中只有课程id,因此找到课程表中的'自然', '社会'的课程id

SELECT
  c_id
FROM
  t_x_course
WHERE
  c_name IN ('自然', '社会')    

(2)在成绩表中找到有该课程id的学生id

SELECT
  student_id
FROM
  t_x_score
WHERE
  course_id IN ('21004', '21005')

(3)选出只报了一门的课程的学生姓名,即 count(student_id)值为 1 的学生
(4)在学生表中找到这些学生id的学生姓名

SELECT
    s_name AS student
FROM
    t_x_student
WHERE
    s_id IN
    (
        SELECT
            student_id
        FROM
            t_x_score
        WHERE
            course_id IN
            (
                SELECT
                    c_id
                FROM
                    t_x_course
                WHERE
                    c_name IN ('自然', '社会')                  
            )
        GROUP BY
            student_id
        HAVING
            COUNT(student_id) = 1
    )

(5) 查询挂科超过两门(包括两门)的学生姓名和班级

  分析:本需求和第4个类似。我们需要在成绩表中拿到挂科超过两门的学生id,然后根据学生id在学生表中查出学生姓名,再根据学生的班级id查出班级名称,即,需要使用到学生表,班级表,成绩表

SELECT
    cls.caption,
    k.s_name AS student
FROM
    t_x_class cls
    INNER JOIN
    (
        SELECT
            s_name,
            class_id
        FROM
            t_x_student stu
            INNER JOIN
            (
                SELECT student_id FROM t_x_score WHERE score < 60 GROUP BY student_id HAVING COUNT(student_id) > 1
            ) sco
            ON
                sco.student_id = stu.s_id
    ) k
    ON
        k.class_id = cls.c_id;

(6) 查询同时选了王佩佩老师所有课的学生班级和姓名

  分析:根据需求,需要先在教师表中找到王佩佩老师的教师id,再去课程表找到王佩佩老师教授的所有课程,然后根据这些课程id在成绩表中查询出学生的id,再通过这些学生的id在学生表中找到这些学生的姓名和学生所在的班级id,最后找到班级id找到班级名称。也就是说,本需求5个表都需要使用到。

  1. 找到王佩佩老师的教师id
SELECT
    t_id
FROM
    t_x_teacher
WHERE
    t_name = '王佩佩';
  1. 根据教师id查到王佩佩老师教授了哪些课程
SELECT
    c_id
FROM
    t_x_course cos
    INNER JOIN
    ( SELECT t_id FROM t_x_teacher WHERE t_name = '王佩佩' ) tea
    ON tea.t_id = cos.teacher_id;
  1. 在成绩表中找到选修了这些课程id的学生的学生id
SELECT
    sco.student_id
FROM
    t_x_score sco
WHERE
    sco.course_id IN
    (
        SELECT
            c_id
        FROM
            t_x_course cos
        INNER JOIN
        ( SELECT t_id FROM t_x_teacher WHERE t_name = '王佩佩' ) tea
        ON tea.t_id = cos.teacher_id
    );
  1. 在学生表中找到这些学生id的学生姓名以及班级id
SELECT
    stu.s_name AS student,
    stu.class_id
FROM
    t_x_student stu
    INNER JOIN
    (
        SELECT
            sco.student_id
        FROM
            t_x_score sco
        WHERE
            sco.course_id IN
            (
                SELECT
                    c_id
                FROM
                    t_x_course cos
                INNER JOIN
                ( SELECT t_id FROM t_x_teacher WHERE t_name = '王佩佩' ) tea
                ON tea.t_id = cos.teacher_id
            )
    ) k
    ON
        k.student_id = stu.s_id;
  1. 将表格中的班级id替换成班级名称
SELECT
    s.s_name AS student_id,
    clas.caption
FROM
    t_x_class clas
    INNER JOIN
    (
        SELECT
            stu.s_name,
            stu.class_id
        FROM
            t_x_student stu
            INNER JOIN
            (
                SELECT
                    sco.student_id
                FROM
                    t_x_score sco
                WHERE
                    sco.course_id IN
                    (
                        SELECT
                            c_id
                        FROM
                            t_x_course cos
                        INNER JOIN
                        ( SELECT t_id FROM t_x_teacher WHERE t_name = '王佩佩' ) tea
                        ON tea.t_id = cos.teacher_id
                    )
            ) k
            ON
                k.student_id = stu.s_id
    ) s
    ON
        s.class_id = clas.c_id;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352