MySQL学习笔记四之连接查询

一、测试表:

  1. 学生表
  CREATE TABLE `students` (
        `id` int NOT NULL AUTO_INCREMENT,
        `name` varchar(12) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
        `gender` tinyint(1) DEFAULT NULL,
        `age` tinyint DEFAULT NULL,
        PRIMARY KEY (`id`)
  ) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

  2. 课程表
  CREATE TABLE `subject` (
        `id` int NOT NULL AUTO_INCREMENT,
        `name` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '科目名称',
        PRIMARY KEY (`id`)
        ) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

  3. 学生成绩表
  CREATE TABLE `score` (
        `id` int NOT NULL AUTO_INCREMENT,
        `subject_id` int DEFAULT NULL,
        `score` smallint DEFAULT NULL,
        `student_id` int DEFAULT NULL,
        PRIMARY KEY (`id`)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

  4. 学生成绩等级表:
  create table grade(
        id int primary key auto_increment,
        min_score tinyint(3),
        max_score tinyint(3),
        name char(1)
  )engine=innodb default charset=utf8mb4 collate=utf8mb4_unicode_ci;
  说明:表结构只为满足实验要求,没有实际业务逻辑参考价值。

二、笛卡尔乘积现象:

  mysql> select * from subject su,score sc;
  | id | name   | id | subject_id | score | student_id |
  +----+--------+----+------------+-------+------------+
  |  5 | 物理   |  1 |          1 |    80 |          1 |
  |  4 | 化学   |  1 |          1 |    80 |          1 |
  |  3 | 英语   |  1 |          1 |    80 |          1 |
  |  2 | 数学   |  1 |          1 |    80 |          1 |
  |  1 | 语文   |  1 |          1 |    80 |          1 |
  |  5 | 物理   |  2 |          2 |    79 |          1 |
  |  4 | 化学   |  2 |          2 |    79 |          1 |
  |  3 | 英语   |  2 |          2 |    79 |          1 |
  |  2 | 数学   |  2 |          2 |    79 |          1 |
  |  1 | 语文   |  2 |          2 |    79 |          1 |
  |  5 | 物理   |  3 |          3 |  NULL |          1 |
  |  4 | 化学   |  3 |          3 |  NULL |          1 |
  |  3 | 英语   |  3 |          3 |  NULL |          1 |
  |  2 | 数学   |  3 |          3 |  NULL |          1 |
  |  1 | 语文   |  3 |          3 |  NULL |          1 |
  如上查询所示:当同时查询多表时,多表之间没有有效的的连接条件时,每张表的每行数据都将与另外几张表的每行数据进行关联输出,导致查询结果错误。
  若表1中有5条数据,表2中有20条数据,在同时查询两张表时,结果将会有 5*20=100条数据
  为避免笛卡尔乘积现象的发生,则需要在多表查询时添加有效的连接条件。
  如:
  mysql> select * from subject su,score sc where su.id=sc.subject_id;
  +----+--------+----+------------+-------+------------+
  | id | name   | id | subject_id | score | student_id |
  +----+--------+----+------------+-------+------------+
  |  1 | 语文   |  1 |          1 |    80 |          1 |
  |  2 | 数学   |  2 |          2 |    79 |          1 |
  |  3 | 英语   |  3 |          3 |  NULL |          1 |
  |  1 | 语文   |  6 |          1 |    86 |          2 |
  |  2 | 数学   |  7 |          2 |    97 |          2 |
  |  3 | 英语   |  8 |          3 |    85 |          2 |
  |  1 | 语文   | 11 |          1 |    90 |          3 |
  |  2 | 数学   | 12 |          2 |  NULL |          3 |
  |  3 | 英语   | 13 |          3 |  NULL |          3 |
  |  1 | 语文   | 16 |          1 |  NULL |          4 |
  |  2 | 数学   | 17 |          2 |    87 |          4 |
  |  3 | 英语   | 18 |          3 |    45 |          4 |
  +----+--------+----+------------+-------+------------+
  12 rows in set (0.00 sec)

三、JOIN连接图

网上找了一张各种JOIN连接查询结果图:

四、内连接

  内连接有两种语法标准:SQL92标准和SQL99标准 
  SQL92标准语法:
        select 查询列表 from table_name1,table_name2,... where 连接条件 order by 排序规则
  SQL99标准语法:
        select 查询列表 from table_name1 [inner] join table_name2 on 连接条件 [where 查询条件 group by 分组 having 筛选条件 order by 排序规则 limit 分页查询]
  1)等值连接
        #1. 查询学生的各科成绩
        mysql> select st.name 姓名,su.name 科目,sc.score 分数 from students st,subject su,score sc where st.id=sc.student_id and sc.subject_id=su.id;
        mysql> select st.name 姓名,su.name 科目,sc.score 分数 from students st inner join score sc on st.id=sc.student_id join subject su on sc.subject_id=su.id;
        +-----------+--------+-------+
        | 姓名      | 科目   | 分数   |
        +-----------+--------+-------+
        | 张三      | 语文   |     80 |
        | 张三      | 数学   |     79 |
        | 张三      | 英语   |   NULL |
        | 李四      | 语文   |     86 |
        | 李四      | 数学   |     97 |
        | 李四      | 英语   |     85 |
        | 周芷若    | 语文   |     90 |
        | 周芷若    | 数学   |   NULL |
        | 周芷若    | 英语   |   NULL |
        | 赵敏      | 语文   |   NULL |
        | 赵敏      | 数学   |     87 |
        | 赵敏      | 英语   |     45 |
        +-----------+--------+--------+
        12 rows in set (0.00 sec)

        #2. 查询有成绩的学生的各科成绩
        mysql> select st.name 姓名,su.name 科目,sc.score 分数 from students st,subject su,score sc where st.id=sc.student_id and sc.subject_id=su.id and sc.score is not null;
        mysql> select st.name 姓名,su.name 科目,sc.score 分数 from students st inner join score sc on st.id=sc.student_id join subject su on sc.subject_id=su.id where sc.score is not null;
        +-----------+--------+--------+
        | 姓名      | 科目   | 分数   |
        +-----------+--------+--------+
        | 张三      | 语文   |     80 |
        | 张三      | 数学   |     79 |
        | 李四      | 语文   |     86 |
        | 李四      | 数学   |     97 |
        | 李四      | 英语   |     85 |
        | 周芷若    | 语文   |     90 |
        | 赵敏      | 数学   |     87 |
        | 赵敏      | 英语   |     45 |
        +-----------+--------+--------+
        8 rows in set (0.00 sec)
  
        #3. 查询每个学生的总成绩,并按总成绩倒序排列
        mysql> select st.name 姓名,sum(sc.score) 总成绩 from students st,score sc where st.id=sc.student_id group by st.name order by 总成绩 desc;
        mysql> select st.name 姓名,sum(sc.score) 总成绩 from students st join score sc on st.id=sc.student_id group by st.name order by 总成绩 desc;
        +-----------+-----------+
        | 姓名      | 总成绩    |
        +-----------+-----------+
        | 李四      |       325 |
        | 张三      |       318 |
        | 赵敏      |       200 |
        | 周芷若    |       150 |
        +-----------+-----------+
        4 rows in set (0.00 sec)
        
  2)非等值连接
        #1. 查询出学生的各科成绩及对应的级别
        mysql> select a.*,b.name 级别 from (select st.name 姓名,su.name 科目,sc.score 分数 from students st inner join score sc on st.id=sc.student_id join subject su on      sc.subject_id=su.id where sc.score is not null) a join grade b on a.分数 between b.min_score and b.max_score;
  +-----------+--------+--------+--------+
  | 姓名      | 科目   | 分数   | 级别   |
  +-----------+--------+--------+--------+
  | 赵敏      | 英语   |     45 | A级    |
  | 张三      | 数学   |     79 | B级    |
  | 赵敏      | 数学   |     87 | C级    |
  | 赵敏      | 语文   |     86 | C级    |
  | 李四      | 英语   |     85 | C级    |
  | 李四      | 语文   |     86 | C级    |
  | 张三      | 语文   |     80 | C级    |
  | 周芷若    | 语文   |     90 | D级    |
  | 李四      | 数学   |     97 | D级    |
  +-----------+--------+--------+--------+
  9 rows in set (0.05 sec)
        
  3)自连接
        #1. 查询出各科成绩前2名的学生姓名,科目,及分数
        select 
              a.* 
        from 
              (
                    select 
                          st.name 姓名,su.name 科目,sc.score 成绩 
                    from 
                          students st,subject su,score sc 
                    where 
                          st.id=sc.student_id and su.id=sc.subject_id
              ) a 
        where 
              2 > (
                    select 
                          count(*) 
                    from 
                          (
                                select 
                                      st.name 姓名,su.name 科目,sc.score 成绩 
                                from 
                                      students st,subject su,score sc 
                                where 
                                      st.id=sc.student_id and su.id=sc.subject_id
                          ) b 
                    where 
                          b.科目=a.科目 and b.成绩>a.成绩
              ) 
              and 
                    a.成绩 is not null
        order by 
              a.科目, a.成绩 desc;
        +-----------+--------+--------+
        | 姓名      | 科目   | 成绩   |
        +-----------+--------+--------+
        | 李四      | 数学   |     97 |
        | 赵敏      | 数学   |     87 |
        | 李四      | 英语   |     85 |
        | 赵敏      | 英语   |     45 |
        | 周芷若    | 语文   |     90 |
        | 李四      | 语文   |     86 |
        | 赵敏      | 语文   |     86 |
        +-----------+--------+--------+
        7 rows in set (0.00 sec)

二、外连接:

  1)左外连接:
        select 查询列表 from table_name1 left [outer] join table_name2 on 连接条件 [where 查询条件 group by 分组列表 having 筛选条件 order by 分组规则 limit 分页信息]
  例:
        #1. 查询出所有学生各科的成绩
        mysql> select st.*,sc.score,su.name from students st left join score sc on st.id=sc.student_id left join subject su on sc.subject_id=su.id;
        +----+-----------+--------+------+-------+--------+
        | id | name      | gender | age  | score | name   |
        +----+-----------+--------+------+-------+--------+
        |  1 | 张三      |      1 |   18 |    69 | NULL   |
        |  1 | 张三      |      1 |   18 |    90 | NULL   |
        |  1 | 张三      |      1 |   18 |  NULL | 英语   |
        |  1 | 张三      |      1 |   18 |    79 | 数学   |
        |  1 | 张三      |      1 |   18 |    80 | 语文   |
        |  2 | 李四      |      1 |   19 |    57 | NULL   |
        |  2 | 李四      |      1 |   19 |  NULL | NULL   |
        |  2 | 李四      |      1 |   19 |    85 | 英语   |
        |  2 | 李四      |      1 |   19 |    97 | 数学   |
        |  2 | 李四      |      1 |   19 |    86 | 语文   |
        |  3 | 周芷若    |      2 |   18 |    60 | NULL   |
        |  3 | 周芷若    |      2 |   18 |  NULL | NULL   |
        |  3 | 周芷若    |      2 |   18 |  NULL | 英语   |
        |  3 | 周芷若    |      2 |   18 |  NULL | 数学   |
        |  3 | 周芷若    |      2 |   18 |    90 | 语文   |
        |  4 | 赵敏      |      2 |   18 |    68 | NULL   |
        |  4 | 赵敏      |      2 |   18 |  NULL | NULL   |
        |  4 | 赵敏      |      2 |   18 |    45 | 英语   |
        |  4 | 赵敏      |      2 |   18 |    87 | 数学   |
        |  4 | 赵敏      |      2 |   18 |    86 | 语文   |
        |  5 | Lucy      |      2 |   19 |  NULL | NULL   |
        |  6 | Tony      |      1 |   20 |  NULL | NULL   |
        |  7 | Lucy      |      2 |   20 |  NULL | NULL   |
        +----+-----------+--------+------+-------+--------+
        23 rows in set (0.00 sec)


        #2. 查询出没有参加考试或总成绩为0的学生信息
        mysql> select b.* from (select student_id from score group by student_id having sum(score)>0 and sum(score) is not null) a left join students b on a.student_id=b.id;
        +----+------+--------+------+
        | id | name | gender | age  |
        +----+------+--------+------+
        |  5 | Lucy |      2 |   19 |
        |  6 | Tony |      1 |   20 |
        |  7 | Lucy |      2 |   20 |
        +----+------+--------+------+
        3 rows in set (0.02 sec)

        #3. 查询出所有没有成绩的学生信息和没有成绩的科目
        mysql> select st.*,sc.score,su.name from students st left join score sc on st.id=sc.student_id left join subject su on sc.subject_id=su.id where sc.score is null;
        +----+-----------+--------+------+-------+--------+
        | id | name      | gender | age  | score | name   |
        +----+-----------+--------+------+-------+--------+
        |  1 | 张三      |      1 |   18 |  NULL | 英语   |
        |  2 | 李四      |      1 |   19 |  NULL | NULL   |
        |  3 | 周芷若    |      2 |   18 |  NULL | NULL   |
        |  3 | 周芷若    |      2 |   18 |  NULL | 英语   |
        |  3 | 周芷若    |      2 |   18 |  NULL | 数学   |
        |  4 | 赵敏      |      2 |   18 |  NULL | NULL   |
        |  5 | Lucy      |      2 |   19 |  NULL | NULL   |
        |  6 | Tony      |      1 |   20 |  NULL | NULL   |
        |  7 | Lucy      |      2 |   20 |  NULL | NULL   |
        +----+-----------+--------+------+-------+--------+
        
        #4. 查询出每个学生的平均成绩并按平均成绩倒序排列
        mysql> select st.name 姓名, avg(sc.score) 平均成绩 from students st left join score sc on st.id=sc.student_id group by st.name order by 平均成绩 desc;
        +-----------+--------------+
        | 姓名      | 平均成绩     |
        +-----------+--------------+
        | 李四      |      81.2500 |
        | 张三      |      79.5000 |
        | 周芷若    |      75.0000 |
        | 赵敏      |      71.5000 |
        | Lucy      |         NULL |
        | Tony      |         NULL |
        +-----------+--------------+
        6 rows in set (0.07 sec)

  2)右外连接语法:
        select 查询列表 from table_name1 right join table_name2 on 连接条件 [where 查询条件 group by 分组列表 having 筛选条件 order by 分组规则 limit 分页信息]
        右外连接和左外连接基本一样,区别在于:
        左外连接left左边的表为主表,右边的表为从表,右外连接与之相反
        如:
        select st.*,sc.score,su.name from score sc right join students st on st.id=sc.student_id left join subject su on sc.subject_id=su.id;
        和左外连接的例1结果相同
  3)全外连接:
        MySQL中不支持full join语法,MySQL中可以使用union连接两个SQL语句,实现全外连接,如:
        select st.*,sc.score from students st left join score sc on st.id=sc.student_id 
        union 
        select st.*,sc.score from students st right join score sc on st.id=sc.student_id;
        +------+-----------+--------+------+-------+
        | id   | name      | gender | age  | score |
        +------+-----------+--------+------+-------+
        |    1 | 张三      |      1 |   18 |    69 |
        |    1 | 张三      |      1 |   18 |    90 |
        |    1 | 张三      |      1 |   18 |  NULL |
        |    1 | 张三      |      1 |   18 |    79 |
        |    1 | 张三      |      1 |   18 |    80 |
        |    2 | 李四      |      1 |   19 |    57 |
        |    2 | 李四      |      1 |   19 |  NULL |
        |    2 | 李四      |      1 |   19 |    85 |
        |    2 | 李四      |      1 |   19 |    97 |
        |    2 | 李四      |      1 |   19 |    86 |
        |    3 | 周芷若    |      2 |   18 |    60 |
        |    3 | 周芷若    |      2 |   18 |  NULL |
        |    3 | 周芷若    |      2 |   18 |    90 |
        |    4 | 赵敏      |      2 |   18 |    68 |
        |    4 | 赵敏      |      2 |   18 |  NULL |
        |    4 | 赵敏      |      2 |   18 |    45 |
        |    4 | 赵敏      |      2 |   18 |    87 |
        |    4 | 赵敏      |      2 |   18 |    86 |
        |    5 | Lucy      |      2 |   19 |  NULL |
        |    6 | Tony      |      1 |   20 |  NULL |
        |    7 | Lucy      |      2 |   20 |  NULL |
        +------+-----------+--------+------+-------+
        21 rows in set (0.00 sec)

        union连接可以实现去重。

三、交叉连接:

  语法:
        select 查询列表 from table_name1 cross join table_name2;
  交叉连接结果为笛卡尔乘积。

四、子查询

  出现在其他语句中的select语句,称为子查询
  #1. 查询出没有参加考试或总成绩为0的学生信息
        select * 
        from students 
        where id in 
              (select student_id from score group by student_id having sum(score)>0 and sum(score) is not null); #子查询
        +----+------+--------+------+
        | id | name | gender | age  |
        +----+------+--------+------+
        |  5 | Lucy |      2 |   19 |
        |  6 | Tony |      1 |   20 |
        |  7 | Lucy |      2 |   20 |
        +----+------+--------+------+
        3 rows in set (0.02 sec)
原文地址:https://www.cnblogs.com/huige185/p/13995690.html