MySQL (六)--外键、联合查询、子查询

时间:2022-04-26 20:07:44

1 外键

  • 外键:foreign key,外面的键(键不在自己表中),如果一张表中有一个字段(非主键)指向另外一张表的主键,那么将该字段称为外键。

1.1 增加外键

  • 外键可以在创建表的时候或创建表之后增加(但是要考虑数据的问题)。
  • 方案一:在创建表的时候增加外键,在所有的表字段之后,使用foreign key(外键字段) references 外部表 (主键字段);
-- 创建班级
CREATE TABLE my_class(
    id INT PRIMARY KEY AUTO_INCREMENT,
    c_name ) NOT NULL,
    room )
);
-- 创建学生表
CREATE TABLE my_student1(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME ) NOT NULL,
    c_id INT ,
    CONSTRAINT fk_c_id FOREIGN KEY (c_id) REFERENCES my_class (id)
);

MySQL (六)--外键、联合查询、子查询

MySQL (六)--外键、联合查询、子查询

  • 方案二:在新增表之后,增加外键,所以需要修改表结构。
alter table 表名 add [constraint 外键名字] foreign key (外键字段) references 父表(主键字段);
-- 创建班级
CREATE TABLE my_class(
    id INT PRIMARY KEY AUTO_INCREMENT,
    c_name ) NOT NULL,
    room )
);
-- 创建学生表
CREATE TABLE my_student2(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME ) NOT NULL,
    c_id INT
);
ALTER TABLE my_student2 ADD CONSTRAINT fk_c_id FOREIGN KEY (c_id) REFERENCES my_class (id);

MySQL (六)--外键、联合查询、子查询

MySQL (六)--外键、联合查询、子查询

1.2 修改外键&删除外键

  • 外键不可以修改:只能先删除后新增。
alter table 表名 drop foreign key 外键名;--一张表中可以有多个外键,但是名字不能相同
ALTER TABLE my_student2 DROP FOREIGN KEY fk_c_id;

MySQL (六)--外键、联合查询、子查询

1.3 外键作用

  • 外键默认的作用有两点:
    • 一个对父表:父表数据进行写操作(删和改,都必须设计到主键本身),如果对应的主键在字表中已经被数据所引用,那么就不允许操作。
    • 一个对字表(外键字段所在的表):字表数据进行写操作(增和改)的时候,如果对应的外键字段在父表找不到对应的匹配,操作会失败。  

1.4 外键条件

  • 外键要存在:首先必须表的存储引擎是innodb。如果不是innodb存储引擎,那么外键可以创建成功,但是没有约束效果。
  • 外键字段的字段类型(列类型)必须和父表的主键类型完全一致。
  • 一张表中的外键名字不能重复。
  • 增加外键的字段(数据已经存在),必须保证数据和父表主键要求对应。

1.5 外键约束

  • 外键约束:就是外键的作用。
  • 外键约束有三种约束模式:都是针对父表的约束
    • district:严格模式(默认的),父表不能删除或更新一个已经被子表数据引用的记录(主键)。
    • cascade:级联模式,父表的操作,对应子表关联的数据也随之变化。
    • set null:置空模式,父表的操作之后,子表对应的数据(外键)被置空。  
  • 通常:父表删除的时候,子表置空;更新的时候,子表级联操作。
constraint 外键名字 foreign key (外键字段) references 主表(主键) on delete set null;
constraint 外键名字 foreign key (外键字段) references 主表(主键) on update cascade;
constraint 外键名字 foreign key (外键字段) references 主表(主键) on delete set null on update cascade;
  • 删除置空的前提:外键字段为空(如果不满足条件,外键无法创建)。
  • 外键虽然很强大,能够进行各种约束,但是对于java来说,外键约束降低了java对数据的可控性。所以,通常情况下,在实际开发中,很少使用外键的级联模式和置空模式。

2 联合查询

  • 联合查询:将多次查询语句,在记录上进行拼接。
  • 基本语法
多条select语句构成:每一条select语句获取的字段必须严格一致(但是字段类型无关)
select 语句1 union [union 选项] 语句2……;
-- union选项:与select选项一样有两个
-- all 保留所有(不管重复)
-- distinct 去重(整个重复):默认的
  • 示例:联合查询
SELECT * FROM my_class
UNION
SELECT * FROM my_class;

MySQL (六)--外键、联合查询、子查询

  • 示例:联合查询
SELECT id,c_name,room FROM my_class
UNION
SELECT id,c_id,NAME FROM my_student2;

MySQL (六)--外键、联合查询、子查询

  • 联合查询的意义
    • 查询同一张表,但是需求不同:如查询学生信息,男生身高升序,女生身高降序(面试题)。
    • 多表查询:多张表的结构是完全一样的,保存的数据(结构)也是一样的。  
  • 联合查询Order by使用
    • 在联合查询中:order by不能直接使用,需要对查询语句使用括号才行。  
(SELECT * FROM my_student2 WHERE sex = '男' ORDER BY height ASC)
UNION
(SELECT * FROM my_student2 WHERE sex = '女' ORDER BY height DESC);

MySQL (六)--外键、联合查询、子查询

    • 如果想order by生效:必须搭配limit,limit使用限定的最大数即可。  
()
UNION
();

MySQL (六)--外键、联合查询、子查询

3 子查询

  • 子查询:查询是在某个查询结果之上进行的(一条select语句内部包含了另外一条select语句)。
  • 子查询分类方式:
    • 按位置分类:子查询(select语句)在外部查询(select语句)中出现的位置。
      • From子查询:子查询在From之后
      • where子查询:子查询出现在where条件中
      • exists子查询:子查询出现在exists里面
    • 按照结果分类:根据子查询得到的数据进行分类(理论上任何一个查询得到的结果都可以理解为二维表)
      • 标量子查询:子查询得到的结果是一行一列。(出现在where之后)
      • 列子查询:子查询得到的结果是一列多行。(出现在where之后)
      • 行子查询:子查询得到的结果是多列一行(多行多列)(出现在where之后)
      • 表子查询:子查询得到的结果是多行多列(出现的位置在From之后)        
  • 示例脚本
-- 创建班级表
CREATE TABLE my_class(
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
    c_name ) NOT NULL COMMENT '班级名字',
    room ) NOT NULL COMMENT '班级所在教室'
);
-- 插入班级信息
INSERT INTO my_class VALUES (NULL,'java001班','A01');
INSERT INTO my_class VALUES (NULL,'Linux003班','C15');
INSERT INTO my_class VALUES (NULL,'c005班','B23');

-- 创建学生表
CREATE TABLE my_student(
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
    NAME ) NOT NULL COMMENT '学生姓名',
    age INT NOT NULL COMMENT '学生年龄',
    gender ) NOT NULL COMMENT '学生性别',
    c_id INT COMMENT '外键' ,
    CONSTRAINT fk_c_id FOREIGN KEY (c_id) REFERENCES my_class (id)
);
-- 插入学生信息
,);
,);
,);
,);
,);
,);

3.1 标量子查询

  • 示例:查询java001班的所有学生。
    • ①确定数据源:获取所有的学生
select * from my_student where c_id = ?;
    • ②获取班级id:可以通过名字确定。  
SELECT id FROM my_class WHERE c_name = 'java001班' ; -- 一行一列
    • ③最后的执行SQL语句  
SELECT * FROM my_student WHERE c_id = (SELECT id FROM my_class WHERE c_name = 'java001班' );

MySQL (六)--外键、联合查询、子查询

3.2 列子查询

  • 示例:查询所有在读班级的学生(班级表中存在的班级)  
    • ①确定数据源:学生   
SELECT *FROM my_student WHERE c_id IN (?);
    • ②确定有效班级的id:所有班级的id  
SELECT id FROM my_class
    • 最后的执行SQL语句  
SELECT *FROM my_student WHERE c_id IN (SELECT id FROM my_class);

MySQL (六)--外键、联合查询、子查询

  • 列子查询返回的结果会比较:一列多行,需要使用in作为条件匹配:其实在mysql中还有几个类似的条件:all、some、any。

3.3 行子查询

  • 行子查询:返回的结果可以是多行多列(一行多列)
  • 修改学生表
ALTER TABLE my_student ADD height INT NOT NULL COMMENT '身高';
 ;
 ;
 ;
 ;
 ;
 ;
  • 示例:要求查询整个学生中,年龄最大且身高
    • 思路1:  
      • ①确定数据源    
select * from my_student where age = ? and height = ?;
      • ②确定最大的年龄和最高的身高    
select max(age),max(height) from my_student;
      • ③最后的SQL执行语句    
SELECT * FROM my_student WHERE (age = (SELECT MAX(age) FROM my_student)) AND  (height =  (SELECT MAX(height) FROM my_student));

MySQL (六)--外键、联合查询、子查询

      • ④貌似上面的是对的哦,但是如果最高的年龄最大,身高最高的不是一个人呢?这样就不能查到数据呢,所以,综上所述,上面的思路貌似正确,但是不合题意。    
    • 思路2:正确解法
      • 需要构造行元素:行元素是由多个字段构成。     
SELECT * FROM my_student WHERE (age,height) = (SELECT MAX(age),MAX(height) FROM my_student);

MySQL (六)--外键、联合查询、子查询

3.4 表子查询

  • 表子查询:子查询返回的结果是当做二维表来使用。
  • 示例:找出每个班中最高的一个学生。
    • ①先对学生按照身高降序  
SELECT * FROM my_student  ORDER BY height DESC
    • ②对排序后的学生临时表按照班级分组  
SELECT * FROM (SELECT * FROM my_student  ORDER BY height DESC) AS student GROUP BY student.c_id;
    • 当然,这一题可能有人会这样想?我先对学生分组,求出最大的年龄,然后用in子句,就可以了。  
SELECT * FROM my_student WHERE height IN (SELECT MAX(height) FROM my_student GROUP BY c_id);
    • 当然,第二种思路也是可以的。      

3.5 exists子查询

  • exists:是否存在,exists子查询是用来判断某些条件是否满足(跨表),exists是在where之后,exists返回的结果是0或1。
  • 示例:查询所有的学生,前提条件是班级存在。
    • ①确定数据源  
SELECT * FROM my_student WHERE ?;
    • ②确定条件是否满足  
EXISTS(SELECT * FROM my_class)
    • 最后的执行SQL语句    
SELECT * FROM my_student WHERE EXISTS(SELECT * FROM my_class);

MySQL (六)--外键、联合查询、子查询