MySQL第四章、表的增删查改(进阶)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 在实际应用中,为了合并多个select的执行结果,可以使用集合操作符 union,union all。GROUP BY 子句进行分组以后,需要对分组结果再进行条件过滤时,不能使用 WHERE 语句,而需要用HAVING:显示平均工资低于1500的角色和它的平均工资。需要满足:使用 GROUP BY 进行分组查询时,SELECT 指定的字段必须是“分组依据字段”,其他字段若想出现在SELECT 中则必须包含在聚合函数中。案例:查询所有同学的成绩,及同学的个人信息,如果该同学没有成绩,也需要显示。

 目录

一、数据库约束

1.1约束类型

1.2NULL约束

1.3 UNIQUE:唯一约束

1.4 DEFAULT:默认值约束

1.5 PRIMARY KEY:主键约束

1.6 FOREIGN KEY:外键约束

1.7 CHECK约束(了解)

二、表的设计

2.1 一对一

2.2 一对多

2.3 多对多

三、 新增

四、查询

4.1 聚合查询

4.1.1 聚合函数

4.1.2 GROUP BY子句

4.1.3 HAVING

4.2 联合查询

4.2.1 内连接

4.2.2 外连接

4.2.3 自连接

4.2.4 子查询

4.2.5 合并查询


一、数据库约束

1.1约束类型

    • NOT NULL - 指示某列不能存储 NULL 值。
    • UNIQUE - 保证某列的每行必须有唯一的值。
    • DEFAULT - 规定没有给列赋值时的默认值。
    • PRIMARY KEY - NOT NULL 和 UNIQUE 的结合。确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。
    • FOREIGN KEY - 保证一个表中的数据匹配另一个表中的值的参照完整性。
    • CHECK - 保证列中的值符合指定的条件。对于MySQL数据库,对CHECK子句进行分析,但是忽略CHECK子句。

    image.gif编辑

    1.2NULL约束

    创建表时,可以指定某列不为空:

    -- 重新设置学生表结构
        DROP TABLE IF EXISTS student;
        CREATE TABLE student (
        id INT NOT NULL,
        sn INT,
        name VARCHAR(20),
        qq_mail VARCHAR(20)
        );

    image.gif

    image.gif编辑

    1.3 UNIQUE:唯一约束

    指定sn列为唯一的、不重复的:

    -- 重新设置学生表结构
    DROP TABLE IF EXISTS student;
    CREATE TABLE student (
        id INT NOT NULL,
        sn INT UNIQUE,
        name VARCHAR(20),
        qq_mail VARCHAR(20)
        );

    image.gif

    image.gif编辑

    1.4 DEFAULT:默认值约束

    指定插入数据时,name列为空,默认值unkown:

    -- 重新设置学生表结构
    DROP TABLE IF EXISTS student;
    CREATE TABLE student (
        id INT NOT NULL,
        sn INT UNIQUE,
        name VARCHAR(20) DEFAULT 'unkown',
        qq_mail VARCHAR(20)
    );

    image.gif

    image.gif编辑

    1.5 PRIMARY KEY:主键约束

    指定id列为主键:

    -- 重新设置学生表结构
    DROP TABLE IF EXISTS student;
    CREATE TABLE student (
        id INT NOT NULL PRIMARY KEY,
        sn INT UNIQUE,
        name VARCHAR(20) DEFAULT 'unkown',
        qq_mail VARCHAR(20)
    );

    image.gif

    image.gif编辑

    对于整数类型的主键,常配搭自增长auto_increment来使用。插入数据对应字段不给值时,使用最大值+1。

    -- 主键是 NOT NULL 和 UNIQUE 的结合,可以不用 NOT NULL
    id INT PRIMARY KEY auto_increment,

    image.gif

    1.6 FOREIGN KEY:外键约束

    外键用于关联其他表的主键或唯一键,语法:

    foreign key (字段名) references 主表(列)

    image.gif

    案例:

      • 创建班级表classes,id为主键:
      -- 创建班级表,有使用MySQL关键字作为字段时,需要使用``来标识
      DROP TABLE IF EXISTS classes;
      CREATE TABLE classes (
          id INT PRIMARY KEY auto_increment,
          name VARCHAR(20),
          `desc` VARCHAR(100)
      );

      image.gif

        • 创建学生表student,一个学生对应一个班级,一个班级对应多个学生。使用id为主键,classes_id为外键,关联班级表id
        -- 重新设置学生表结构
        DROP TABLE IF EXISTS student;
        CREATE TABLE student (
            id INT PRIMARY KEY auto_increment,
            sn INT UNIQUE,
            name VARCHAR(20) DEFAULT 'unkown',
            qq_mail VARCHAR(20),
            classes_id int,
            FOREIGN KEY (classes_id) REFERENCES classes(id)
        );

        image.gif

        image.gif编辑

        image.gif编辑

        1.7 CHECK约束(了解)

        MySQL使用时不报错,但忽略该约束:

        drop table if exists test_user;
        create table test_user (
            id int,
            name varchar(20),
            sex varchar(1),
            check (sex ='男' or sex='女')
        );

        image.gif


        二、表的设计

        三大范式:

        2.1 一对一

        image.gif编辑

        image.gif编辑  

        2.2 一对多

        image.gif编辑

        image.gif编辑

        2.3 多对多

        image.gif编辑image.gif编辑

        -- 创建课程表
        DROP TABLE IF EXISTS course;
        CREATE TABLE course (
            id INT PRIMARY KEY auto_increment,
            name VARCHAR(20)
        );
        image.gif
        -- 创建课程学生中间表:考试成绩表
        DROP TABLE IF EXISTS score;
        CREATE TABLE score (
            id INT PRIMARY KEY auto_increment,
            score DECIMAL(3, 1),
            student_id int,
            course_id int,
            FOREIGN KEY (student_id) REFERENCES student(id),
            FOREIGN KEY (course_id) REFERENCES course(id)
        );
        image.gif

        三、 新增

        插入查询结果

        INSERT INTO table_name [(column [, column ...])] SELECT ...

        image.gif

        案例:创建一张用户表,设计有name姓名、email邮箱、sex性别、mobile手机号字段。需要把已有的学生数据复制进来,可以复制的字段为name、qq_mail

        -- 创建用户表
        DROP TABLE IF EXISTS test_user;
        CREATE TABLE test_user (
            id INT primary key auto_increment,
            name VARCHAR(20) comment '姓名',
            age INT comment '年龄',
            email VARCHAR(20) comment '邮箱',
            sex varchar(1) comment '性别',
            mobile varchar(20) comment '手机号'
        );
        -- 将学生表中的所有数据复制到用户表
        insert into test_user(name, email) select name, qq_mail from student;

        image.gif

        image.gif编辑


        四、查询

        4.1 聚合查询

        4.1.1 聚合函数

        常见的统计总数、计算平均值等操作,可以使用聚合函数来实现,常见的聚合函数有:

        image.gif编辑

        案例:

        -- 统计班级共有多少同学
        SELECT COUNT(*) FROM student;
        SELECT COUNT(0) FROM student;
        -- 统计班级收集的 qq_mail 有多少个,qq_mail 为 NULL 的数据不会计入结果
        SELECT COUNT(qq_mail) FROM student;
        -- 统计数学成绩总分
        SELECT SUM(math) FROM exam_result;
        -- 不及格 < 60 的总分,没有结果,返回 NULL
        SELECT SUM(math) FROM exam_result WHERE math < 60;
        -- 统计平均总分
        SELECT AVG(chinese + math + english) 平均总分 FROM exam_result;
        -- 返回英语最高分
        SELECT MAX(english) FROM exam_result;
        -- 返回 > 70 分以上的数学最低分
        SELECT MIN(math) FROM exam_result WHERE math > 70;

        image.gif

        4.1.2 GROUP BY子句

        SELECT 中使用 GROUP BY 子句可以对指定列进行分组查询。需要满足:使用 GROUP BY 进行分组查询时,SELECT 指定的字段必须是“分组依据字段”,其他字段若想出现在SELECT 中则必须包含在聚合函数中。

        select column1, sum(column2), .. from table group by column1,column3;

        image.gif

        案例:准备测试表及数据:职员表,有id(主键)、name(姓名)、role(角色)、salary(薪水)

        create table emp(
            id int primary key auto_increment,
            name varchar(20) not null,
            role varchar(20) not null,
            salary numeric(11,2)
        );
        insert into emp(name, role, salary) values
        ('马云','服务员', 1000.20),
        ('马化腾','游戏陪玩', 2000.99),
        ('孙悟空','游戏角色', 999.11),
        ('猪无能','游戏角色', 333.5),
        ('沙和尚','游戏角色', 700.33),
        ('隔壁老王','董事长', 12000.66);

        image.gif

        查询每个角色的最高工资、最低工资和平均工资

        select role,max(salary),min(salary),avg(salary) from emp group by role;

        image.gif

        4.1.3 HAVING

        GROUP BY 子句进行分组以后,需要对分组结果再进行条件过滤时,不能使用 WHERE 语句,而需要用HAVING:显示平均工资低于1500的角色和它的平均工资

        select role,max(salary),min(salary),avg(salary) from emp group by role
        having avg(salary)<1500;

        image.gif

        4.2 联合查询

        实际开发中往往数据来自不同的表,所以需要多表联合查询。多表查询是对多张表的数据取笛卡尔积:

        image.gif编辑

        image.gif编辑

        注意:关联查询可以对关联表使用别名。

        image.gif编辑

        初始化测试数据:
        insert into classes(name, `desc`) values
        ('计算机系2019级1班', '学习了计算机原理、C和Java语言、数据结构和算法'),
        ('中文系2019级3班','学习了中国传统文学'),
        ('自动化2019级5班','学习了机械自动化');
        insert into student(sn, name, qq_mail, classes_id) values
        ('09982','黑旋风李逵','xuanfeng@qq.com',1),
        ('00835','菩提老祖',null,1),
        ('00391','白素贞',null,1),
        ('00031','许仙','xuxian@qq.com',1),
        ('00054','不想毕业',null,1),
        ('51234','好好说话','say@qq.com',2),
        ('83223','tellme',null,2),
        ('09527','老外学中文','foreigner@qq.com',2);
        insert into course(name) values
        ('Java'),('中国传统文化'),('计算机原理'),('语文'),('高阶数学'),('英文');
        insert into score(score, student_id, course_id) values
        -- 黑旋风李逵
        (70.5, 1, 1),(98.5, 1, 3),(33, 1, 5),(98, 1, 6),
        -- 菩提老祖
        (60, 2, 1),(59.5, 2, 5),
        -- 白素贞
        (33, 3, 1),(68, 3, 3),(99, 3, 5),
        -- 许仙
        (67, 4, 1),(23, 4, 3),(56, 4, 5),(72, 4, 6),
        -- 不想毕业
        (81, 5, 1),(37, 5, 5),
        -- 好好说话
        (56, 6, 2),(43, 6, 4),(79, 6, 6),
        -- tellme
        (80, 7, 2),(92, 7, 6);

        image.gif

        4.2.1 内连接

        select 字段 from 表1 别名1 [inner] join 表2 别名2 on 连接条件 and 其他条件;
        select 字段 from 表1 别名1,表2 别名2 where 连接条件 and 其他条件;

        image.gif

        案例:

        (1)查询“许仙”同学的 成绩

        select sco.score from student stu inner join score sco on stu.id=sco.student_id
        and stu.name='许仙';
        -- 或者
        select sco.score from student stu, score sco where stu.id=sco.student_id and
        stu.name='许仙';

        image.gif

        image.gif编辑

        (2)查询所有同学的总成绩,及同学的个人信息:

        -- 成绩表对学生表是多对1关系,查询总成绩是根据成绩表的同学id来进行分组的
        SELECT
            stu.sn,
            stu.NAME,
            stu.qq_mail,
            sum( sco.score )
        FROM
            student stu
            JOIN score sco ON stu.id = sco.student_id
        GROUP BY
            sco.student_id;

        image.gif

        image.gif编辑

        (3)查询所有同学的成绩,及同学的个人信息:

        image.gif编辑

        -- 查询出来的都是有成绩的同学,“老外学中文”同学 没有显示
        select * from student stu join score sco on stu.id=sco.student_id;
        -- 学生表、成绩表、课程表3张表关联查询
        SELECT
            stu.id,
            stu.sn,
            stu.NAME,
            stu.qq_mail,
            sco.score,
            sco.course_id,
            cou.NAME
        FROM
            student stu
            JOIN score sco ON stu.id = sco.student_id
            JOIN course cou ON sco.course_id = cou.id
        ORDER BY
            stu.id;

        image.gif

        4.2.2 外连接

        外连接分为左外连接和右外连接。如果联合查询,左侧的表完全显示我们就说是左外连接;右侧的表完全显示我们就说是右外连接。

        image.gif编辑

        语法:

        -- 左外连接,表1完全显示
        select 字段名 from 表名1 left join 表名2 on 连接条件;
        -- 右外连接,表2完全显示
        select 字段 from 表名1 right join 表名2 on 连接条件;

        image.gif

        案例:查询所有同学的成绩,及同学的个人信息,如果该同学没有成绩,也需要显示

        -- “老外学中文”同学 没有考试成绩,也显示出来了
        select * from student stu left join score sco on stu.id=sco.student_id;
        -- 对应的右外连接为:
        select * from score sco right join student stu on stu.id=sco.student_id;
        -- 学生表、成绩表、课程表3张表关联查询
        SELECT
            stu.id,
            stu.sn,
            stu.NAME,
            stu.qq_mail,
            sco.score,
            sco.course_id,
            cou.NAME
        FROM
            student stu
            LEFT JOIN score sco ON stu.id = sco.student_id
            LEFT JOIN course cou ON sco.course_id = cou.id
        ORDER BY
            stu.id;

        image.gif

        image.gif编辑

        三张表的代码:

        image.gif编辑

        4.2.3 自连接

        自连接是指在同一张表连接自身进行查询。

        案例:显示所有“计算机原理”成绩比“Java”成绩高的成绩信息

        image.gif编辑

        -- 先查询“计算机原理”和“Java”课程的id
        select id,name from course where name='Java' or name='计算机原理';
        -- 再查询成绩表中,“计算机原理”成绩比“Java”成绩 好的信息
        SELECT
            s1.*
        FROM
            score s1,
            score s2
        WHERE
            s1.student_id = s2.student_id
            AND s1.score < s2.score
            AND s1.course_id = 1
            AND s2.course_id = 3;
        -- 也可以使用join on 语句来进行自连接查询
        SELECT
            s1.*
        FROM
            score s1
            JOIN score s2 ON s1.student_id = s2.student_id
            AND s1.score < s2.score
            AND s1.course_id = 1
            AND s2.course_id = 3;

        image.gif

        以上查询只显示了成绩信息,并且是分布执行的。要显示学生及成绩信息,并在一条语句显示:

        SELECT
            stu.*,
            s1.score Java,
            s2.score 计算机原理
        FROM
            score s1
            JOIN score s2 ON s1.student_id = s2.student_id
            JOIN student stu ON s1.student_id = stu.id
            JOIN course c1 ON s1.course_id = c1.id
            JOIN course c2 ON s2.course_id = c2.id
            AND s1.score < s2.score
            AND c1.NAME = 'Java'
            AND c2.NAME = '计算机原理';

        image.gif

        image.gif编辑

        4.2.4 子查询

        子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询

        image.gif编辑

          • 单行子查询:返回一行记录的子查询

          查询与“不想毕业” 同学的同班同学:

          select * from student where classes_id=(select classes_id from student where
          name='不想毕业');

          image.gif

            • 多行子查询:返回多行记录的子查询

            案例:查询“语文”或“英文”课程的成绩信息

            1. [NOT] IN关键字:

            -- 使用IN
            select * from score where course_id in (select id from course where
            name='语文' or name='英文');
            -- 使用 NOT IN
            select * from score where course_id not in (select id from course where
            name!='语文' and name!='英文');

            image.gif

            可以使用多列包含:

            -- 插入重复的分数:score, student_id, course_id列重复
            insert into score(score, student_id, course_id) values
            -- 黑旋风李逵
            (70.5, 1, 1),(98.5, 1, 3),
            -- 菩提老祖
            (60, 2, 1);
            -- 查询重复的分数
            SELECT
                *
            FROM
                score
            WHERE
                ( score, student_id, course_id ) IN ( SELECT score, student_id,
                course_id FROM score GROUP BY score, student_id, course_id HAVING
                count( 0 ) > 1 );

            image.gif

            2. [NOT] EXISTS关键字:

            -- 使用 EXISTS
            select * from score sco where exists (select sco.id from course cou
            where (name='语文' or name='英文') and cou.id = sco.course_id);
            -- 使用 NOT EXISTS
            select * from score sco where not exists (select sco.id from course cou
            where (name!='语文' and name!='英文') and cou.id = sco.course_id);

            image.gif

              • 在from子句中使用子查询:子查询语句出现在from子句中。这里要用到数据查询的技巧,把一个子查询当做一个临时表使用。

              查询所有比“中文系2019级3班”平均分高的成绩信息:

              -- 获取“中文系2019级3班”的平均分,将其看作临时表
              SELECT
                  avg( sco.score ) score
              FROM
                  score sco
                  JOIN student stu ON sco.student_id = stu.id
                  JOIN classes cls ON stu.classes_id = cls.id
              WHERE
                  cls.NAME = '中文系2019级3班';

              image.gif

              查询成绩表中,比以上临时表平均分高的成绩:

              SELECT
              *
              FROM
              score sco,
              (
              SELECT
              avg( sco.score ) score
              FROM
              score sco
              JOIN student stu ON sco.student_id = stu.id
              JOIN classes cls ON stu.classes_id = cls.id
              WHERE
              cls.NAME = '中文系2019级3班'
              ) tmp
              WHERE
              sco.score > tmp.score;

              image.gif

              image.gif编辑

              4.2.5 合并查询

              在实际应用中,为了合并多个select的执行结果,可以使用集合操作符 union,union all。使用UNION和UNION ALL时,前后查询的结果集中,字段需要一致。

                • union

                该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行。

                案例:查询id小于3,或者名字为“英文”的课程:
                select * from course where id<3
                union
                select * from course where name='英文';
                -- 或者使用or来实现
                select * from course where id<3 or name='英文';

                image.gif

                  • union all

                  该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行。

                  案例:查询id小于3,或者名字为“Java”的课程
                  -- 可以看到结果集中出现重复数据Java
                  select * from course where id<3
                  union all
                  select * from course where name='英文';

                  image.gif

                  image.gif编辑

                  相关实践学习
                  如何在云端创建MySQL数据库
                  开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
                  全面了解阿里云能为你做什么
                  阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
                  目录
                  相关文章
                  |
                  5月前
                  |
                  关系型数据库 MySQL 数据库
                  数据迁移脚本优化过程:从 MySQL 到 Django 模型表
                  在大规模的数据迁移过程中,性能问题往往是开发者面临的主要挑战之一。本文将分析一个数据迁移脚本的优化过程,展示如何从 MySQL 数据库迁移数据到 Django 模型表,并探讨优化前后的性能差异。
                  |
                  2月前
                  |
                  SQL Java 关系型数据库
                  MySQL数据库基础:增删查改
                  本文详细介绍了数据库中常用数据类型的使用方法及其在Java中的对应类型,并演示了如何创建表、插入数据、查询数据(包括全列查询、指定列查询、去重查询、排序查询、条件查询和分页查询)、修改数据以及删除数据。此外,还特别强调了处理NULL值时的注意事项,以及在执行修改和删除操作时应谨慎使用条件语句,以避免误操作导致的数据丢失。
                  77 14
                  MySQL数据库基础:增删查改
                  |
                  1月前
                  |
                  存储 关系型数据库 MySQL
                  【探究Mysql表中的增删查改(进阶篇)】
                  【探究Mysql表中的增删查改(进阶篇)】
                  54 7
                  |
                  4月前
                  |
                  存储 SQL 关系型数据库
                  MySQL设计规约问题之在数据库设计中,为什么要适当考虑反范式的表设计
                  MySQL设计规约问题之在数据库设计中,为什么要适当考虑反范式的表设计
                  |
                  4月前
                  |
                  SQL 存储 数据库
                  MySQL设计规约问题之如何处理日志类型的表
                  MySQL设计规约问题之如何处理日志类型的表
                  |
                  5月前
                  |
                  关系型数据库 MySQL
                  蓝易云 - 如何修复MySQL中损坏的表
                  最后,为了防止数据丢失,定期备份数据是非常重要的。
                  153 3
                  |
                  4月前
                  |
                  运维 关系型数据库 MySQL
                  实时计算 Flink版产品使用问题之在处理MySQL表新增数据记录时,没有正确触发变更事件,该如何解决
                  实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
                  |
                  5月前
                  |
                  SQL 关系型数据库 MySQL
                  ClickHouse(23)ClickHouse集成Mysql表引擎详细解析
                  ClickHouse的MySQL引擎允许执行`SELECT`查询从远程MySQL服务器。使用`MySQL(&#39;host:port&#39;, &#39;database&#39;, &#39;table&#39;, &#39;user&#39;, &#39;password&#39;[,...])`格式连接,支持简单`WHERE`子句在MySQL端处理,复杂条件和`LIMIT`在ClickHouse端执行。不支持`NULL`值,用默认值替换。系列文章涵盖ClickHouse安装、集群搭建、表引擎解析等主题。[链接](https://zhangfeidezhu.com/?p=468)有更多
                  257 0
                  |
                  5月前
                  |
                  SQL 缓存 关系型数据库
                  MySQL操作全攻略:库、表、数据、事务全面指南
                  MySQL操作全攻略:库、表、数据、事务全面指南
                  |
                  5月前
                  |
                  SQL 关系型数据库 MySQL
                  经验大分享:MySQL(三)数据库表的查询操作【重要】
                  经验大分享:MySQL(三)数据库表的查询操作【重要】
                  61 0
                  下一篇
                  无影云桌面