数据库
数据库基本概念
数据库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的数据库
注:每条SQL语句结尾的";"并非必须的。
查看已经创建的数据库 创建数据库时指定字符集 创建数据库mydb1,指定字符集为UTF-8 CREATE DATABASE mydb1 CHARSET=UTF8 创建数据库mydb2,指定字符集为GBK CREATE DATABASE mydb2 CHARSET=GBK 复制代码 查看创建某个数据库时的信息
语法 SHOW CREATE DATABASE 数据库名 复制代码 例如 SHOW CREATE DATABASE mydb 复制代码 删除数据库
语法 例如 注意:删除数据库是不可逆操作
切换数据库
在DBMS下会存在多个不同的数据库,只有切换到对应的数据库上,那么后期学习的所有操作才是针对该数据库进行的,比如创建表才是在切换的数据库上进行。
语法 例如 切换到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为整数类型,长度是数字的位数 ) 复制代码 查看库中所有的表 查看创建表时的信息
语法 例如 查看表结构
语法 例如 修改表名
语法 例如 user表重命名为userinfo RENAME TABLE user TO userinfo 复制代码 删除表
语法 例如 将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 复制代码
删除表中字段
语法 例如 将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 INT; ALTER 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值和默认值
批量插入
可以在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 过滤条件] 复制代码 注意 ELETE语句通常都要添加WHERE子句,否则为清空表操作
例如 删除'李老四' DELETE FROM person WHERE name='李老四' 删除年龄在25岁以下的人 DELETE FROM person WHERE age<25 复制代码 清空表 总结
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.修改刘备年龄为52岁 7.修改年龄小于等于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.修改刘备年龄为52岁 UPDATE 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) 复制代码
自增
通常具有主键约束的字段都会为其添加自增,自增是数据库为字段生成值的一种机制。
AUTO_INCREMENT
AUTO_INCREMENT是一个关键字,当字段添加后具有自增特性。
例如
非空约束
NOT NULL为非空约束,当字段施加非空约束后,字段的值任何时候都不允许为NULL。
插入数据时不可以将NULL值插入到该字段上 更新数据时也不可以将NULL值更新 一张表中可以有多个字段添加非空约束
例如
唯一性约束
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字段的值必须在0到120之间 复制代码
插入数据时,值不能违背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脚本文件
DQL语言-数据查询语言
DQL语言使用检索表中记录的语言
语法 执行顺序 SELECT 子句 6 FROM 子句 1 JOIN... ON ... 子句 2 WHERE 子句 3 GROUP BY 子句 4 HAVING 子句 5 ORDER BY 子句 7 LIMIT 子句 8 复制代码 基础查询
语法 SELECT 字段1,字段2,... FROM 表1,表2,... 检索表中每条记录中指定字段的内容 SELECT * FROM 表1,表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的优先级,需要使用"()"括起来
IN(列表)
IN(列表)表示字段的值在列表中。等于列表的其中之一。
例如
NOT IN(列表)
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.查看工资在6000到10000之间的老师以及具体工资? 6.查看工资在4000到8000以外的老师及具体工资? 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.查看工资在6000到10000之间的老师以及具体工资? SELECT name,salary FROM teacher WHERE salary BETWEEN 6000 AND 10000 6.查看工资在4000到8000以外的老师及具体工资? 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后第一个字段为最优先,以此类推
例如
分页查询
分页查询就是将一条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:每页显示多少条
例如
在DQL语句中可以使用函数或表达式
在SELECT子句中使用表达式
例如
查看老师的工资和年薪分别是多少?
SELECT name,salary,salary*12 FROM teacher 复制代码
在SELECT子句中使用函数
数据库中都内置了很多函数,但是不同的数据库函数不完全一样。
IFNULL函数
定义
作用
例如
查看每个老师的工资,奖金,工资+奖金分别是多少?
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子句中使用表达式
例如
在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.查询负责课程编号1和2号且工资高于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.查询负责课程编号1和2号且工资高于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-10名 SELECT 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.查看所有老师的平均奖金和奖金总和是多少? 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子句可以按照多字段分组,哪些字段值都相同的记录被看作一组
按照单字段分组
例如
按照多字段分组
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子句之后的,用于添加条件过滤分组的
问题
错误
聚合函数不能出现在WHERE子句中的
原因
过滤时机不对:
WHERE的过滤时机:在检索表中记录时 ,逐行扫描数据,将满足WHERE条件的记录生成结果集 聚合函数作为过滤条件,前提
聚合函数是对结果集进行统计的,因此前提是要现有结果集 而WHER是产生结果集时过滤的,因此它是在聚合函数使用前发挥作用的
解决办法
使用HAVING子句
例如
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中使用子查询
单行单列子查询
例如
多行单列子查询
多行单列子查询可以同时查询出多个值
如果进行等值判断时,要配合IN,NOT IN 使用 如果进行关系运算(>,>=,<,<=)
>ANY(列表):大于列表其中一个值即可(>最小的) >ALL(列表):大于列表中所有值(>最大的)
例如
在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) 复制代码
视图使用场景
关联查询(重点)
定义
联合多张表查询数据,意味着查询的结果集中字段来自多张表。
例如:查看每个学生的名字,所在的班级的名字,以及该班班主任是谁?
张三,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个连接条件
语法 例如
主外键关联
通常在两张表时,如果两张表中的记录存在关联关系时,那么就会在这两张表中分别定义主键与外键。
什么是主键:
主键通常是表中的第一个字段,名字一般为"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个连接条件
例如
综合练习
题干 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:每门课的老师所带班级各多少学生? 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子句中指定的字段的个数,顺序,类型需要完全一致 不同结果集中的重复数据仅保留一次 将左外连接和右外连接的结果集并在一起达到全外连接的效果
例如
自连接
定义
当一张表中的一条记录可以对应它自己的其他多条记录时,就是自连接
自连接时发生在同一张表中的
场景
当一组具有相同属性的数据又存在上下级关系时(树状结构数据),可以使用自连接设计。
特点
该表中存在一个外键,记录了它自己主键字段的值
应用场景
电商中的分类树,就是用自连接设计
在电商项目中,有一张表:type。记录了所有商品的类别。但是类别本身有存在上下级关系:
一家公司的员工组织结构,都是员工,但是员工又存在上下级关系。
例如
自连接在查询时,就是将一张表当作两张表看待即可,一张当作保存老师信息,一张当作保存领导信息。
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='刘苍松' 复制代码
外键约束
关联关系中,通常两张表建立关联关系就是靠主键与外键的等值连接作为连接条件。
外键约束要求
外键约束带来的问题
外键约束不能保存主键字段没有的值
要求主键表中不能删除外键存在值的记录
当删除主键字段记录时会导致外键记录存在违反约束的情况,因此不允许删除 若要删除主键字段记录,则需要先将所有外键字段对应的值改为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; 以上两种都不会将不满足连接条件的记录列出来 复制代码 [外链图片转存中…(img-cfI0jBw2-1751535637281)]
使用左外连接,以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-FaNfVYwq-1751535637281)]
查看所有班级信息和对应的班主任信息,如果该老师不带班,也要将老师信息列出来
SELECT c.name,t.name FROM class c RIGHT OUTER JOIN teacher t ON c.teacher_id=t.id; 复制代码 [外链图片转存中…(img-eTgyoRXL-1751535637281)]
全外连接
在ORACLE中有全外连接:FULL OUTER JOIN
但是MySQL,MariaDB没有
UNION并集操作
UNION可以将多个查询结果集并在一起 要求:SELECT子句中指定的字段的个数,顺序,类型需要完全一致 不同结果集中的重复数据仅保留一次 将左外连接和右外连接的结果集并在一起达到全外连接的效果
例如
自连接
定义
当一张表中的一条记录可以对应它自己的其他多条记录时,就是自连接
自连接时发生在同一张表中的
场景
当一组具有相同属性的数据又存在上下级关系时(树状结构数据),可以使用自连接设计。
特点
该表中存在一个外键,记录了它自己主键字段的值
应用场景
电商中的分类树,就是用自连接设计
在电商项目中,有一张表:type。记录了所有商品的类别。但是类别本身有存在上下级关系:
一家公司的员工组织结构,都是员工,但是员工又存在上下级关系。
例如
自连接在查询时,就是将一张表当作两张表看待即可,一张当作保存老师信息,一张当作保存领导信息。
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-jMhJEMhf-1751535637281)]
外键约束
关联关系中,通常两张表建立关联关系就是靠主键与外键的等值连接作为连接条件。
外键约束要求
外键约束带来的问题
结论:实际开发中,通常不适用外键约束,通过逻辑来关联即可