把结果先按照分组的形式,再进行数据分析
2.1 查询每个部门里员工的最高薪资
select deptno,max(sal) from emp group by deptno;
2.2 查询每个部门里员工的最高薪资和平均值
select max(sal),avg(sal),deptno from emp group by deptno;
如果出现了聚合函数(max,min,avg,sum,count),非聚合列,必须分组
2.3 查询每个岗位的员工的最高薪资和最低薪资
select max(sal),min(sal),job from emp group by job;
2.4 查询平均工资小于8000的部门
select deptno from emp group by deptno having avg(sal)<8000;
having 和 group by 是固定搭配,用来过滤分组后的数据
不可用where,where中不能出现聚合函数
2.5 deptno出现的次数大于2
select deptno,count(deptno) from emp group by deptno having count(deptno)>2;
3.1 where和having都用于对表中记录进行筛选过滤.
3.2 where用于在分组之前对记录进行筛选过滤,而having用于对分组之后的记录进行筛选过滤.
3.3 where子句中不能使用多行函数和列别名,但可以使用表别名.
3.4 having子句中可以使用多行函数、列别名和表别名.
为了保证数据的安全,如果要操作多条SQL,要么全部成功,要么全部失败 .
2.1 原子性(Atomicity,或称不可分割性): 多条SQL是密不可分的,整体的结果要么全成功,要么全失败.
2.2 一致性(Consistency): 保证数据的守恒,总和不变.
2.3 隔离性(Isolation,或称独立性): 数据库支持高并发(即允许多个并发事务同时对其数据进行读写操作),但是操作之间是独立的、被隔离的.
2.4 持久性(Durability): 是指SQL语句对数据库的操作(增删改),影响是永久的.
3.1 读未提交(Read uncommitted): 性能最好,但可能发生并发数据问题,安全性最差.
3.2 读已提交(Read committed): 性能稍差,但是数据安全 (Oracle默认的隔离级别).
3.3 可重复读(Repeatable Read): 性能一般,但是安全性较好 (MySQL默认的隔离级别).
3.4 串行化(Serializable): 表级锁,读写都加锁,效率低下,安全性高,不能并发.
在默认情况下,MySQL每执行一条SQL语句,都是一个单独的事务,如果需要在一个事务中包含多条SQL语句,那么就需要手动开启事务和结束事务.
开启事务: start transaction
结束事务: commit(提交事务) 或 rollback(回滚事务)
在执行上去了语句之前,先执行start transaction,这就开启了一个事务(事务的起点),然后可以去执行多条SQL语句,最后要结束事务,commit表示提交,即事务中的多条生气了语句所作出的影响会持久化到数据库中.或者rollback,表示回滚,即回滚到事务的起点,之前做的所有操作都被撤销.
MySQL 8.0之前使用:
SELECT @@tx_isolation;
MySQL 8.0中使用:
SELECT @@transaction_isolation;
Repeatable Read(可重读)
MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行.
5.1 在MySQL中只有使用了Innodb 数据库引擎的数据库或表才支持事务.
5.2 事务处理可以用来维护数据的完整性,保证成批的SQL语句要么全部执行,要么全部不执行.
5.3 事务用来管理 insert 、update 、delete语句,因为这些操作才会"破坏"数据,查询select语句不会.
5.4 MySQL默认数据库的事务是开启的,执行SQL后自动提交.
5.5 MySQL的事务也可以改为手动提交,那么就会有两个步骤:先开启,写完SQL语句后,再手动提交.
6.1 begin: 开启事务
6.2 commit / rollback: 关闭事务
i. commit: 提交事务,产生永久影响
ii. rollback: 回滚事务,数据库里没有新数据
6.3 查询SQL,commit之后才有数据,没有commit或者rollback都查不到
DROP TABLE IF EXISTS tb_user; #如果表存在则删除,慎用会丢失数据
CREATE TABLE tb_user(
id INT AUTO_INCREMENT,
NAME VARCHAR(30) UNIQUE NOT NULL,
age INT,
phone VARCHAR(20) UNIQUE NOT NULL,
email VARCHAR(30) UNIQUE NOT NULL,
PRIMARY KEY (id)
);
DESC tb_user;
#id为自增主键,null值无效,数据库会自动用下一个id值替代
#age因为运行为null,所以可以设置为null
INSERT INTO tb_user (id,age) VALUES(NULL,NULL);
name字段创建了唯一约束,插入数据时数据会进行检查,如果插入的值相同,就会检查报错
DROP TABLE IF EXISTS tb_user; #如果表存在则删除,慎用会丢失数据
CREATE TABLE tb_user(
id INT,
NAME VARCHAR(30) UNIQUE NOT NULL,
phone VARCHAR(20) UNIQUE NOT NULL,
email VARCHAR(30) UNIQUE NOT NULL,
PRIMARY KEY (id)
);
DESC tb_user;
INSERT INTO tb_user (id,NAME) VALUES(1,'tony');
INSERT INTO tb_user (id,NAME) VALUES(2,'tony');
执行上面语句会出现错误
Query : INSERT INTO tb_user (id,NAME) VALUES(2,'tony')
Error Code : 1062
Duplicate entry 'tony' for key 'name'
主键是一条记录的唯一标识,具有唯一性,不能重复
DROP TABLE IF EXISTS tb_user; #如果表存在则删除,慎用会丢失数据
CREATE TABLE tb_user(
id INT,
NAME VARCHAR(30),
PRIMARY KEY (id)
);
INSERT INTO tb_user (id,NAME) VALUES(1,'tony');
INSERT INTO tb_user (id,NAME) VALUES(1,'hellen');
第二句插入会报错:
Query : INSERT INTO tb_user (id,NAME) VALUES(1,'hellen')
Error Code : 1062
Duplicate entry '1' for key 'PRIMARY'
提示主键1的值已经存在.
DROP TABLE IF EXISTS tb_user_address; #如果表存在则删除,慎用会丢失数据
DROP TABLE IF EXISTS tb_user; #如果表存在则删除,慎用会丢失数据
CREATE TABLE tb_user (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, #自增主键
NAME VARCHAR(50) NOT NULL UNIQUE, #非空,唯一索引
sex CHAR(2) DEFAULT '男', #默认值
phone CHAR(18),
age INT,
CHECK (age>0 AND age<=200),
createdTime DATE DEFAULT NOW()
);
CREATE TABLE tb_user_address (
user_id INT PRIMARY KEY NOT NULL,
address VARCHAR(200),
FOREIGN KEY(user_id) REFERENCES tb_user(id)
);
DESC tb_user;
tb_user_address中user_id字段录入tb_user表不存在的主键值,将报错.
如果要删除tb_user表中的数据,则先需要删除tb_user_addr中约束的数据,才可以删除tb_user表中的数据.
DROP TABLE IF EXISTS tb_user; #如果表存在则删除,慎用会丢失数据
CREATE TABLE tb_user (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, #自增主键
NAME VARCHAR(50) NOT NULL UNIQUE, #非空,唯一索引
sex CHAR(2) DEFAULT '男', #默认值
phone CHAR(18),
age INT,
createdTime DATE DEFAULT NOW()
);
DESC tb_user;
如今很少使用,例如age超过200将会报错
DROP TABLE IF EXISTS tb_user; #如果表存在则删除,慎用会丢失数据
CREATE TABLE tb_user (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, #自增主键
NAME VARCHAR(50) NOT NULL UNIQUE, #非空,唯一索引
sex CHAR(2) DEFAULT '男', #默认值
phone CHAR(18),
age INT,
CHECK (age>0 AND age<=200),
createdTime DATE DEFAULT NOW()
);
DESC tb_user;
表table代表了生活中一个主体,如部门表dept,员工表emp.表关联则代表了表之间的关系,如:部门和员工,商品和商品分类,老师和学生,教室和学生.
同时,也要知道,表并不都有关系,它们形成自己的小圈子.如商品和商品详情一圈,部门和员工一圈,出圈就可能没关系了,如商品和员工无关,商品和学生无关.
一对一(one to one): QQ 和QQ邮箱,员工和员工编号
一对多(one to many): 最常见,部门和员工,用户和订单
多对一(many to one): 一对多反过来,员工和部门,订单和用户
多对多(many to many): 老师和学生,老师和课程
多表查询是指基于两个和两个以上的表的查询。在实际应用中,查询单个表可能不能满足你的需求,如显示员工表emp中不只显示deptno,还要显示部门名称,而部门名称dname在dept表中。
把两个表的数据都拼接起来
select * from emp,dept;
上面这种查询两个表的方式称为:笛卡尔积(Cartesian product),又称直积。一般笛卡尔积没有实际的业务意义,但多表查询都是先生成笛卡尔积,再进行数据的筛选过滤。
这点很值得注意,实际开发中尽量少用多表联查,其根本原因就在这里,查询过程中,现在内存中构建一个大大的结果集,然后再进行数据的过滤。那这个构建过程,和所使用的内存资源,包括过滤时的判断,都是既耗费资源,又浪费时间。
连接 | jion |
---|---|
内连接 | inner join |
左(外)连接 | left join |
右(外)连接 | right join |
5.1 查询tony的部门信息
笛卡尔积
select * from emp,dept
where
emp.deptno = dept,id //描述了两张表的关系
and
emp.ename = 'tony'; //过滤条件
5.2 join分为内连接,外连接(左外连接left join / 右外连接right join)
内连接,取交集
select * from emp inner join dept on emp.deptno = dept.id and emp.ename = 'tony';
左连接,取左边的所有和右边满足了的
select * from emp left join dept on emp.deptno = dept.id and emp.ename = 'tony';
右连接,取右边的所有和左边满足了的
select * from emp right join dept on emp.deptno = dept.id and emp.ename = 'tony';