【MySQL_JDBC】Day23-Day28 数据库基础、JDBC基础、聊天室3.0

数据库

数据库基本概念

数据库DataBase

定义:

保存数据的仓库就称为数据库

例如

编写一个用户管理系统,可以让用户在我们编写的系统上进行注册等操作,此时就涉及到了保存用户数据的操作,目前我们的做法可以将一个用户信息以一个User对象形式表示,然后利用IO知识中的文件流与对象流将对象序列化都写入XXX.obj文件中保存。当有大量的obj文件后,可以使用一个专门的目录"users"来保存所有的obj文件,用来归纳所有的用户数据。同理,其他数据也可以放在对应的目录中,这些目录都可以称为数据库。

以目录和文件形式保存数据,如何维护?

半编码半手动,编码很难做到通用,灵活,效率高。手动则效率低。随之带来的就是维护成本的增加。

数据库管理系统DBMS

定义

这是一套可以独立运行,用于维护磁盘上的数据的一套软件。

特点

维护性高,灵活度高,效率高,可扩展性强

常见的DBMS
  • MariaDB
  • MySQL
  • Oracle
  • SQLServer
  • DB2
数据库在JAVA项目中所处的角色

在这里插入图片描述

数据库管理系统中的常用概念

库是表的集合,将来不同的项目应当创建不同的库,来保存项目中所有需要涉及到的数据

是一句具有相同属性数据的集合,一张表是二维的,分为

  • 列:数据应具有的属性信息
  • 行:具有这组属性的一条具体数据
  • 以面向对象的思想:
    • 表:对标java中的类
    • 列:对标类中的属性
    • 行:对标具体的实例

在这里插入图片描述

库与表的关系

不同的项目可以有不同的库, 每个库中又可以保存不同的表。DBMS可以同时管理多个库和库中的表。

在这里插入图片描述

如何操作数据库

数据库的角色

DBMS安装以后是一个独立可运行的软件,并且是以服务端形式在操作系统中运行的,我们如若操作数据库则是以客户端身份与DBMS进行链接,连接后进行通讯完成对数据的操作。

数据库客户端
  • 命令行工具(数据库自带)
  • 图形化界面(数据库自带)
  • JDBC:java数据库链接,也是我们将来最常用的方式
  • 集成开发环境(比如IDEA),也算是图形化界面,底层也是利用JDBC操作
如何操作数据库

我们是以客户端形式与服务端建立链接,向数据库发送指令,让数据库进行相关的操作

这里和数据库对话时发送的指令称为SQL

SQL的全程:Structured Query Language – 结构化查询语言

优点

SQL语句有标准,执行标准是SQL92标准,基本的数据库操作都有规定的语法,不同的DBMS都支持该标准,因此我们发送SQL语句给任何DBMS都可以进行相应的操作。

SQL语句是DBMS界的普通话

缺点

SQL92标准并非将所有数据库操作都规定了语法,这导致没有在该标准中的操作不同的数据库厂商定义了自己的类似SQL的语法,这部分操作称为"方言"

学习重点:学会SQL语句,对数据库进行操作

SQL语言-Structured Query Language

SQL语言的特点

  • SQL语言不区分大小写,要养成良好的书写习惯:
    • 关键字全大写
    • 非关键字全小写
    • 例如:SELECT username,password,nickname,age FROM user
  • SQL语句的字符串使用单引号表示字面量,并且区分大小写
    • SELECT username,password FROM user WHERE username=‘jack’
    • SELECT username,password FROM user WHERE username=‘JACK’
    • 上述jack和JACK是完全不同的字符串,内容区分大小写

SQL分类

  • DDL:数据定义语言,用于进行数据库对象的相关操作。常见的数据库对象:库,表,索引,视图,序列等
  • DML:数据操作语言,对表中数据进行操作的语言。设计的操作:增(INSERT),删(DELETE),改(UPDATE)
  • DQL:数据查询语言,对表中数据进行检索的语言(相对较难,语法众多,学习重点)。
  • TCL:事务控制语言,设计的操作:COMMIT,ROLLBACK
  • DCL:数据控制语言,对数据库维护操作,例如权限管理等。是DBA需要重点学习的内容。

DDL语言-数据定义语言

对数据库对象进行操作的语言,涉及的关键字:CREATE,ALTER,DROP

对数据库进行操作

新建一个数据库
语法
CREATE DATABASE 数据库名 [CHARSET=字符集]
例如

创建一个名为mydb的数据库

CREATE DATABASE mydb;

注:每条SQL语句结尾的";"并非必须的。

查看已经创建的数据库
SHOW DATABASES
创建数据库时指定字符集
创建数据库mydb1,指定字符集为UTF-8
CREATE DATABASE mydb1 CHARSET=UTF8

创建数据库mydb2,指定字符集为GBK
CREATE DATABASE mydb2 CHARSET=GBK
查看创建某个数据库时的信息
语法
SHOW CREATE DATABASE 数据库名
例如
SHOW CREATE DATABASE mydb

删除数据库

语法
DROP DATABASE 数据库名
例如
DROP DATABASE mydb;

注意:删除数据库是不可逆操作

切换数据库

在DBMS下会存在多个不同的数据库,只有切换到对应的数据库上,那么后期学习的所有操作才是针对该数据库进行的,比如创建表才是在切换的数据库上进行。

语法
USE 数据库名
例如
切换到mydb1库
USE mydb1;

切换到mydb2库
USE mydb2;

练习

题干
1. 创建 db1和db2 数据库 字符集分别为utf8和gbk
2. 查询所有数据库检查是否创建成功
3. 检查两个数据库的字符集是否正确(查看创建时的SQL)
4. 先使用db2 再使用 db1
5. 删除两个数据库
答案
1. 创建 db1和db2 数据库 字符集分别为utf8和gbk
   CREATE DATABASE db1 CHARSET=UTF8
   CREATE DATABASE db2 CHARSET=GBK
2. 查询所有数据库检查是否创建成功
   SHOW DATABASES   
3. 检查两个数据库的字符集是否正确(查看创建时的SQL)
   SHOW CREATE DATABASE db1
   SHOW CREATE DATABASE db2
4. 先使用db2 再使用 db1
   USE db2
   USE db1
5. 删除两个数据库
   DROP DATABASE db1
   DROP DATABASE db2

创建一张表

语法
CREATE TABLE 表名(
                   字段1名 类型[(长度)] [DEFAULT 默认值] [约束],
                   字段2名 类型[(长度)] [DEFAULT 默认值] [约束],
  ...
                   字段n名 类型[(长度)] [DEFAULT 默认值] [约束]
  )
例如
准备一个数据库mydb并使用它,基于该库学习表的创建(创建出来的表都保存在mydb库中)
CREATE DATABASE mydb;
USE mydb;

创建一张表:user
表中字段:用户名,密码,昵称,年龄

CREATE TABLE user(
	username VARCHAR(32),			VHARCHAR在数据库中是字符串类型
	password VARCHAR(32),           圆括号用来指定长度,单位是字符
	nickname VARCHAR(32),
	age INT(3)                      INT为整数类型,长度是数字的位数
)




查看库中所有的表
SHOW TABLES
查看创建表时的信息
语法
SHOW CREATE TABLE 表名
例如
SHOW CREATE TABLE user
查看表结构
语法
DESC 表名
例如
查看user表的结构
DESC user

修改表名

语法
RENAME TABLE 原表名 TO 新表名
例如
user表重命名为userinfo
RENAME TABLE user TO userinfo

删除表

语法
DROP TABLE 表名
例如
将userinfo表删除
DROP TABLE userinfo

练习

题干
1.创建数据库mydb3 字符集gbk 并使用
2.创建t_hero英雄表, 有名字和年龄字段
3.修改表名为hero
4.查看表hero的信息
5.查询表hero结构
6.删除表hero
7.删除数据库mydb3

答案
1.创建数据库mydb3 字符集gbk 并使用
  CREATE DATABASE mydb3 CHARSET=GBK
  USE mydb3
2.创建t_hero英雄表, 有名字和年龄字段
CREATE TABLE t_hero(
                     name VARCHAR(32),
                     AGE INT(3)
)
  3.修改表名为hero
  RENAME TABLE t_hero TO hero
4.查看表hero的信息
  SHOW CREATE TABLE hero
  5.查询表hero结构
  DESC hero
  6.删除表hero
DROP TABLE hero
  7.删除数据库mydb3
DROP DATABASE mydb3

修改表结构

准备一张表
CREATE TABLE hero(
	name VARCHAR(32),
	age INT
)
添加一个字段
语法
ALTER TABLE 表名 ADD 字段名 类型[(长度)][DEFAULT 默认值][约束]
例如

在这里插入图片描述

在表最开始插入字段
语法
ALTER TABLE 表名 ADD 字段名 类型[(长度)][DEFAULT 默认值][约束] FIRST
例如
向hero表中最开始添加id字段
ALTER TABLE hero ADD id INT FIRST

在这里插入图片描述

在表中插入新字段
语法
将指定字段添加到表中指定字段之后
ALTER TABLE 表名 ADD 字段名 类型[(长度)][DEFAULT 默认值][约束] AFTER 表中某字段
例如
在hero表中name字段之后添加pwd字段
ALTER TABLE hero ADD pwd VARCHAR(32) AFTER name

在这里插入图片描述

删除表中字段
语法
ALTER TABLE 表名 DROP 字段名
例如
将hero表中的pwd字段删除
ALTER TABLE hero DROP pwd

在这里插入图片描述

修改表中现有字段
语法
ALTER TABLE 表名 CHANGE 原字段名 新字段名 类型[(长度)][DEFAULT 默认值][约束]
例如
将hero表中的age字段长度改为3
ALTER TABLE hero CHANGE age age INT(3)

在这里插入图片描述

将hero表中的age字段类型改为字符串,长度改为20
ALTER TABLE hero CHANGE age age VARCHAR(20)

在这里插入图片描述

将hero表中的gender字段改名为nickname,类型为字符串,长度30.且该字段内容不允许为空
ALTER TABLE hero CHANGE gender nickname VARCHAR(30) NOT NULL

在这里插入图片描述

修改表结构的注意事项
  • 修改表结构最好是在表中没有记录的情况下进行
  • 如果表中存在数据
    • 尽量不要修改字段类型,因为可能由于表中记录对应字段的值不符合新修改的类型导致修改失败
    • 字段长度尽可能不要缩短
    • 为字段添加约束时,表中记录该字段的值不能有违反约束的情况

综合练习

题干
1.创建数据库mydb4 字符集utf8并使用
2.创建teacher表 有名字(name)字段
3.添加表字段: 最后添加age 最前面添加id(int型) , age前面添加salary工资(int型)
4.删除age字段
5.修改表名为t
6.删除表t
7.删除数据库mydb4

答案
1.创建数据库mydb4 字符集utf8并使用
  CREATE DATABASE mydb4 CHARSET=UTF8
  USE mydb4
2.创建teacher表 有名字(name)字段
  CREATE TABLE teacher(
  	name VARCHAR(32)
  )
3.添加表字段: 最后添加age 最前面添加id(int) , age前面添加salary工资(int)
  ALTER TABLE teacher ADD age INTALTER TABLE teacher ADD id INT FIRST;
  ALTER TABLE teacher ADD salary INT AFTER name;
4.删除age字段
  ALTER TABLE teacher DROP age
5.修改表名为t
  RENAME TABLE teacher TO t
6.删除表t
  DROP TABLE t
7.删除数据库mydb4
  DROP DATABASE mydb4

DML语言-数据操作语言

定义

对表中记录进行操作的语言,涉及的操作:

  • INSERT:向表中插入记录的操作
  • UPDATE:修改表中记录的操作
  • DELETE:删除表中记录的操作

准备工作

应用mydb库
USE mydb;

创建一张表person
CREATE TABLE person(
	name VARCHAR(30),
    age INT(3)
)

INSERT语句

INSERT语句用于向表中插入记录

语法
INSERT INTO 表名 [(字段1,字段2,...)] VALUES(1,2,....)
例如
INSERT INTO person (name,age) VALUES ('张三',22);
INSERT INTO person (age,name) VALUES (33,'李四');

以下是错误示范
INSERT INTO person (name,age) VALUES (35,'王五');		指定的字段与给定值顺序不一致
INSERT INTO person (name,age) VALUES ('王五');		没有足够的值

在这里插入图片描述

注意事项
  • 数据库中字符出字面量要使用单引号括起来
  • INSERT语句中VALUES子句执行的值的顺序,个数,类型要与前面指定的字段一致
  • 指定的字段顺序,个数可以与表不一致
补充

查看表中记录的SQL语句,这属于DQL语言,后面会详细说明

SELECT * FROM 表名
SELECT * FROM person
插入默认值

INSERT语句中没有指明的字段,记录中该字段插入默认值。

如果创建表shi字段没有明确指定默认值时,默认值均为NULL

例如

在这里插入图片描述

默认值的设定

设定默认值属于DDL语句的范畴,可以在创建表和修改表时进行

  • 在创建表示为字段明确的指定默认值

    CREATE TABLE person(
    	name VARCHAR(30) DEFAULT '无名氏',
    	age INT(3) DEFAULT 22
    )
    
  • 修改表时为字段补充默认值

    ALTER TABLE person CHANGE name name VARCHAR(30) DEFAULT '无名氏'
    

    在这里插入图片描述

插入默认值
INSERT INTO person (age) VALUES(33);
此时name字段插入默认值'无名氏'

在这里插入图片描述

全列插入

INSERT子句中不指定任何字段时,为全列插入。此时VALUES子句指定的值的顺序,个数,类型要与表结构中字段完全一致

语法
INSERT INTO 表名 VALUES(值1,2,...
例如
INSERT INTO person VALUES('赵六',33);

以下是错误示范:
INSERT INTO person VALUES(33,'赵六');		//值的顺序与表结构不同
INSERT INTO person VALUES('赵六');		//没有足够的值

注:开发中不建议这样的写法

插入NULL值和默认值
  • 可以在VALUES子句中显示的使用关键字NULL向字段插入NULL值

  • 可以在VALUES子句中显示的使用关键字DEFAULT向字段插入默认值

    向name字段插入默认值
    INSERT INTO person VALUES(DEFAULT,22)
    
    向name字段插入NULL值
    INSERT INTO person VALUES(NULL,33)
    
批量插入

可以在VALUE子句中同时指定最组值,同时插入多条记录

语法
INSERT INTO 表名 [(字段1,字段2,...)] VALUES (值1,值2,值3...),(第二组值),(第三组值),...
例如
INSERT INTO person(name,age) 
VALUES ('阿猫',22),('阿狗',33),('阿三',44)

UPDATE语句

UPDATE语句用于修改表中记录

语法
UPDATE 表名
SET 字段1=新值1,字段2=新值2,...
[WHERE 过滤条件]			添加过滤条件可以做到仅将表中满足该条件的记录进行修改
例如
将person表中每条记录的age字段值改为40
UPDATE person
SET age=40

注意:UPDATE语句不添加WHERE子句时,表示表中每条记录都要修改!这样的操作在实际开发中很少发生

WHERE子句在UPDATE语句中的作用

通常UPDATE语句中要添加WHERE子句,用于指定过滤条件,将满足条件的记录进行修改

例如

在这里插入图片描述


在这里插入图片描述

WHERE子句的基础条件

WHERE子句会在后面DQL语言中详细说明会

WHERE中常用的基础条件:=,>,>=,<,<=,<>(不等于"<>“,而”!="不是所有数据库都支持)

例如
将年龄大于30岁的人,改为年龄29
UPDATE person
SET age=29
WHERE age>30
修改多个字段
将李四的名字改为"李老四",年龄改为66
UPDATE person
SET name='李老四',age=66
WHERE name='李四'

DELETE语句

DELETE语句用于删除表中记录

语法
DELETE FROM 表名
[WHERE 过滤条件]

注意:DELETE语句通常都要添加WHERE子句,否则为清空表操作

例如
删除'李老四'
DELETE FROM person
WHERE name='李老四'

删除年龄在25岁以下的人
DELETE FROM person
WHERE age<25
清空表
DELETE FROM person

总结

  • INSERT语句,向表中插入记录
    • INSERT后面指定的字段名可以与表结构不一致,但是要求VALUES子句中指定的值个数,顺序,类型必须与指定的字段一致
    • INSERT语句可以忽略某些字段,此时被忽略的字段会插入默认值
    • INSERT语句可以显示的插入默认值,此时VALUES子句中对应字段的值使用关键字DEFAULT
    • INSERT语句可以显示的插入NULL值,此时VALUES子句中对应字段的值使用关键字NULL
    • INSERT语句可以不指定任何字段,此时为全列插入,要求VALUES子句中指定的值的顺序,个数,类型必须与表表结构完全一致
  • UPDATE语句,用来修改表中的记录
    • 通常UPDATE语句不会忽略WHERE子句,因为修改表中记录是修改满足WHERE子句要求的记录。
    • 如果忽略了WHERE子句则是表中所有记录进行修改
  • DELETE语句,用来删除表中的记录
    • 通常DELETE语句不应当忽略WHERE子句,否则为清空表操作

综合练习

题干
1.创建数据库day2db 字符集utf8并使用
2.创建t_hero表, 有name字段
3.修改表名为hero
4.最后面添加价格字段money(整数类型,长度6), 最前面添加id字段(整数类型), name后面添加 age字
段(整数类型,长度3)
5.表中添加以下数据: 1,李白,50,6888 2,赵云,30,13888 3,刘备,25,6888
6.修改刘备年龄为527.修改年龄小于等于50岁的价格为5000
8.删除价格为5000的信息
9.删除表, 删除数据库
答案
1.创建数据库day2db 字符集utf8并使用
  CREATE DATABASE day2db CHARSET=UTF8
  USE day2db
2.创建t_hero表, 有name字段
  CREATE TABLE t_hero(
  	name VARCHAR(32)
  )
3.修改表名为hero
  RENAME TABLE t_hero TO hero
4.最后面添加价格字段money(整数类型,长度6), 最前面添加id字段(整数类型), name后面添加 age字段(整数类  型,长度3)
  ALTER TABLE hero ADD money INT(6);
  ALTER TABLE hero ADD id INT FIRST;
  ALTER TABLE hero ADD age INT(3) AFTER name;
5.表中添加以下数据: 1,李白,50,6888 2,赵云,30,13888 3,刘备,25,6888
  INSERT INTO hero (id,name,age,money)
  VALUES(1,'李白',50,6888),(2,'赵云',30,13888),(3,'刘备',25,6888)
6.修改刘备年龄为52UPDATE hero
  SET age=52
  WHERE name='刘备'
7.修改年龄小于等于50岁的价格为5000
  UPDATE hero
  SET money=5000
  WHERE age<=50
8.删除价格为5000的信息
  DELETE FROM hero
  WHERE money=5000
9.删除表, 删除数据库
  DROP TABLE hero;
  DROP DATABASE day2db

数据类型

在数据库中每一张表的每一个字段都要指定数据类型以确保可以正确的保存对应的数据

注意:数据类型本身是方言,不同的数据库类型名称不完全一样

数字类型

整数类型
INT类型

INT类型占用4个字节,保存的数字±21亿。

  • INT(n):n表示数字的位数,如果不指定则默认长度为11位
BIGINT类型

BIGINT类型占用8个字节。

浮点数类型
DOUBLE类型

DOUBLE类型用于保存小数

  • DOUBLE(M,N):M和N都是一个整数,M用于表示数字的总位数,N表示其中小数的位数。M包含N

    DOUBLE(7,4):一共有7位数字,其中4位是小数。最大值:999.9999

例如
CREATE TABLE person1(
	age INT(3),					age字段只能存放整数,最大值999
	salary DOUBLE(7,2)			salary字段可以保存小数,最大值99999.99
)
  • 插入小数

    INSERT INTO person1(age,salary) VALUES(22,99999.99);
    
    超过范围会报错
    INSERT INTO person1(age,salary) VALUES(22,9999999);
    

    在这里插入图片描述

  • 插入小数时,如果精度超多了最大范围,会四舍五入

    INSERT INTO person1(age,salary) VALUES(33,12345.678);
                                                      ^
                                               这里会进行四舍五入
    

    在这里插入图片描述

  • 插入小数时如果四舍五入后超过最大范围会报错

    INSERT INTO person1(age,salary) VALUES(33,99999.997);
    

    在这里插入图片描述

字符类型

定长字符串
CHAR类型

CHAR类型是定长字符串类型,无论实际保存的字符是多少,该字段一定要占用字段指定长度的字符量,不足部分补充空格。

  • CHAR(n):n是一个数字,单位是字符,最大长度255
  • CHAR在磁盘开辟的空间是固定的,存入数据不足指定长度时补充空格来达到长度
  • 优点:磁盘空间占用长度是固定的,因此查询效率高
  • 缺点:磁盘空间有所浪费
  • 在数据量不大,并且通常保存固定字符个数的数据时使用。例如性别
变长字符串
VARCHAR类型

VARCHAR类型是变长字符串,磁盘占用量由实际保存的数据决定(用多少占多少)

  • VARCHAR(n):n是一个数字,单位是字符,最大长度65535
  • VARCHAR保存数据时,实际存入的数据时多少则磁盘空间占用多少
  • 优点:磁盘空间没有浪费
  • 缺点:查询性能差
TEXT类型

TEXT,MEDIUMTEXT,LONGTEXT

TEXT最大值也是65535,如果超过可以选择MEDIUMTEXT,LONGTEXT

TEXT不是在表的行中直接保存数据,而是单独开辟了一段空间保存数据,查询性能弱。

如果存放超过65535个字符数据时,可以选取(实际应用较少)

利用GPT来了解TEXT类型

日期类型

日期类型的种类
  • DATE:可以保存年,月,日
  • TIME:可以保存时,分,秒
  • DATETIME:可以保存年月日时分秒
  • TIMESTAMP:时间戳,保存UTC时间,可以精确到毫秒
例如
  • 准备一张表

    CREATE TABLE userinfo(
    	id INT,						id为整数类型,没有指定长度,默认长度为11
        name VARCHAR(30),			name为变长字符串类型,长度为30个字符
        gender CHAR(1),				gender为定长字符串,长度为1个字符
        birth DATETIME,				birth为日期类型,可以保存年月日时分秒
        salary DOUBLE(7,2)			salary为浮点数类型,可以保存5位整数和2位小数
    )
    
  • 插入日期类型时,DATETIME类型字段为例:可以以字符串形式插入,该字符串格式’yyyy-MM-dd hh:mm:ss’。其中大写M表示月,小写m表示分。都是数字。

    INSERT INTO userinfo
    (id,name,gender,birth,salary)
    VALUES
    (1,'张三','男','1992-08-02 20:55:33',5000.19)
    

    在这里插入图片描述

  • DATETIME类型插入数据时,可以忽略时分秒,忽略后默认值为0

    INSERT INTO userinfo
    (id,name,gender,birth,salary)
    VALUES
    (2,'李四','女','1997-03-15',8000)
    

    在这里插入图片描述

  • DATATIME累哦行插入数据时,不可以忽略年月日

    INSERT INTO userinfo
    (id,name,gender,birth,salary)
    VALUES
    (3,'王五','男','20:12:16')
    

    在这里插入图片描述

约束条件

我们可以为表施加特定的约束条件,那么只有满足该约束条件的操作才可以进行,否则会被数据库拒绝

约束的种类

  • 主键约束
  • 非空约束
  • 唯一性约束
  • 检查约束
  • 外键约束(实际开发中几乎不用)

主键约束

什么是主键:主键字段的值用来唯一表示该表中的一条记录。

可以作为主键的值要求:非空且唯一

  • 非空:在表中每条记录都要有该值
  • 唯一:表中每条记录该字段的值不可以重复
  • 符合上述两条要求的值就可以作为主键使用

主键约束就要求该字段必须是非空且唯一的,否则不允许进行操作

PRIMARY KEY
  • 当表中某个字段被施加主键约束后,该字段插入值或更新值时都要求不能为空且必须唯一,否则数据库拒绝操作
  • 一张表中只能有一个字段为主键约束
  • 通常主键字段的字段名为"ID"
例如
CREATE TABLE user1(
	id INT PRIMARY KEY,
	name VARCHAR(30),
	age INT(3)
);

INSERT INTO user1(id,name,age) 
VALUES(1,'张三',22),(2,'李四',33)

在这里插入图片描述

  • 主键字段不可以插入重复的值

    INSERT INTO user1(id,name,age) VALUES(1,'王五',44)
    

    在这里插入图片描述

  • 主键字段不可以插入NULL值

    INSERT INTO user1(id,name,age) VALUES(NULL,'王五',44)
    

    在这里插入图片描述

  • 插入记录时不可以忽略主键字段,除非主键字段有默认的生成方式(比如自增)

    INSERT INTO user1(name,age) VALUES('王五',44)
    

    在这里插入图片描述

  • 更新数据时,不可以将重复的值更新到主键字段上

    将李四的id改为1
    UPDATE user1
    SET id=1
    WHERE name='李四'
    

    在这里插入图片描述

  • 不可以将NULL值更新到主键字段上

    UPDATE user1
    SET id=NULL
    WHERE name='李四'
    

    在这里插入图片描述

  • 主键字段的值不会进行更新,通常插入数据时应有系统生成

自增

通常具有主键约束的字段都会为其添加自增,自增是数据库为字段生成值的一种机制。

AUTO_INCREMENT

AUTO_INCREMENT是一个关键字,当字段添加后具有自增特性。

例如
  • 创建时为字段添加自增

    CREATE TABLE user2(
    	id INT PRIMARY KEY AUTO_INCREMENT,
        name VARCHAR(30),
        age INT(3)
    );
    

    在这里插入图片描述

  • 修改表时,也可以为字段添加自增

    ALTER TABLE user1 CHANGE id id INT PRIMARY KEY AUTO_INCREMENT;
    

    在这里插入图片描述

    如果该字段已经具有主键约束,可以仅添加自增

    ALTER TABLE user1 CHANGE id id INT AUTO_INCREMENT;
    
  • 主键具有自增后,那么插入数据时可以忽略主键字段

    INSERT INTO user2(name,age) VALUES('张三',22);
    INSERT INTO user2(name,age) VALUES('李四',33);
    

    在这里插入图片描述

  • 插入时可以显示的插入NULL,此时还是会使用自增,并非将NULL值插入

    INSERT INTO user2(id,name,age) VALUES(NULL,'王五',36)
    

    在这里插入图片描述

    虽然可以这样写,但是不推荐这样的写法, 会出现语义不明确的问题

非空约束

NOT NULL为非空约束,当字段施加非空约束后,字段的值任何时候都不允许为NULL。

  • 插入数据时不可以将NULL值插入到该字段上
  • 更新数据时也不可以将NULL值更新
  • 一张表中可以有多个字段添加非空约束
例如
  • 创建表时为字段指定非空约束

    CREATE TABLE user3(
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	name VARCHAR(30) NOT NULL,
    	age INT(3)
    )
    

    在这里插入图片描述

  • 修改表时为字段添加非空约束

    修改user2表时为name字段添加非空约束
    ALTER TABLE user2 CHANGE name name VARCHAR(30) NOT NULL
    
  • 向具有非空约束字段插入数据时,不可以将NULL值插入

    INSERT INTO user3(name,age) VALUES(NULL,22);
    

    在这里插入图片描述

  • 插入数据时也不可以忽略具有非空约束的字段

    INSERT INTO user3(age) VALUES(22);
    
  • 不能将NULL值更新到具有非空约束的字段上

    INSERT INTO user3(name,age) VALUES('张三',22);
    
    UPDATE user3
    SET name=NULL			不可以将NULL值更新到name字段上
    WHERE name='张三'
    

唯一性约束

UNIQUE唯一性约束,该约束要求对应字段在整张表中的值是不可以重复的。一张表可以有多个字段添加唯一性约束。

例如
  • 创建一张表,为字段添加唯一性约束

    CREATE TABLE user4(
    	id INT PRIMARY KEY AUTO_INCREMENT,
        name VARCHAR(30) UNIQUE,				为name字段添加唯一性约束
        age INT(3)
    );
    

    在这里插入图片描述

  • 修改表时可以为字段添加唯一性约束

    注意:如果该字段具有非空约束,那么若仅指定唯一性约束时,会将非空约束取消

    ALTER TABLE user3 CHANGE name name VARCHAR(30) UNIQUE
    上述SQL执行后,user3表中name字段原本的非空约束会被取消
    

    如果需要保留非空约束,需要一并指定

    ALTER TABLE user3 CHANGE name name VARCHAR(30) NOT NULL UNIQUE;
    
  • 插入数据时不能将重复的值插入到具有唯一性约束的字段上

    INSERT INTO user4(name,age) VALUES('张三',22);		可以成功插入
    
    INSERT INTO user4(name,age) VALUES('张三',44);		失败,name字段值违反唯一性约束
    

    在这里插入图片描述

  • NULL值可以插入到具有唯一性约束的字段上,并且多条记录都可以是NULL

    INSERT INTO user4(name,age) VALUES(NULL,33);
    INSERT INTO user4(name,age) VALUES(NULL,44);
    

    NULL是不存在,因此不具有可比性,所以不存在重复的意思

  • 更新数据也不能将重复的值更新到具有唯一性约束的字段上,NULL除外

    INSERT INTO user4(name,age) VALUES('李四',33);
    
    UPDATE user4
    SET name='张三'					已经存在name为张三的记录,因此修改失败
    WHERE name='李四'		
    
    UPDATE user4
    SET name=NULL					 可以将NULL值更新
    WHERE name='李四'
    

检查约束

CHECK约束,该约束允许我们自定义约束条件,此时仅允许满足该条件的操作进行

例如
  • 创建一张表时为字段添加CHECK约束

    CREATE TABLE user5(
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	name VARCHAR(30),
    	age INT(3) CHECK(age>0 AND age<120)
    )
    约束要求age字段的值必须在0120之间
    
  • 插入数据时,值不能违背CHECK约束要求

    INSERT INTO user5(name,age) VALUES('张三',22);			成功
    		
    INSERT INTO user5(name,age) VALUES('李四',160);			失败
    
    

    在这里插入图片描述

  • 更新数据时也不能违背CHECK约束要求

    UPDATE user5
    SET age=0
    WHERE id=1
    

    在这里插入图片描述

外键约束

外键约束实际开发中几乎不用

导入SQL脚本文件

  • 鼠标右键选择tedu.sql文件,并进行配置

    在这里插入图片描述

  • 在弹出的配置窗口中点击"+"来设置数据库源,目的是在哪个数据库中执行这个tedu.sql脚本

    在这里插入图片描述

  • 选择我们配置的数据源后点击OK完成配置

    在这里插入图片描述

  • 再次鼠标右键选择tedu.sql文件选择运行这个文件

    在这里插入图片描述

  • 在弹出框中点击run按钮并等待执行完毕即可

    在这里插入图片描述

DQL语言-数据查询语言

DQL语言使用检索表中记录的语言

语法

											执行顺序
SELECT 					子句					6
FROM 					子句					1					
JOIN... ON ...			子句					2
WHERE 					子句					3
GROUP BY 				子句					4	
HAVING 					子句					5
ORDER BY 				子句					7
LIMIT 					子句					8

基础查询

语法
SELECT 字段1,字段2,... FROM1,2,...			检索表中每条记录中指定字段的内容

SELECT * FROM1,2,...					       检索表中每条记录所有字段的内容
  • SELECT 子句用于指明检索表中那些字段
  • FROM 子句用于指明数据来自哪些表
例如
USE tedu;				切换到tedu库,上面导入的tedu.sql文件后生成的库

查看所有老师信息
SELECT * FROM teacher

注意:实际开发中,当我们检索表中记录时,应当避免使用"SELECT * FROM 表",原因是,当我们使用"*"时数据库首先要查询内部的数据字典了解表中的字段情况后才可以进行查询工作,这个查询如果频繁进行则会降低查询效率。将来我们在java中书写SQL语句,因此就算是全字段查询,也应当将所有字段列出来

  • 检索表中指定字段的值

    查看所有老师的名字,工资,性别,职称
    SELECT name,salary,gender,title
    FROM teacher
    

    在这里插入图片描述

  • 查看所有学生的名字,年龄,性别,生日

    SELECT name,age,gender,birth
    FROM student
    

WHERE子句

WHERE子句用于添加过滤条件,在DQL语句中仅将满足过滤条件的记录查询出来

例如
  • 查看职称为"一级讲师"的老师的名字,职称,工资,年龄

    1:数据来自哪张表?		明确FROM子句:teacher表
    2:查询那些信息?		 明确SELECT子句:name,title,salary,age
    3:查询条件是什么?      明确WHERE子句:该记录title值为'一级讲师'   title='一级讲师'
    
    SELECT name,title,salary,age
    FROM teacher
    WHERE title='一级讲师'
    
    
    

    在这里插入图片描述

  • 查看除了"刘苍松"以外的所有老师的名字,工资,奖金,职位

    SELECT name,salary,comm,title
    FROM teacher
    WHERE name<>'刘苍松'
    
  • 查看职位是"大队长"的学生的名字,年龄,性别?

    SELECT name,age,gender
    FROM student
    WHERE job='大队长'
    
  • 查看年龄在30岁以上(含)的老师的名字,职称,工资,奖金

    SELECT name,title,salary,comm
    FROM teacher
    WHERE age>=30
    
  • 查看2层以上(含)都有那些班?列出班级名字,所在楼层

    SELECT name,floor
    FROM class
    WHERE floor>=2
    
连接多个条件
  • AND:“与”,都为真时才为真
  • OR:“或”,都为假时才为假
例如
  • 查看7岁的"大队长"都有谁?列出这些学生的名字,年龄,性别和职位

    SELECT name,age,gender,job
    FROM student
    WHERE job='大队长' AND age=7
    
  • 查看班级编号小于6的所有中队长都有谁?列明名字,年龄,性别,班级编号(class_id),职位

    SELECT name,age,gender,class_id,job
    FROM student
    WHERE class_id<6 AND job='中队长'
    
  • 查看所有一级讲师和三级讲师的名字,职称,工资?

    SELECT name,title,salary
    FROM teacher
    WHERE title='一级讲师' OR title='三级讲师'
    
  • 查看所有大队长,中队长和小队长的名字,性别,年龄和职位?

    SELECT name,gender,age,job
    FROM student
    WHERE job='大队长' OR job='中队长' OR job='小队长'
    
AND的优先级是高于OR的

若要提高OR的优先级,需要使用"()"括起来

  • 查看班级编号在6(含)以下的所有大队长和中队长的名字,年龄,性别,班级编号和职位

    SELECT name,age,gender,class_id,job
    FROM student
    WHERE class_id<=6 AND job='大队长' OR job='中队长'
    条件:  班级编号小于等于6的所有大队长  或  所有中队长(中队长没有班级编号要求)
    
    下面为正确写法
    SELECT name,age,gender,class_id,job
    FROM student
    WHERE class_id<=6 AND (job='大队长' OR job='中队长')
    
    
IN(列表)

IN(列表)表示字段的值在列表中。等于列表的其中之一。

例如
  • 查看所有大队长,中队长和小队长的名字,性别,年龄和职位?

    SELECT name,gender,age,job
    FROM student
    WHERE job='大队长' OR job='中队长' OR job='小队长'
    
    等价于
    
    SELECT name,gender,age,job
    FROM student
    WHERE job IN ('大队长','中队长','小队长')
    
  • 查看所有一级讲师,二级讲师,三级讲师的名字,职称,工资和性别

    SELECT name,title,salary,gender
    FROM teacher
    WHERE title IN ('一级讲师','二级讲师','三级讲师')
    
NOT IN(列表)

NOT IN(列表)表示字段值不在列表中。不能等于列表中任何一项

例如
  • 查看除一级讲师和二级讲师之外的所有老师的名字,职称,工资

    SELECT name,title,salary
    FROM teacher
    WHERE title<>'一级讲师' AND title<>'二级讲师'
    
    等价于
    
    SELECT name,title,salary
    FROM teacher
    WHERE title NOT IN('一级讲师','二级讲师')
    
  • 查看除大队长,中队长,小队长的其他学生的名字,职位,性别,年龄

    SELECT name,job,gender,age
    FROM student
    WHERE job NOT IN('大队长','中队长','小队长')
    
BETWEEN…AND…

BETWEEN n AND m用于判断字段值在两者区间,逻辑:>=n AND <=m。n是下限,m是上限。

例如
  • 查看工资在2000到5000之间的老师的名字,性别,年龄,工资

    SELECT name,gender,age,salary
    FROM teacher
    WHERE salary>=2000 AND salary<=5000
    
    等价于
    
    SELECT name,gender,age,salary
    FROM teacher
    WHERE salary BETWEEN 2000 AND 5000
    
    
  • 查看年龄在7到10岁的学生的名字,性别,年龄

    SELECT name,gender,age
    FROM student
    WHERE age BETWEEN 7 AND 10
    
  • 查看年龄在20到35之间的男老师都有谁?列出名字,性别,年龄,职称

    SELECT name,gender,age,title
    FROM teacher
    WHERE age BETWEEN 20 AND 35 
    AND gender='男'
    
  • 查看所有在3-5层的班级都有哪些?列出班级名称和所在楼层

    SELECT name,floor
    FROM class
    WHERE floor BETWEEN 3 AND 5
    
NOT BETWEEN… AND … 用于判断不在区间范围内
  • 查看年龄在7到10岁以外的学生的名字,性别,年龄

    SELECT name,gender,age
    FROM student
    WHERE age NOT BETWEEN 7 AND 10
    
DISTINCT

去除重复行

DISTINCT用在SELECT子句中,并且需要紧跟在SELECT关键字之后

可以将结果集中指定字段重复的记录去除

例如
  • 查看老师都有哪些职称?

    SELECT DISTINCT title
    FROM teacher
    
    将结果集中title字段值重复的记录去除重复行
    
  • 查看学生都有哪些职位?

    SELECT DISTINCT job
    FROM student
    
多字段去重

DISTINCT后面可以指定多个字段,去重规则:这几个字段值的组合重复的去除重复行

例如

  • 查看各年龄段的学生都有哪些职位

    SELECT DISTINCT age,job		记录中age与job字段值相同的记录去除重复行
    FROM student			   
    上述SQL中原本可以查询出多个7岁的大队长,但是结果集中仅列出一行
    
综合练习
题干
1.查看负责课程编号(subject_id)1的男老师都有谁?
2.查看工资高于5000的女老师都有谁?
3.查看工资高于5000的男老师或所有女老师的工资?
4.查看所有9岁学生的学习委员和语文课代表都是谁?
5.查看工资在600010000之间的老师以及具体工资?
6.查看工资在40008000以外的老师及具体工资?
7.查看老师负责的课程编号都有什么?
8.查看所有女老师的职称都是什么?
9.查看7-10岁的男同学的职位都有哪些?
10.查看一级讲师和二级讲师的奖金(comm)是多少?
11.查看除老板和总监的其他老师的工资和奖金是多少?
12.查看'3年级2班''5年级3班'在那层楼?
答案
1.查看负责课程编号(subject_id)1的男老师都有谁?
  SELECT name,gender,salary
  FROM teacher
  WHERE subject_id=1 AND gender='男'
  
2.查看工资高于5000的女老师都有谁?
  SELECT name,gender,salary
  FROM teacher
  WHERE salary>5000 AND gender='女'

3.查看工资高于5000的男老师或所有女老师的工资?
  SELECT name,gender,salary
  FROM teacher
  WHERE salary>5000 AND gender='男'
  OR gender='女'
  
4.查看所有9岁学生的学习委员和语文课代表都是谁?
  SELECT name,age,job
  FROM student
  WHERE age=9 AND job IN('学习委员','语文课代表')
  
5.查看工资在600010000之间的老师以及具体工资?
  SELECT name,salary
  FROM teacher
  WHERE salary BETWEEN 6000 AND 10000
  
6.查看工资在40008000以外的老师及具体工资?
  SELECT name,salary
  FROM teacher
  WHERE salary NOT BETWEEN 4000 AND 8000
  
7.查看老师负责的课程编号都有什么?
  SELECT DISTINCT subject_id
  FROM teacher
  
8.查看所有女老师的职称都是什么?
  SELECT DISTINCT title
  FROM teacher
  WHERE gender='女'
  
9.查看7-10岁的男同学的职位都有哪些?
  SELECT DISTINCT job
  FROM student
  WHERE age BETWEEN 7 AND 10
  AND gender='男'
  
10.查看一级讲师和二级讲师的奖金(comm)是多少?
  SELECT name,comm
  FROM teacher
  WHERE title IN('一级讲师','二级讲师')

11.查看除老板和总监的其他老师的工资和奖金是多少?
  SELECT name,title,salary,comm
  FROM teacher
  WHERE title NOT IN('老板','总监')

12.查看'3年级2班''5年级3班'在那层楼?
  SELECT name,floor
  FROM class
  WHERE name IN('3年级2班','5年级3班')
LIKE模糊查询

LIKE可以进行模糊查询,有两个通配符

  • _:下划线表示一个字符
  • %:表示任意个字符(0-多个)
例如
LIKE '%X%'					表示字符串中含有字符X(X之前和之后可以有任意个字符)
LIKE '_X%'					表示字符串中第二个字符是X
LIKE 'X%'					表示字符串以X开始
LIKE '%X'					表示字符串以X结束
LIKE '%X_Y'					表示字符串倒数第三个字符是X以及最后一个字符是Y
LIKE '__X%'					表示字符串第三个字符是X
LIKE 'X%Y'					表示字符串以X开始并且以Y结尾
  • 查看名字中含有’晶’的老师都有谁?

    SELECT name,gender,salary
    FROM teacher
    WHERE name LIKE '%晶%'
    
  • 查看姓张的学生都有谁?

    SELECT name,age,gender
    FROM student
    WHERE name LIKE '张%'
    
  • 查看三个字名字中第二个字是’平’的学生都有谁

    SELECT name,age,gender
    FROM student
    WHERE name LIKE '_平_'
    
  • 查看最后一个字是’晶’的老师都有谁?

    SELECT name,age,gender
    FROM teacher
    WHERE name LIKE '%晶'
    
练习

题干

1.查询名字姓"李"的学生姓名
2.查询名字中包含"江"的学生姓名
3.查询名字以"郭"结尾的学生姓名
4.查询9-12岁里是"课代表"的学生信息
5.查询名字第二个字是"苗"的学生信息
6.查询姓"邱"的课代表都是谁?
7.查看哪些学生是课代表?列出他的名字和职位
8.查看所有的2班都在哪层?

答案

1.查询名字姓"李"的学生姓名
  SELECT name,gender,age
  FROM student
  WHERE name LIKE '李%'
  
2.查询名字中包含"江"的学生姓名
  SELECT name,age,gender
  FROM student
  WHERE name LIKE '%讲%'
  
3.查询名字以"郭"结尾的学生姓名
  SELECT name,age,gender
  FROM student
  WHERE name LIKE '%郭'
   
4.查询9-12岁里是"课代表"的学生信息
  SELECT name,age,job
  FROM student
  WHERE age BETWEEN 9 AND 12
  AND job LIKE '%课代表'
 
5.查询名字第二个字是"苗"的学生信息
  SELECT name,age,gender
  FROM student
  WHERE name LIKE '_苗%'
  
6.查询姓"邱"的课代表都是谁?
  SELECT name,job,age
  FROM student
  WHERE name LIKE '邱%'
  AND job LIKE '%课代表'
 
7.查看哪些学生是课代表?列出他的名字和职位
  SELECT name,job
  FROM student
  WHERE job LIKE '%课代表'
  
8.查看所有的2班都在哪层?
  SELECT name,floor
  FROM class
  WHERE name LIKE '%2班'
NULL值判断

在数据库中,所有字段默认值都是NULL。NULL表示不存在,空的。

NULL不能算作一个值,应该是一种状态。

判断NULL时

  • IS NULL:判断一个字段的值是否为NULL
  • IS NOT NULL:判断一个字段的值是否非空
  • 不可以用=或<>来判断NULL。
例如
  • 哪些老师没有奖金(奖金为null)

    SELECT name,salary,comm
    FROM teacher
    WHERE comm=null
    上述SQL查询不出任何记录,因为没有任何一个值可以和NULL作等值比较
    
    
    SELECT name,salary,comm
    FROM teacher
    WHERE comm IS NULL
    
    
  • 哪些老师奖金不为空

    SELECT name,salary,comm
    FROM teacher
    WHERE comm IS NOT NULL
    
    

ORDER BY子句

ORDER BY子句可以将结果集按照指定的字段值升序或降序排序

  • ORDER BY 字段(可以多字段) ASC:按照指定字段值升序将结果集排序
  • ORDER BY 字段 DESC:按照指定字段降序排序结果集
  • 不指定排序方式时,默认为升序
  • 如果按照多字段排序,规则
    • 首先将结果集按照第一个字段的排序方式对结果集排序
    • 当第一个字段值存在重复的记录时,再将这几条记录按照第二个字段值的排序方式排序
    • 优先级:ORDER BY后第一个字段为最优先,以此类推
例如
  • 查看老师的工资,工资从多到少

    SELECT name,salary
    FROM teacher
    ORDER BY salary DESC
    
  • 查看老师的奖金排名

    SELECT name,salary,comm
    FROM teacher
    ORDER BY comm DESC
    
  • 查看学生的生日,按照从远到近

    SELECT name,birth
    FROM student
    ORDER BY birth
    
    日期比较大小的规则:远小近大。  距离现在越近的日期越大
    
  • 查看7-10岁的学生信息,学生按照年龄从大到小排序(同年龄的看生日)

    SELECT name,age,birth
    FROM student
    WHERE age BETWEEN 7 AND 10
    ORDER BY birth
    
  • 查看老师的工资和奖金,首先按照奖金的升序,再按照工资的降序

    SELECT name,salary,comm
    FROM teacher
    ORDER BY comm ASC,salary DESC 
                  ^^^
                  ASC可以不写
        
        
    SELECT name,salary,comm
    FROM teacher
    ORDER BY comm,salary DESC 
    注:NULL在MariaDB中被视作最小值
    

    在这里插入图片描述

分页查询

分页查询就是将一条DQL语句的查询结果集分段查询出来。

使用场景

当一条DQL语句查询的结果集记录数过多时,就应当使用分页查询。

例如:淘宝

当我们所有一件商品,实际可能查询出上万条记录,但是淘宝仅将前面的30-50条记录传给我们,然后通过我们点击下一页再去查看后30-50条记录。这种现象就是分页查询。

优点:占用资源少,减少了网络传输的数据量,提高了传输效率。

方言

分页查询是方言,在SQL92标准中没有涉及到分页的语法定义,因此不同的数据库SQL写法完全不同。

MySQL和MariaDB中的分页是使用LIMIT子句实现的

语法
SELECT ...
  FROM ...
  WHERE ...
  ORDER BY ...
  LIMIT M,N 		M和N是两个整数
  • M:表示跳过结果集中多少条记录

  • N:表示从M位置开始查询出多少条记录

  • 分页中常见的参数

    • 当前的页数
    • 每页显示多少条记录
  • 分页公式

    • M:跳过结果集中条目数,计算方式:(当前页数-1)*每页显示的条目数
    • N:每页显示多少条
例如
  • 查看老师工资的前5名?

    按照老师工资的降序查询数据。分页查询,一页显示5条,显示第一页
    1:首先按照工资降序查询老师信息
    2:分页查询
      M:(1-1)*5	   (页数-1)*N
      N:5
    SELECT name,salary
    FROM teacher
    ORDER BY salary DESC
    LIMIT 0,5       
    
  • 查看老师奖金信息,按照降序排序后,每页显示3条,显示第5页?

    M:(5-1)*3   M:12
    N:3         N:3
    
    SELECT name,comm
    FROM teacher
    ORDER BY comm DESC
    LIMIT 12,3
    

在DQL语句中可以使用函数或表达式

在SELECT子句中使用表达式
例如
  • 查看老师的工资和年薪分别是多少?

    SELECT name,salary,salary*12
    FROM teacher
    
在SELECT子句中使用函数

数据库中都内置了很多函数,但是不同的数据库函数不完全一样。

IFNULL函数

定义

IFNULL(arg1,arg2)

作用

  • 如果arg1的值不为NULL,函数返回arg1的值

  • 若arg1的值为NULL,则返回返回arg2的值

  • 该函数的作用:将一个NULL替换为一个非NULL值

  • 每部逻辑

    用java代码理解逻辑
    IFNULL(arg1,arg2){
        if(arg1!=null){
            return arg1;
        }else{
            return arg2
        }
    }
    

例如

  • 查看每个老师的工资,奖金,工资+奖金分别是多少?

    SELECT name,salary,comm,salary+comm
    FROM teacher
    在数据库中,任何数据和NULL运算,结果都是NULL
    

    在这里插入图片描述

    可以先将NULL替换为一个非NULL值在进行计算
    SELECT name,salary,comm,salary+IFNULL(comm,0)
    FROM teacher
    

在这里插入图片描述

  • 查看每个老师的奖金,以及全年奖金?

    SELECT name,comm,IFNULL(comm*12,0)
    FROM teacher
    或
    SELECT name,comm,IFNULL(comm,0)*12
    FROM teacher
    
在WHERE子句中使用表达式
例如
  • 查看哪些老师的年薪高于60000,并按照工资从高到低

    SELECT name,salary,salary*12
    FROM teacher
    WHERE salary*12>60000
    ORDER BY salary DESC
    
在WHERE子句中使用函数
例如
  • 查看哪些老师的奖金少于3000?

    SELECT name,comm
    FROM teacher
    WHERE comm<3000
    
    NULL不仅不能作等值判断,>,>=,<,<=都不能进行判断,都得不到正确结果
    
    SELECT name,comm
    FROM teacher
    WHERE IFNULL(comm,0)<3000		先将NULL换成0,再进行判断
    
    

别名

在SQL语句中可以为字段,表等取别名

  • 在SELECT子句中我们通常会为函数或表达式取别名,使得在结果集中对于该字段的描述更清晰直观
  • 在FROM子句中为表取别名,后期关联查询中可以更简化的指明表
语法
  • 字段 [AS] 别名
  • 字段 [AS] ‘别名’
  • 字段 [AS] “别名”
例如
  • 查看老师的工资和年薪

    SELECT name,salary,salary*12
    FROM teacher
    

    结果集中直接用函数或表达式作为字段名,不直观

    在这里插入图片描述

    SELECT name,salary,salary*12 annusal
    FROM teacher
    

在这里插入图片描述

  • 字段名 AS 别名

    SELECT name,salary,salary*12 AS annusal
    FROM teacher
    
  • 别名可以用引号

    SELECT name,salary,salary*12 AS 'annusal'
    FROM teacher
    
    SELECT name,salary,salary*12 AS "annusal"
    FROM teacher
    
    SELECT name,salary,salary*12 'annusal'
    FROM teacher
    
    SELECT name,salary,salary*12 "annusal"
    FROM teacher
    
  • 当别名中含有空格,应当添加引号

    SELECT name,salary,salary*12 annu sal
    FROM teacher
    

    在这里插入图片描述

    SELECT name,salary,salary*12 'annu sal'
    FROM teacher
    
  • 当别名中含有SQL关键字时,也应当添加引号

    SELECT name,salary,salary*12 'from'
    FROM teacher
    

综合练习

题干
1.查询所有10岁学生的生日,按生日对应的年纪从大到小.
2.查询8岁同学中名字含有"苗"的学生信息
3.查询负责课程编号12号且工资高于6000的老师信息
4.查询10岁以上的语文课代表和数学课代表
5.查询不教课程编号1的老师信息,按照工资降序排序
6.查询没有奖金的老师信息
7.查询所有老师的奖金,并按照奖金降序排序
8.查看工资高于8000的老师负责的课程编号都有那些?
9.查看全校年龄最小学生的第6-10
答案
1.查询所有10岁学生的生日,按生日对应的年纪从大到小.
  SELECT name,birth,age
  FROM student
  WHERE age=10
  ORDER BY birth
  
2.查询8岁同学中名字含有"苗"的学生信息
  SELECT name,age
  FROM student
  WHERE age=8 
  AND name LIKE '%苗%'

3.查询负责课程编号12号且工资高于6000的老师信息
  SELECT name,subject_id,salary
  FROM teacher
  WHERE subject_id IN (1,2)
  AND salary>6000
  
4.查询10岁以上的语文课代表和数学课代表
  SELECT name,age,job
  FROM student
  WHERE age>10
  AND job IN ('语文课代表','数学课代表')
 
5.查询不教课程编号1的老师信息,按照工资降序排序
  SELECT name,subject_id,salary
  FROM teacher
  WHERE subject_id<>1
  ORDER BY salary DESC
  
6.查询没有奖金的老师信息
  SELECT name,comm
  FROM teacher
  WHERE comm IS NULL
  
  0也属于没有奖金
  SELECT name,comm
  FROM teacher
  WHERE IFNULL(comm,0)=0

7.查询所有老师的奖金,并按照奖金降序排序
  SELECT name,comm
  FROM teacher
  ORDER BY comm DESC
 
8.查看工资高于8000的老师负责的课程编号都有那些?
  SELECT DISTINCT subject_id
  FROM teacher
  WHERE salary>8000

9.查看全校年龄最小学生的第6-10SELECT name,birth,age
  FROM student
  ORDER BY birth DESC
  LIMIT 5,5
  

上述练习第9题遇到的问题:

以下是按照生日降序排序后的前几条数据

在这里插入图片描述

按照分页查询6-10条记录时,得到的数据,和不添加分页时排序出的6-10条不一致

在这里插入图片描述

原因

MySQL官方文档上做出了解释

If multiple rows have identical values in the ORDER BY columns, the server is
free to return those rows in any order, and may do so differently depending on
the overall execution plan. In other words, the sort order of those rows is
nondeterministic with respect to the nonordered columns.
One factor that affects the execution plan is LIMIT, so an ORDER BY query with
and without LIMIT may return rows in different orders.
翻译:
如果多行在 ORDER BY 列中具有相同的值,则服务器可以自由地以任意顺序返回这些行,并且可能会根据整
体执行计划以不同的方式返回这些行。换句话说,这些行的排序顺序对于无序列是不确定的。
影响执行计划的一个因素是 LIMIT,因此带有和不带 LIMIT 的 ORDER BY 查询可能会返回不同顺序的

解决办法

查询时,无论带不带LIMIT若想保证具有相同值的字段排序结果集顺序一致时,需要再添加一个排序字段,保证该字段的值没有重复的即可,通常可以让ID参与排序,因为ID字段值一定不同

SELECT name,birth
FROM student
ORDER BY birth DESC,id
LIMIT 5,5

聚合函数

聚会函数又称为多行函数,分组函数。

作用:将多条记录按照指定的字段进行统计并得出一个结果

聚合函数分类
  • MIN:统计指定字段的最小值
  • MAX:统计指定字段的最大值
  • AVG:统计指定字段的平均值
  • SUM:统计指定字段值的总和
  • COUNT:不是对字段值的统计,是对记录数的统计。
注意事项
  • 聚合函数忽略NULL值,尤其在AVG,COUNT上比较明显
  • MIN,MAX,AVG,SUM是对值的统计,COUNT是对记录数的统计
例如
  • 查看老师的平均工资是多少?

    1:先将参与统计的记录查询出来(编写相应SQL)
    2:在该SQL上添加聚合函数进行统计
    
    1:统计数据-所以老师的工资
    SELECT salary
    FROM teacher
    
    2:在该SQL基础上添加聚合函数
    SELECT AVG(salary)
    FROM teacher
    
  • 查看老师的最高工资,最低工资,平均工资和工资总和都是多少?

    1:查询出参与统计的数据
    SELECT salary,salary,salary,salary
    FROM teacher
    
    2:分别为字段添加聚合函数
    SELECT MAX(salary),MIN(salary),AVG(salary),SUM(salary)
    FROM teacher
    
  • 查看负责课程编号1的老师的平均工资是多少?

    1:列出待统计的数据-课程编号1的老师工资
    SELECT salary
    FROM teacher
    WHERE subject_id=1
    
    2:添加聚合函数
    SELECT AVG(salary)
    FROM teacher
    WHERE subject_id=1
    
    
  • 查看一共多少位老师

    1:列出所有老师
    SELECT name FROM teacher
    
    2:添加聚合函数
    SELECT COUNT(name) FROM teacher		当统计记录数时,指定字段值不应有NULLSELECT COUNT(comm) FROM teacher     不为NULL的comm字段值共17条记录
    由于聚合函数忽略NULL值,因此不建议使用可能包含NULL的字段进行COUNT统计。
    
    
    SELECT 1 FROM teacher	       查询teacher表所有记录,每条记录列出'1'
    SELECT COUNT(1) FROM teacher   统计该字段值共多少行记录('1'一定不为NULL)
    
    
    SELECT COUNT(*) FROM teacher   DBMS(数据库管理系统)几乎都对COUNT(*)作了优化,推荐写法
    
    
    
    
练习
题干
1.查看所有老师的平均奖金和奖金总和是多少?
2.查看负责课程编号2的老师共多少人?
3.查看班级编号(class_id)1的学生有多少人?
4.查看全校学生生日最大的是哪天?
5.查看11岁的课代表总共多少人?
6.姓张的学生有多少人?
7.工资高于5000的老师中最低工资是多少?
8.4层有几个班?
9.老师中"总监"的平均工资是多少?
答案
1.查看所有老师的平均奖金和奖金总和是多少?
  -列出所有老师的奖金(NULL替换为0)
  SELECT IFNULL(comm,0) FROM teacher
  -添加聚合函数
  SELECT AVG(IFNULL(comm,0)),SUM(comm)
  FROM teacher
  
2.查看负责课程编号2的老师共多少人?
  SELECT COUNT(*)
  FROM teacher
  WHERE subject_id=2
  
3.查看班级编号(class_id)1的学生有多少人?
  SELECT COUNT(*)
  FROM student
  WHERE class_id=1
  
4.查看全校学生生日最大的是哪天?
  SELECT MIN(birth)
  FROM student
  
5.查看11岁的课代表总共多少人?
  SELECT COUNT(*)
  FROM student
  WHERE age=11 
  AND job LIKE '%课代表'
  
6.姓张的学生有多少人?
  SELECT COUNT(*)
  FROM student
  WHERE name LIKE '张%'

7.工资高于5000的老师中最低工资是多少?
  SELECT MIN(salary)
  FROM teacher
  WHERE salary>5000

8.4层有几个班?
  SELECT COUNT(*)
  FROM class
  WHERE floor=4

9.老师中"总监"的平均工资是多少?
  SELECT AVG(salary)
  FROM teacher
  WHERE title='总监'

GROUP BY子句

GROUP BY子句用于对结果集按照指定字段值相同的记录分组。配合聚合函数可以做到组内统计。

  • GROUP BY子句一定是配合聚合函数的,如果SELECT子句没有聚合函数,不会使用GROUP BY

  • 在SELECT子句中凡是不在聚合函数中的字段都应当出现在GROUP BY子句中

  • GOURP BY子句可以按照多字段分组,哪些字段值都相同的记录被看作一组

按照单字段分组
例如
  • 查看每种职位的老师平均工资是多少?

    SELECT AVG(salary),title    title不在聚合函数中,但是在GROUP BY中因此可以出现在SELECT子句中
    FROM teacher
    GROUP BY title		        结果集中title字段值相同的记录会被划分为一组
    

    在这里插入图片描述

  • 查看每个班级各多少人?

    SELECT COUNT(*),class_id
    FROM student
    GROUP BY class_id				class_id值一样的学生是一个班的
    
  • 查看学生每种职位各多少人,以及最大生日和最小生日?

    SELECT COUNT(*) '人数',
           MIN(birth) '最大生日',
           MAX(birth) '最小生日',
           job
    FROM student
    GROUP BY job
    
按照多字段分组

GROUP BY子句后指定多个字段,结果集中这些字段值都相同的记录会被划分为一组

例如
  • 查看同班级同性别的学生分别多少人?

    SELECT COUNT(*),class_id,gender
    FROM student
    GROUP BY class_id,gender			同班级同性别的记录被划分为一组
    
  • 查看每个班每种职位各多少人?

    SELECT COUNT(*),class_id,job
    FROM student
    GROUP BY class_id,job
    
    
将结果集按照聚合函数的统计结果排序
例如
  • 查看每个科目老师的平均工资排名?

    SELECT AVG(salary),subject_id
    FROM teacher
    GROUP BY subject_id
    ORDER BY AVG(salary) DESC
    
    通常会将聚合函数添加别名,ORDER BY子句中按照别名排序即可(好的书写习惯)
    SELECT AVG(salary) avg_sal,subject_id
    FROM teacher
    GROUP BY subject_id
    ORDER BY AVG(salary) DESC
    
    

HAVING子句

HAVING子句是紧跟在GROUP BY子句之后的,用于添加条件过滤分组的

问题
  • 查看每个科目老师的平均工资?但是仅查看平局工资高于6000的那些.

    SELECT AVG(salary),subject_id
    FROM teacher
    WHERE AVG(salary)>6000
    GROUP BY subject_id
    

    在这里插入图片描述

错误

聚合函数不能出现在WHERE子句中的

原因

过滤时机不对:

  • WHERE的过滤时机:在检索表中记录时,逐行扫描数据,将满足WHERE条件的记录生成结果集
  • 聚合函数作为过滤条件,前提
    • 聚合函数是对结果集进行统计的,因此前提是要现有结果集
    • 而WHER是产生结果集时过滤的,因此它是在聚合函数使用前发挥作用的
解决办法

使用HAVING子句

例如
  • 查看每个科目老师的平均工资?但是仅查看平局工资高于6000的那些.

    								执行顺序
    SELECT AVG(salary),subject_id	  5		最终在保留的分组中统计结果
    FROM teacher                      1		确定数据来自哪张表
    [WHERE] 				          2		确定结果集中的记录
    GROUP BY subject_id               3		确定结果集的分组				可以分为6HAVING AVG(salary)>6000           4		确定分组的取舍					 仅有2组满足要求
    
  • 查看每个科目老师的平均工资,前提是该科目老师最高工资要超过9000

    								执行顺序
    SELECT AVG(salary),subject_id		4		2个分组中统计结果
    FROM teacher						1		确定数据来自哪张表
    GROUP BY subject_id					2		确定分组(可以分为6)
    HAVING MAX(salary)>9000				3		确定保留哪些分组(保留2)
    
HAVING与WHERE的区别
  • 过滤时机不同,WHERE先过滤,HAVING后过滤
  • WHERE用于确定结果集的记录
  • HAVING用于确定保留哪些分组
  • WHERE不可以使用聚合函数作为过滤条件,HAVING可以

综合练习

题干
1:查看科目老师的工资总和是多少?前提是该科老师的平均奖金要高于4000.
2:查看各科目男老师的平均工资是多少?前提是该科目老师最低工资高于4000.
3:查看班级编号小于6的每个班各多少人?
4:查看3层一共多少个班?
5:查看工资低于8000的老师的平均工资是多少?
6:查看班级人数超过60人的班级中年纪最大的同学生日是多少?
7:查看教课程编号1的老师的平均年龄是多少?
8:查看同一科目平均年龄超过35岁的老师中最小年龄是多少?
9:查看同职称人数超过4人的老师的平均工资是多少?
答案
1:查看科目老师的工资总和是多少?前提是该科老师的平均奖金要高于4000.
  SELECT SUM(salary),subject_id
  FROM teacher
  GROUP BY subject_id
  HAVING AVG(comm)>4000
  
2:查看各科目男老师的平均工资是多少?前提是该科目老师最低工资高于4000.
  SELECT AVG(salary),subject_id
  FROM teacher
  WHERE gender='男'
  GROUP BY subject_id
  HAVING MIN(salary)>4000
  
3:查看班级编号小于6的每个班各多少人?
  SELECT COUNT(*),class_id
  FROM student
  WHERE class_id<6
  GROUP BY class_id
  
4:查看3层一共多少个班?
  SELECT COUNT(*)
  FROM class
  WHERE floor=3
  
5:查看工资低于8000的老师的平均工资是多少?
  SELECT AVG(salary)
  FROM teacher
  WHERE salary<8000

6:查看班级人数超过60人的班级中年纪最大的同学生日是多少?
  SELECT MIN(birth) max_age,class_id
  FROM student
  GROUP BY class_id
  HAVING COUNT(*)>60
  
7:查看教课程编号1的老师的平均年龄是多少?
  SELECT AVG(age)
  FROM teacher
  WHERE subject_id=1
  
8:查看同一科目平均年龄超过35岁的老师中最小年龄是多少?
  SELECT MIN(age) min_age,subject_id
  FROM teacher
  GROUP BY subject_id
  HAVING AVG(age)>35
  
9:查看同职称人数超过4人的老师的平均工资是多少?
  SELECT AVG(salary),title
  FROM teacher
  GROUP BY title
  HAVING COUNT(*)>4
  

子查询(SubQuery)

概念

嵌套在其他SQL语句中的一条DQL语句,这条DQL语句就称为子查询

应用查询
  • 在DQL语句中使用
    • 在SELECT子句中使用子查询,将该查询结果当作一个字段列在外层查询的结果集中
    • 在FROM子句中使用,将该查询结果集当作一张表使用(视图)
  • 在DML语句中使用
    • 在增删改中,基于该查询结果集对表中数据进行操作
  • 在DDL语句中使用
    • 将该查询结果集当作一张表创建出来
    • 将查询结果集当作视图进行创建
子查询的分类
  • 单行单列子查询:查询结果集就是一个值,常用在DML,DQL中

  • 多行单列子查询:查询结果集有多个值,常用在DML,DQL中

  • 多列子查询:多列子查询(无论单行多行)通常是当作一张表使用。在DQL,DDL中应用

在DQL中使用子查询
单行单列子查询

例如

  • 查看比范传奇工资高的老师都有谁?

    1:未知条件-范传奇的工资是多少?
    SELECT salary FROM teacher WHERE name='范传奇'  ===>查询结果:3000
    
    2:查看谁的工资高于3000即可
    SELECT salary,name FROM teacher WHERE salary>3000
    
    用java的编码思想
    int sal = SELECT salary FROM teacher WHERE name='范传奇';  sal=3000;
    SELECT salary,name FROM teacher WHERE salary>sal
    
    如果写成一句:
    SELECT salary,name 
    FROM teacher 
    WHERE salary>SELECT salary FROM teacher WHERE name='范传奇'
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                 将该查询语句的结果当作外层查询语句的一个过滤条件
                 那这个查询语句就是嵌套在其他查询语句中的,它就是子查询
                 
    SQL语言语法要求,子查询被嵌套时应当使用"()"括起来:
    SELECT salary,name 
    FROM teacher 
    WHERE salary>(SELECT salary FROM teacher WHERE name='范传奇')
    
    
  • 查看哪些老师的工资是高于平均工资的?

    1:未知条件:平均工资是多少
    SELECT AVG(salary) FROM teacher
    
    2:查看高于平均工资的
    SELECT name,salary
    FROM teacher
    WHERE salary>(SELECT AVG(salary) FROM teacher)
    
  • 查看和’李费水’在同一个班的学生都有谁?

    1:查看李费水的班级编号是多少
    SELECT class_id FROM student WHERE name='李费水'
    
    2:进行查询
    SELECT name,class_id
    FROM student
    WHERE class_id=(SELECT class_id FROM student WHERE name='李费水')
    
  • 查看和’3年级2班’在同一层的班级都有哪些?列出班级编号,名字,楼层

    SELECT id,name,floor
    FROM class
    WHERE floor=(SELECT floor FROM class WHERE name='3年级2班')
    
  • 查看3年级2班的学生都有谁?

    学生表有一个字段是class_id记录了每个学生所在班级的id。而这个id就是class表中对应班级的id值。
    1:查询'3年级2班'的id是多少?
      SELECT id FROM class WHERE name='3年级2班'
    
    2:哪个学生的class_id与'3年级2班'的id一致
      SELECT name,class_id
      FROM student
      WHERE class_id=(SELECT id FROM class WHERE name='3年级2班')
    
  • 查看教语文的老师都有谁?

    1:语文科目的id是多少?
    SELECT id FROM subejct WHERE name='语文'
    
    2:查看哪个老师的subject_id与'语文'的id一致
    SELECT name,subject_id
    FROM teacher
    WHERE subject_id=(SELECT id FROM subejct WHERE name='语文')
    
  • 查看工资最高的老师的工资和奖金是多少?

    1:最高工资是多少?
    SELECT MAX(salary) FROM teacher
    
    2:谁的工资与上述DQL一致
    SELECT name,salary,comm
    FROM teacher
    WHERE salary=(SELECT MAX(salary) FROM teacher)
    
多行单列子查询

多行单列子查询可以同时查询出多个值

  • 如果进行等值判断时,要配合IN,NOT IN使用
  • 如果进行关系运算(>,>=,<,<=)
    • >ANY(列表):大于列表其中一个值即可(>最小的)
    • <ANY(列表):小于列表其中一个即可(<最大的)
    • >ALL(列表):大于列表中所有值(>最大的)
    • <ALL(列表):小于列表中所有值(<最小的)

例如

  • 查看与"祝雷"和"李费水"在同一个班的学生都有谁?

    1:未知条件-祝雷,李费水的班级编号分别是多少?
    SELECT class_id FROM student WHERE name IN('祝雷','李费水')
    
    2:查询谁的班级编号与其中之一一致
    SELECT name,class_id
    FROM student
    WHERE class_id IN(SELECT class_id 
                      FROM student 
                      WHERE name IN('祝雷','李费水'))
    

    多行单列子查询不能使用"="判断,会出现错误

    在这里插入图片描述

  • 查看不与’范传奇’和’王克晶’教同一课的老师都有谁?

    SELECT name,subject_id
    FROM teacher
    WHERE subject_id NOT IN(SELECT subject_id
                            FROM teacher
                            WHERE name IN ('王克晶','范传奇'))
    
  • 查看比教科目2和科目4老师工资都高的老师都有谁?

    1:未知条件-这两个科目的老师工资是多少?
    SELECT salary FROM teacher WHERE subject_id IN(2,4)
    
    2:查看高于他们所有
    SELECT salary,name
    FROM teacher
    WHERE salary>ALL(SELECT salary FROM teacher WHERE subject_id IN(2,4))
    
    
    另一种思路
    SELECT MAX(salary) FROM teacher WHERE subject_id IN(2,4)
    
    SELECT name,salary
    FROM teacher
    WHERE salary>(SELECT MAX(salary) 
                  FROM teacher 
                  WHERE subject_id IN(2,4))
    
    
在DML语句中使用子查询

在增删改操作中使用子查询

例如
  • 给与’范传奇’负责同一科目的所有老师工资涨500

    UPDATE teacher
    SET salary=salary+500
    WHERE subject_id=(SELECT subject_id 
                      FROM teacher 
                      WHERE name='范传奇')
                      
    数据还原一下
    UPDATE teacher
    SET salary=salary-500
    WHERE subject_id=(SELECT subject_id 
                      FROM teacher 
                      WHERE name='范传奇')
    
  • 将王克晶的工资改为与范传奇一样

    UPDATE teacher
    SET salary=(SELECT salary FROM teacher WHERE name='范传奇')
    WHERE name='王克晶'
    
    还原数据
    UPDATE teacher
    SET salary=8000
    WHERE name='王克晶'
    
  • 将与范传奇教同一科目的老师删除

    不执行,理解即可
    DELETE FROM teacher
    WHERE subjct_id=(SELECT subject_id FROM teacher WHERE name='范传奇')
    
在DDL语句中使用子查询

在DDL中可以将一个查询结果集当作一张表

例如
  • 创建一张表,该表中记录了每个科目老师的工资情况,要求展示:最高,最低,总和和平均工资以及该科目 id

    CREATE TABLE salary_info_table(
    	subject_id INT,
        max_salary INT,
        min_salary INT,
        sum_salary INT,
        avg_salary INT
    )
    INSERT ...
    INSERT ...
    INSERT ...
    
    
    
    正确做法:
    1:先将该表的数据查询出来
    SELECT subject_id,
           MIN(salary) min_salary,
           MAX(salary) max_salary,
           SUM(salary) sum_salary,
           AVG(salary) avg_salary
    FROM teacher
    GROUP BY subject_id
    
    2:希望将上述DQL语句的查询结果集当作一张表创建出来
    CREATE TABLE salary_info_table
    AS
    SELECT subject_id,
           MIN(salary) min_salary,
           MAX(salary) max_salary,
           SUM(salary) sum_salary,
           AVG(salary) avg_salary
    FROM teacher
    GROUP BY subject_id
    
  • 可以将结果集当作视图创建

    什么是视图(VIEW),也是数据库对象之一,和表的区别是表是实际在物理磁盘上保存数据的数据库对象,而视图仅仅对应一条SQL语句,使用时先通过视图将结果集查询出来,再基于它进行其他操作。

    视图一般也称为"伪表"

    CREATE VIEW v_salary_info
    AS
    SELECT subject_id,
           MIN(salary) min_salary,
           MAX(salary) max_salary,
           SUM(salary) sum_salary,
           AVG(salary) avg_salary
    FROM teacher
    GROUP BY subject_id
    
    SELECT * FROM v_salary_info
    等同于
    SELECT * FROM (SELECT subject_id,
           		   		MIN(salary) min_salary,
           		   		MAX(salary) max_salary,
                   		SUM(salary) sum_salary,
                   		AVG(salary) avg_salary
                   FROM teacher
                   GROUP BY subject_id)
    
    
  • 视图使用场景

    • 重复使用子查询:在多个DQL语句中都要使用相同的子查询作为表使用时,可以以视图重用子查询

    • 为了隐藏敏感数据:比如老师表记录了工资信息,程序员需要在项目中查询老师信息,但是不需要看到老师的敏感数据,此时DBA则会创建一个不含有敏感信息的老师信息视图提供给程序员使用。并不分配程序员查看老师表的权限。

      CREATE VIEW v_teacher_info
      AS
      SELECT id,name,title,gender,age,subject_id
      FROM teacher;
      
      
      SELECT * FROM v_teacher_info;
      
      
      

关联查询(重点)

定义

联合多张表查询数据,意味着查询的结果集中字段来自多张表。

例如:查看每个学生的名字,所在的班级的名字,以及该班班主任是谁?

  • 张三,1年级1班,范传奇(只是举例,数据并非真实表中关联)
    • 张三为什么是1年级1班,不是其他班级?student表的数据如何与class表的数据对应上?
    • 1年级1班的班主任是范传奇,为什么不是王克晶?class表中的数据如何再与teacher表的数据对应上?
关联关系

表与表中的数据会产生对应的关系,这样的关系被称为关联关系

关联关系的分类

两张表就可以产生对应的关联关系

  • 一对一关系:A表的1条记录只能对应B表的1条记录
  • 一对多关系:A表的1条记录可以对应B表的多条记录
    • 一个班级可以对应多个学生
    • class表的一条记录可以对应student表的多条记录(3年级2班可以对应查询出student表若干记录)
  • 多对多关系:A表与B表双向都是一对多时就是多对多关系
连接条件

连接条件用于关联两张表中数据的对应关系

好比:在子查询案例中,查询3年级2班的学生都有谁

  • 找到3年级2班的记录id是多少,再查看student表中class_id等于3年级2班的id的学生信息
    • 相当于条件:student.class_id=class.id 这个就是class表与student表数据之间的连接条件

注意事项

  • 在关联查询中通常两张表之间要指定连接条件,否则会产生笛卡尔积,这通常是一个无意义的结果集,开销大,除非必要通常要避免
  • 连接条件的数量:N张表关联查询至少要有N-1个连接条件
    • 比如2张表关联查询至少要有1个连接条件
语法

例如
  • 查看每个老师的名字和其所教科目的名字

    SELECT teacher.name,teacher.salary,teacher.age,subject.name
    FROM teacher,subject
    WHERE teacher.subject_id=subject.id
    当多张表都存在相同名字的字段时,要表名字段所属的表
    
    由于表名通常较长,写起来过于累赘,因此可以为表取别名,字段所属表时可以使用表别名
    SELECT t.name,t.salary,t.age,su.name
    FROM teacher t,subject su
    WHERE t.subject_id=su.id
    

    在这里插入图片描述

  • 在关联查询中不指定连接条件,会产生笛卡尔积

    SELECT t.name,t.salary,t.age,su.name
    FROM teacher t,subject su
    

    在这里插入图片描述

    笛卡尔积通常没有意义,要避免

    • A表的一条记录与B表每条记录连线时,由于没有连接条件,因此会分别摘取字段构成一条结果记录
    • 笛卡尔积的查询结果记录数为两张表记录数的乘积
  • 查看班级的名称和对应的班主任(老师)是谁?

    1:数据来自哪些表?
      班级名称->class表
      班主任名字(老师名字)->teacher表
      确定FROM子句:FROM class c,teacher t
      
    2:表明确后,确定连接条件。2张表关联查询时要求1个连接条件
      class表的记录中的teacher_id要与teacher表的id相同的记录对应。
      明确了连接条件:c.teacher_id=t.id
    
    SELECT c.name,c.teacher_id,t.name,t.id
    FROM class c,teacher t
    WHERE c.teacher_id=t.id
    
    
  • 查看每个学生的名字,年龄,以及其所在的班级名称和所在楼层

    1:数据来自哪些表?
      学生信息来自student表
      班级信息来自class表
      FROM student s,class c
    
    2:2张表中记录如何对应,连接条件是什么?
      每个学生的class_id要与class表中id一致的记录对应
      s.class_id=c.id
      
      SELECT s.name,s.age,c.name,c.floor
      FROM student s,class c
      WHERE s.class_id=c.id
    
    
  • 查看每个学生来自哪座城市的名字,列出学生的名字,性别,年龄和所在城市的名字

    1:数据来自哪些表?
      student s表,location l表
    2:连接条件
      s.location_id=l.id
      
    SELECT s.name,s.gender,s.age,l.name
    FROM student s,location l
    WHERE s.location_id=l.id
    
主外键关联

通常在两张表时,如果两张表中的记录存在关联关系时,那么就会在这两张表中分别定义主键与外键。

  • 什么是主键:
    • 主键通常是表中的第一个字段,名字一般为"id"
    • 主键的特点:非空且唯一,可以使用主键约束进行定义
    • 主键值用来标识表中每一条记录
  • 什么是外键
    • 外键是用来记录另一张表主键字段值的一个字段
    • 设计有外键字段值的表在关联关系中处于关联关系一对多中"多"的一方
  • 例如:Class表与Student表
    • Student表中有一个字段名为:class_id,该字段记录了Class表中某条记录id的值
    • Class表的id字段就是Class表的主键字段(PK)
    • Student表的class_id就是外键(FK)对应的就是Class表主键字段id
    • 因此Studnet表与Class表就存在关联关系,在一对多关系中,Class表是"一",Student表是"多"
  • 在关联查询中,连接条件通常是主键与外键的等值连接。A表的主键字段值=B表的外键字段值
连接条件要与过滤条件同时满足
例如
  • 王克晶是哪个班的班主任?列出:班级名称,楼层,老师名称,工资

    1:数据来自哪些表?
      teacter t,class c
    2:连接条件?
      c.teacher_id=t.id
    3:过滤条件?
      老师的名字叫王克晶
      t.name='王克晶'
      
     SELECT c.name,c.floor,t.name,t.salary 
     FROM teacher t,class c
     WHERE t.id=c.teacher_id					连接条件
     AND t.name='王克晶'						  过滤条件
                                        要同时满足,否则会出现笛卡尔积
    
  • 查看来自南京的学生都有谁?要列出城市名字,学生名字,年龄,性别

    SELECT l.name,s.name,s.age,s.gender
    FROM student s,location l
    WHERE s.location_id=l.id
    AND l.name='南京'
    
  • 查看三年级的班级班主任都是谁?要列出班级名称,所在楼层,班主任名字和工资

    SELECT c.name,c.floor,t.name,t.salary
    FROM class c,teacher t
    WHERE c.teacher_id=t.id
    AND c.name LIKE '3年级%'
    
  • 查看5年级的中队长都有谁?要列出学生名字,年龄,性别,职位和所在班级的名字以及楼层

    SELECT s.name,s.age,s.gender,s.job,c.name,c.floor
    FROM student s,class c
    WHERE s.class_id=c.id
    AND c.name LIKE '5年级%' AND s.job='中队长'
    
    
N张表关联

N张表关联至少要有N-1个连接条件

例如
  • 查看"范传奇"所带班级的学生都有谁?要列出:学生名字,年龄,班级名称,老师名字

    SELECT s.name,s.age,c.name,t.name
    FROM student s,class c,teacher t
    WHERE s.class_id=c.id AND c.teacher_id=t.id
    AND t.name='范传奇'
    
    

    在这里插入图片描述

  • 查看1年级1班的同学的名字和来自的城市

    SELECT s.name,l.name,c.name
    FROM student s,class c,location l
    WHERE s.class_id=c.id AND s.location_id=l.id
    AND c.name='1年级1班'
    
综合练习
题干
1.查看来自北京的学生都是谁?
2."英语"的老师都是谁?
3.刘苍松所带班级的学生都有谁?
4.教语文的老师所带的班级有哪些?
5.王克晶所带的班级学生都来自哪些城市(去重)?
6.3年级的几个班主任都教哪些课程?
7.工资高于10000的老师所带班里的大队长都是谁?
8."李费水"的班主任教哪门课?
9.所在4楼的班里的大队长和中队长以及班主任都是谁?
10.全校最小的同学的班主任是谁?
11."语文"的老师所带班级的学生都来自哪些城市?列出科目,老师名字,班级名字,学生名字,所在城市
12.全校最小的学生来自哪个城市,所在哪个班以及班主任是谁?
13.工资最高的老师所带班里来自"北京"的学生都有谁?
答案
1.查看来自北京的学生都是谁?
  SELECT s.name,l.name
  FROM student s,location
  WHERE s.location_id=l.id
  AND l.name='北京'
  
2."英语"的老师都是谁?
  SELECT t.name,su.name
  FROM teacher t,subject su
  WHERE t.subject_id=su.id
  AND su.name='英语'
  
3.刘苍松所带班级的学生都有谁?
  SELECT s.name,c.name,t.name
  FROM student s,class c,teacher t
  WHERE s.class_id=c.id
  AND c.teacher_id=t.id
  AND t.name='刘苍松'
  
4.教语文的老师所带的班级有哪些?
  SELECT c.name,t.name,su.name
  FROM class c,teacher t,subject su
  WHERE c.teacher_id=t.id
  AND t.subejct_id=su.id
  AND su.name='语文'

5.王克晶所带的班级学生都来自哪些城市(去重)?
  SELECT DISTINCT l.name
  FROM student s,class c,teacher t,location l
  WHERE s.class_id=c.id
  AND s.location_id=l.id
  AND c.teacher_id=t.id
  AND t.name='王克晶'
  
6.3年级的几个班主任都教哪些课程?
  SELECT c.name,t.name,su.name
  FROM class c,teacher t,subject su
  WHERE c.teacher_id=t.id
  AND t.subject_id=su.id
  AND c.name LIKE '3年级%'
  
7.工资高于10000的老师所带班里的大队长都是谁?
  SELECT s.name,s.job,c.name,t.name
  FROM student s,class c,teacher t
  WHERE s.class_id=c.id
  AND c.teacher_id=t.id
  AND t.salary>10000
  AND s.job='大队长'
	
8."李费水"的班主任教哪门课?
  SELECT s.name,c.name,t.name,su.name
  FROM student s,class c,teacher t,subject su
  WHERE s.class_id=c.id
  AND c.teacher_id=t.id
  AND t.subject_id=su.id
  AND s.name='李费水'
  
9.所在4楼的班里的大队长和中队长以及班主任都是谁?
  SELECT s.name,s.job,c.name,t.name
  FROM student s,class c,teacher t
  WHERE s.class_id=c.id
  AND c.teacher_id=t.id
  AND c.floor=4
  AND s.job IN('大队长','中队长')
  
  不用IN
  SELECT s.name,s.job,c.name,t.name
  FROM student s,class c,teacher t
  WHERE s.class_id=c.id
  AND c.teacher_id=t.id
  AND c.floor=4
  AND (s.job='大队长' OR s.job='中队长')
  因为OR的优先级较低,要保证连接条件与过滤条件同时满足时,OR要提高优先级
  

10.全校最小的同学的班主任是谁?
  SELECT DISTINCT t.name 
  FROM student s,class c,teacher t
  WHERE s.class_id=c.id
  AND c.teacher_id=t.id
  AND s.birth=(SELECT MAX(birth) FROM student)
  如果存在多个最小生日的学生,并且隶属同一个班级,那么会查询出多个记录,老师名字可以去重

11."语文"的老师所带班级的学生都来自哪些城市?列出科目,老师名字,班级名字,学生名字,所在城市
  SELECT s.name,l.name,c.name,t.name,su.name
  FROM student s,location l,class c,teacher t,subject su
  WHERE s.location_id=l.id
  AND s.class_id=c.id
  AND c.teacher_id=t.id
  AND t.subject_id=su.id
  AND su.name='语文'
  
12.全校最小的学生来自哪个城市,所在哪个班以及班主任是谁?
  SELECT s.name,l.name,c.name,t.name
  FROM student s,class c,location l,teacher t
  WHERE s.location_id=l.id
  AND s.class_id=c.id
  AND c.teacher_id=t.id
  AND s.birth=(SELECT MAX(birth) FROM student)
 
13.工资最高的老师所带班里来自"北京"的学生都有谁?
 SELECT s.name,l.name,c.name,t.name,t.salary
 FROM student s,class c,teacher t,location l
 WHERE s.class_id=c.id
 AND c.teacher_id=t.id
 AND s.location_id=l.id
 AND t.salary=(SELECT MAX(salary) FROM teacher)
 AND l.name='北京'
 
关联查询中可以使用聚合函数
例如
  • 查看范传奇所带班级的学生共多少人?

    聚合函数用于统计数据
    1:查询参与统计的记录
      查看范传奇班里的学生
      
    2:数据来自哪些表
      FROM student s,class c,teacher t
    
    3:连接条件
      WHERE s.class_id=c.id AND c.teacher_id=t.id
    
    4:过滤条件
      t.name='范传奇'
      
      查看范传奇班里的学生
      SELECT s.name,...
      FROM student s,class c,teacher t
      WHERE s.class_id=c.id AND c.teacher_id=t.id
      AND t.name='范传奇'
      
      加上聚合函数
      SELECT COUNT(*)
      FROM student s,class c,teacher t
      WHERE s.class_id=c.id AND c.teacher_id=t.id
      AND t.name='范传奇'
    
  • 查看教语文的老师平均工资是多少?

    1:查询参与统计的数据
      查询教语文的老师工资是多少
      SELECT t.salary
      FROM teacher t,subject su
      WHERE t.subject_id=su.id
      AND su.name='语文'
      
    2:添加聚合函数
      SELECT AVG(t.salary)
      FROM teacher t,subject su
      WHERE t.subject_id=su.id
      AND su.name='语文'
    
  • 查看教每门课老师的平均工资是多少?列出平均工资和科目名称

    1:查询参与统计的记录
      查询所有老师的工资以及所教科目
      SELECT t.name,t.salary,su.name
      FROM teacher t,subject su
      WHERE t.subject_id=su.id
      
    2:分组,按照科目名称相同的记录分组,添加聚合函数
      SELECT AVG(t.salary),su.name
      FROM teacher t,subject su
      WHERE t.subject_id=su.id
      GROUP BY su.name
    
  • 仅查看平均工资高于6000的那些科目的老师平均工资是多少?列出平均工资和科目名称

    上一个案例查询了所有科目老师的平均工资
    SELECT AVG(t.salary),su.name
    FROM teacher t,subject su
    WHERE t.subject_id=su.id
    GROUP BY su.name
    
    过滤分组
    SELECT AVG(t.salary),su.name
    FROM teacher t,subject su
    WHERE t.subject_id=su.id
    GROUP BY su.name
    HAVING AVG(t.salary)>6000
    
  • 查看工资最高的老师班里的学生共多少人?

    1:查询参与统计的数据
      将工资最高的老师班里学生信息查询出来
      SELECT s.name,...
      FROM student s,class c,teacher t
      WHERE s.class_id=c.id
      AND c.teacher_id=t.id
      AND t.salary=(SELECT MAX(salary) FROM teacher)
      
    2:加聚合函数
      SELECT COUNT(*)
      FROM student s,class c,teacher t
      WHERE s.class_id=c.id
      AND c.teacher_id=t.id
      AND t.salary=(SELECT MAX(salary) FROM teacher)
    
综合练习
题干
1:教语文的老师所带班级各多少学生?
2:每门课的老师所带班级各多少学生?
3:来自上海的学生的班主任都有谁?
4:来自南京的学生共多少人
5:来自武汉的男同学和女同学分别多少人?
6:每个城市的学生各多少人
7:高于平均工资的老师所带的班级分别多少学生?
8:每个老师班里各多少来自郑州的学生?
答案
1:教语文的老师所带班级各多少学生?
  SELECT COUNT(*),t.name,c.name
  FROM student s,class c,teacher t,subject su
  WHERE s.class_id=c.id
  AND c.teacher_id=t.id
  AND t.subject_id=su.id
  AND su.name='语文'
  GROUP BY t.name,c.name
  
2:每门课的老师所带班级各多少学生?
  SELECT COUNT(*),su.name,c.name
  FROM subject su,teacher t,class c,student s
  WHERE su.id=t.subject_id 
  AND t.id=c.teacher_id 
  AND c.id=s.class_id
  GROUP BY su.name,c.name;



3:来自上海的学生的班主任都有谁?
  SELECT l.name,s.name,c.name,t.name
  FROM location l,student s,class c,teacher t
  WHERE l.id=s.location_id
  AND s.class_id=c.id
  AND c.teacher_id=t.id
  AND l.name='上海'
  
4:来自南京的学生共多少人
  SELECT COUNT(*)
  FROM student s,location l
  WHERE s.location_id=l.id
  AND l.name='南京'
  
5:来自武汉的男同学和女同学分别多少人?
  SELECT COUNT(*),s.gender
  FROM student s,location l
  WHERE s.location_id=l.id
  AND l.name='武汉'
  GROUP BY s.gender
  
6:每个城市的学生各多少人
  SELECT COUNT(*),l.name
  FROM student s,location l
  WHERE s.location_id=l.id
  GROUP BY l.name
  
7:高于平均工资的老师所带的班级分别多少学生?
  SELECT COUNT(*),c.name,t.name
  FROM student s,class c,teacher t
  WHERE s.class_id=c.id
  AND c.teacher_id=t.id
  AND t.salary>(SELECT AVG(salary) FROM teacher)
  GROUP BY c.name,t.name
  
8:每个老师班里各多少来自郑州的学生?
  SELECT COUNT(*),c.name,t.name
  FROM location l,student s,class c,teacher t
  WHERE l.id=s.location_id
  AND s.class_id=c.id
  AND c.teacher_id=t.id
  AND l.name='郑州'
  GROUP BY c.name,t.name
  
  

多对多关系

概念

两张表双向都是一对多就是多对多关系

多对多的设计

多对多关系通常会将两张表的外键字段提取出来单独设计为一张表,而这张表用来维护两张表的多对多关系。

例如
  • 查看学习语文的学生都有谁?

    SELECT s.name,su.name,tss.score
    FROM student s,t_stu_subject_score tss,subject su
    WHERE s.id=tss.stu_id AND su.id=tss.subject_id
    AND su.name='语文'
    
  • 查看’李费水’都学了哪门课程以及成绩?

    SELECT s.name,su.name,tss.score
    FROM student s,t_stu_subject_score tss,subject su
    WHERE s.id=tss.stu_id AND su.id=tss.subject_id
    AND s.name='李费水'
    
练习
题干
1.查看1年级1班所有同学的语文成绩是多少?
2.统计1年级1班数学成绩的平均值?
3.统计6年级的英语成绩的平均值?
4.查看"刘苍松"所带班级的英语平均分?
5.查看工资最高的老师所带班级的各科成绩的平均分,最高分和最低分分别是多少?
6.查看每位大队长的5门成绩平均分是多少?
答案
1.查看1年级1班所有同学的语文成绩是多少?
  SELECT s.name,su.name,tss.score,c.name
  FROM class c,student s,t_stu_subject_score tss,subject su
  WHERE c.id=s.class_id AND s.id=tss.stu_id AND su.id=tss.subject_id
  AND c.name='1年级1班' AND su.name='语文'
2.统计1年级1班数学成绩的平均值?
  SELECT AVG(tss.score)
  FROM class c,student s,t_stu_subject_score tss,subject su
  WHERE c.id=s.class_id AND s.id=tss.stu_id AND su.id=tss.subject_id
  AND c.name='1年级1班' AND su.name='数据'
  
3.统计6年级的英语成绩的平均值?
  SELECT AVG(tss.score)
  FROM class c,student s,t_stu_subject_score tss,subject su
  WHERE c.id=s.class_id AND s.id=tss.stu_id AND su.id=tss.subject_id
  AND c.name LIKE '6年级%' AND su.name='英语'

4.查看"刘苍松"所带班级的英语平均分?
  SELECT AVG(tss.score)
  FROM teacher t,class c,student s,t_stu_subject_score tss,subject su
  WHERE t.id=c.teacher_id AND c.id=s.class_id 
  AND s.id=tss.stu_id AND su.id=tss.subject_id
  AND t.name='刘苍松' AND su.name='英语'
  
  
5.查看工资最高的老师所带班级的各科成绩的平均分,最高分和最低分分别是多少?
  SELECT AVG(tss.score),MAX(tss.socre),MIN(tss.socre),su.name
  FROM teacher t,class c,student s,t_stu_subject_score tss,subject su
  WHERE t.id=c.teacher_id AND c.id=s.class_id 
  AND s.id=tss.stu_id AND su.id=tss.subject_id
  AND t.salary=(SELECT MAX(salary) FROM teacher)
  GROUP BY su.name
  
6.查看每位大队长的5门成绩平均分是多少?
  SELECT AVG(tss.score),s.name
  FROM student s,t_stu_subject_score tss,subject su
  WHERE s.id=tss.stu_id AND su.id=tss.subject_id
  AND s.job='大队长'
  GROUP BY s.name
内连接

内连接是关联查询的另一种写法

语法
关联查询写法:
SELECT 子句
FROM 表A,表B,表C,...
WHERE A与B的连接条件 AND B与C的连接条件 AND ...
AND 过滤条件

内连接写法:
SELECT 子句
FROM 表A
JOIN 表B ON A与B的连接条件
JOIN 表C ON 连接条件
JOIN ... ON 连接条件
WHERE 过滤条件

例如
  • 查看1年级1班的学生信息?列出学生名字,年龄,所在班级

    关联查询
    SELECT s.name,s.age,c.name
    FROM class c,student s
    WHERE c.id=s.class_id
    AND c.name='1年级1班'
    
    内连接
    SELECT s.name,s.age,c.name
    FROM class c
    JOIN student s ON c.id=s.class_id
    WHERE c.name='1年级1班'
    
  • 查看教英语的老师都有谁?

    SELECT t.name,su.name
    FROM teacher t
    JOIN subject su ON t.subject_id=su.id
    WHERE su.name='英语';
    
  • 查看每个班级名以及对应的班主任名字?

    SELECT c.name,t.name
    FROM class c
    JOIN teacher t ON c.teacher_id=t.id;
    
  • 查看王克晶所带班级的女同学都有谁?(列出:老师名字,班级名字,学生名字,学生性别)

    SELECT s.name,s.gender,c.name,t.name
    FROM teacher t
    JOIN class c ON t.id=c.teacher_id
    JOIN student s ON c.id=s.class_id
    WHERE t.name='王克晶' AND s.gender='女';
    
  • 查看刘苍松所带班里来自南京的学生的语文成绩是多少?

    SELECT t.name,c.name,s.name,l.name,su.name,tss.score
    FROM teacher t
    JOIN class c ON t.id=c.teacher_id
    JOIN student s ON c.id=s.class_id
    JOIN location l ON l.id=s.location_id
    JOIN t_stu_subject_score tss ON s.id=tss.stu_id
    JOIN subject su ON su.id=tss.subject_id
    WHERE t.name='刘苍松' AND l.name='南京' AND su.name='语文';
    
外连接

外连接也是关联查询,特点:将不满足连接条件的记录也查询出来

  • 左外连接:以JOIN左侧表作为驱动表,将该表记录都查询出来,不满足连接条件时,来自右侧表的中的字段补NULL
  • 右外连接:以JOIN右侧表作为驱动表,将该表记录都查询出来,不满足连接条件时,来自左侧表的中的字段补NULL
例如
  • 查看所有班级信息和对应的班主任信息,如果该班没有班主任也要将班级信息列出来

    关联查询
    SELECT c.name,t.name
    FROM class c,teacher t
    WHERE c.teacher_id=t.id;
    
    内连接
    SELECT c.name,t.name
    FROM class c
    JOIN teacher t ON c.teacher_id=t.id;
    
    以上两种都不会将不满足连接条件的记录列出来
    

    在这里插入图片描述

    使用左外连接,以JOIN左侧表作为驱动表,将该表记录满足过滤条件的记录都查询出来

    SELECT c.name,t.name
    FROM class c LEFT OUTER JOIN teacher t ON c.teacher_id=t.id;
    
    注:OUTER可以不写
    
    SELECT c.name,t.name
    FROM class c LEFT JOIN teacher t ON c.teacher_id=t.id;
    

    在这里插入图片描述

  • 查看所有班级信息和对应的班主任信息,如果该老师不带班,也要将老师信息列出来

    SELECT c.name,t.name
    FROM class c RIGHT OUTER JOIN teacher t ON c.teacher_id=t.id;
    

    在这里插入图片描述

全外连接

在ORACLE中有全外连接:FULL OUTER JOIN

但是MySQL,MariaDB没有

UNION并集操作
  • UNION可以将多个查询结果集并在一起
  • 要求:SELECT子句中指定的字段的个数,顺序,类型需要完全一致
  • 不同结果集中的重复数据仅保留一次
  • 将左外连接和右外连接的结果集并在一起达到全外连接的效果
例如
  • 查看所有班级信息和对应的班主任信息,无论是班级还是老师不满足连接条件也要展示

    SELECT c.name,t.name
    FROM class c LEFT OUTER JOIN teacher t ON c.teacher_id=t.id
    UNION
    SELECT c.name,t.name
    FROM class c RIGHT OUTER JOIN teacher t ON c.teacher_id=t.id
    

    在这里插入图片描述

  • UNION除了实现全外连接,还可以做其他操作

    • 查看姓张的和姓李的学生各多少人?

      SELECT COUNT(*) '人数' , '姓张' AS '姓氏' FROM student WHERE name LIKE '张%'
      UNION
      SELECT COUNT(*) '人数' , '姓李' AS '姓氏' FROM student WHERE name LIKE '李%'
      
自连接
定义

当一张表中的一条记录可以对应它自己的其他多条记录时,就是自连接

自连接时发生在同一张表中的

场景

当一组具有相同属性的数据又存在上下级关系时(树状结构数据),可以使用自连接设计。

特点

该表中存在一个外键,记录了它自己主键字段的值

应用场景

电商中的分类树,就是用自连接设计

在电商项目中,有一张表:type。记录了所有商品的类别。但是类别本身有存在上下级关系:

  • 生活用品
  • 家用电器
  • 电脑
    • 电脑整机
      • 笔记本
      • 台式机
      • 游戏本
      • 一体机
      • 平板电脑
    • 电脑配件
    • 游戏设备
  • 手机

一家公司的员工组织结构,都是员工,但是员工又存在上下级关系。

例如
  • 查看’刘苍松’的下属都有谁?

    在teacher表中,记录了学校所有的老师,但是老师也存在上下级关系。因此在teacher表中定义了一个外键字段名为manager,用来记录该老师上级领导的id。而这个领导的id本身就是该名老师的id。

自连接在查询时,就是将一张表当作两张表看待即可,一张当作保存老师信息,一张当作保存领导信息。

SELECT t.name '下属名字',m.name '领导名字'
FROM teacher t , teacher m       			t当作保存老师的信息     m当作保存领导信息
WHERE t.manager=m.id
AND m.name='刘苍松'

内连接写法
SELECT t.name '下属名字',m.name '领导名字'
FROM teacher t
JOIN teacher m ON t.manager=m.id
WHERE m.name='刘苍松'

在这里插入图片描述

  • 查看3年级2班的班长是谁?(student表中team_leader记录班长的学生id)

    SELECT DISTINCT l.name
    FROM class c
    JOIN student s ON s.class_id=c.id
    JOIN student l ON s.team_leader=l.id
    WHERE c.name='3年级2班';
    
    SELECT s.name
    FROM class c
    JOIN student s ON s.class_id=c.id
    WHERE c.name='3年级2班'
    AND s.team_leader=s.id
    
    
  • 学生"祝雷"的班长是谁?

    SELECT l.name
    FROM student s 
    JOIN student l ON s.team_leader=l.id
    WHERE s.name='祝雷'
    
    
  • 年龄最大的学生所在班的班主任的上司是谁?

    SELECT s.name,c.name,t.name,m.name
    FROM student s
    JOIN class c ON s.class_id=c.id
    JOIN teacher t ON c.teacher_id=t.id
    JOIN teacher m ON t.manager=m.id
    WHERE s.birth=(SELECT MIN(birth) FROM student)
    
    
外键约束

关联关系中,通常两张表建立关联关系就是靠主键与外键的等值连接作为连接条件。

外键约束要求
  • 外键约束要求外键字段的值必须参照主键字段的值

  • 外键字段可以存放NULL

  • 外键约束要求外键字段保存的值必须是主键字段中存在的值

外键约束带来的问题
  • 外键约束不能保存主键字段没有的值

    在这里插入图片描述

  • 要求主键表中不能删除外键存在值的记录

    • 当删除主键字段记录时会导致外键记录存在违反约束的情况,因此不允许删除
    • 若要删除主键字段记录,则需要先将所有外键字段对应的值改为null,这会导致大量的DML操作,影响性能

    在这里插入图片描述

结论:实际开发中,通常不适用外键约束,通过逻辑来关联即可
me,tss.score
FROM teacher t
JOIN class c ON t.id=c.teacher_id
JOIN student s ON c.id=s.class_id
JOIN location l ON l.id=s.location_id
JOIN t_stu_subject_score tss ON s.id=tss.stu_id
JOIN subject su ON su.id=tss.subject_id
WHERE t.name=‘刘苍松’ AND l.name=‘南京’ AND su.name=‘语文’;

#### 外连接

外连接也是关联查询,特点:将不满足连接条件的记录也查询出来

- 左外连接:以JOIN左侧表作为驱动表,将该表记录都查询出来,不满足连接条件时,来自右侧表的中的字段补NULL
- 右外连接:以JOIN右侧表作为驱动表,将该表记录都查询出来,不满足连接条件时,来自左侧表的中的字段补NULL

##### 例如

- 查看所有班级信息和对应的班主任信息,如果该班没有班主任也要将班级信息列出来

```sql
关联查询
SELECT c.name,t.name
FROM class c,teacher t
WHERE c.teacher_id=t.id;

内连接
SELECT c.name,t.name
FROM class c
JOIN teacher t ON c.teacher_id=t.id;

以上两种都不会将不满足连接条件的记录列出来

使用左外连接,以JOIN左侧表作为驱动表,将该表记录满足过滤条件的记录都查询出来

SELECT c.name,t.name
FROM class c LEFT OUTER JOIN teacher t ON c.teacher_id=t.id;

注:OUTER可以不写

SELECT c.name,t.name
FROM class c LEFT JOIN teacher t ON c.teacher_id=t.id;

[外链图片转存中…(img-DgfLl4WK-1725349829018)]

  • 查看所有班级信息和对应的班主任信息,如果该老师不带班,也要将老师信息列出来

    SELECT c.name,t.name
    FROM class c RIGHT OUTER JOIN teacher t ON c.teacher_id=t.id;
    
全外连接

在ORACLE中有全外连接:FULL OUTER JOIN

但是MySQL,MariaDB没有

UNION并集操作
  • UNION可以将多个查询结果集并在一起
  • 要求:SELECT子句中指定的字段的个数,顺序,类型需要完全一致
  • 不同结果集中的重复数据仅保留一次
  • 将左外连接和右外连接的结果集并在一起达到全外连接的效果
例如
  • 查看所有班级信息和对应的班主任信息,无论是班级还是老师不满足连接条件也要展示

    SELECT c.name,t.name
    FROM class c LEFT OUTER JOIN teacher t ON c.teacher_id=t.id
    UNION
    SELECT c.name,t.name
    FROM class c RIGHT OUTER JOIN teacher t ON c.teacher_id=t.id
    

    [外链图片转存中…(img-ScxWoWeE-1725349829018)]

  • UNION除了实现全外连接,还可以做其他操作

    • 查看姓张的和姓李的学生各多少人?

      SELECT COUNT(*) '人数' , '姓张' AS '姓氏' FROM student WHERE name LIKE '张%'
      UNION
      SELECT COUNT(*) '人数' , '姓李' AS '姓氏' FROM student WHERE name LIKE '李%'
      
自连接
定义

当一张表中的一条记录可以对应它自己的其他多条记录时,就是自连接

自连接时发生在同一张表中的

场景

当一组具有相同属性的数据又存在上下级关系时(树状结构数据),可以使用自连接设计。

特点

该表中存在一个外键,记录了它自己主键字段的值

应用场景

电商中的分类树,就是用自连接设计

在电商项目中,有一张表:type。记录了所有商品的类别。但是类别本身有存在上下级关系:

  • 生活用品
  • 家用电器
  • 电脑
    • 电脑整机
      • 笔记本
      • 台式机
      • 游戏本
      • 一体机
      • 平板电脑
    • 电脑配件
    • 游戏设备
  • 手机

一家公司的员工组织结构,都是员工,但是员工又存在上下级关系。

例如
  • 查看’刘苍松’的下属都有谁?

    在teacher表中,记录了学校所有的老师,但是老师也存在上下级关系。因此在teacher表中定义了一个外键字段名为manager,用来记录该老师上级领导的id。而这个领导的id本身就是该名老师的id。

自连接在查询时,就是将一张表当作两张表看待即可,一张当作保存老师信息,一张当作保存领导信息。

SELECT t.name '下属名字',m.name '领导名字'
FROM teacher t , teacher m       			t当作保存老师的信息     m当作保存领导信息
WHERE t.manager=m.id
AND m.name='刘苍松'

内连接写法
SELECT t.name '下属名字',m.name '领导名字'
FROM teacher t
JOIN teacher m ON t.manager=m.id
WHERE m.name='刘苍松'

[外链图片转存中…(img-4IjeXHVJ-1725349829019)]

  • 查看3年级2班的班长是谁?(student表中team_leader记录班长的学生id)

    SELECT DISTINCT l.name
    FROM class c
    JOIN student s ON s.class_id=c.id
    JOIN student l ON s.team_leader=l.id
    WHERE c.name='3年级2班';
    
    SELECT s.name
    FROM class c
    JOIN student s ON s.class_id=c.id
    WHERE c.name='3年级2班'
    AND s.team_leader=s.id
    
    
  • 学生"祝雷"的班长是谁?

    SELECT l.name
    FROM student s 
    JOIN student l ON s.team_leader=l.id
    WHERE s.name='祝雷'
    
    
  • 年龄最大的学生所在班的班主任的上司是谁?

    SELECT s.name,c.name,t.name,m.name
    FROM student s
    JOIN class c ON s.class_id=c.id
    JOIN teacher t ON c.teacher_id=t.id
    JOIN teacher m ON t.manager=m.id
    WHERE s.birth=(SELECT MIN(birth) FROM student)
    
    
外键约束

关联关系中,通常两张表建立关联关系就是靠主键与外键的等值连接作为连接条件。

外键约束要求
  • 外键约束要求外键字段的值必须参照主键字段的值

  • 外键字段可以存放NULL

  • 外键约束要求外键字段保存的值必须是主键字段中存在的值

外键约束带来的问题
  • 外键约束不能保存主键字段没有的值

  • 要求主键表中不能删除外键存在值的记录

    • 当删除主键字段记录时会导致外键记录存在违反约束的情况,因此不允许删除
    • 若要删除主键字段记录,则需要先将所有外键字段对应的值改为null,这会导致大量的DML操作,影响性能

结论:实际开发中,通常不适用外键约束,通过逻辑来关联即可

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值