基础
入门 MySQL
数据库三大范式
为了建立冗余较小,结构合理的数据库,设计数据库时必须遵循一定的规则。
在关系型数据库中这种规则就成为范式。
a. 第一范式
确保每列保持原子性,数据库中的所有字段都是不可分解的原子值。
b. 第二范式
确保表中的每列都和主键相关。
c. 第三范式
确保每列都和主键列之间相关,而不是间接相关
关系模型
关系数据库的表和表之间的关系 : 一对多 一对一 多对一
关系型数据库使通过 主键 和 外键 进行维护的
主键
定义:
在关系型数据库中,一张表中的每一行数据被称为一条记录,一条记录有多个字段组成。主键是用来区分两条记录的,在关系型数据库中不能存在两条相同的记录。
选取:
- 字段唯一
- 字段不能和业务有任何相关性。
一般使用id字段,其类型有:
- 自增整数类型:数据库会在插入数据时自动为每一条记录分配一个自增整数。(每添加一条自增1,所以不会重复。删除已经出现的id,不会重置id自增的当前初始值。)
- 全局唯一GUID类型:这是一种全局唯一的字符串作为主键,GUID算法使用网卡MAC地址,时间戳,随机数保证唯一性。
联合主键:
关系型数据库实际上允许多个字段唯一表示记录,多个字段都设置为主键,这种主键是联合主键。
联合主键不常用。
外键
定义:
通过主键我们可以确定一个表中的唯一的一条记录,但是如何将这个记录和其他表的某一条记录联系起来,为了表达一张表记录和其他表记录之间的关系,我们加入外键的概念。
外键不是通过列名实现的,而是通过定义外键约束实现的。(所以列名随意)
MySQL 语句:
FOREIGN KEY (列名)
指定某列为外键,REFERENDCES classes(id)
指定这个外键将关联到classes表的id列(可以是classes表的主键,也可以是普通的列。)缺点:外键的约束会降低数据库的性能,所以大部分互联网程序为了追求速度,并不设置外键约束,而是依靠自身保证逻辑的正确性。
删除外键约束,通过
ALTER TABLE
实现:ALTER TABLE students DROP FOREIGN KEY fk_class_id;
★:删除外键约束,并没有删除外键这一列。删除外键这一列通过
DROP COLUMN
实现。
多对多关系
一个老师对应多个班级,一个班级对应多个老师。
多对多关系实际上是通过 两个 一对多关系实现的,通过一个中间表,关联了两个一对多关系。
teachers
表:
id | name |
---|---|
1 | 张老师 |
2 | 王老师 |
3 | 李老师 |
classes
表:
id | name |
---|---|
1 | 一班 |
2 | 二班 |
中间表:`teacher_class` 关联两个表
id | teacher_id | class_id |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 1 |
4 | 2 | 2 |
5 | 3 | 1 |
由上表可知:teacher id为1的,教学1班和2班,teacher id为 2的 教学 1班和2班,teacher id 为 3 的教学1班。
一对一
指 一个记录对应另一个表的唯一一个记录。
例如:学生表有自己唯一的联系方式,联系方式存在另一个表中。
既然是一对一为什么不直接增加一列? 因为目的是将常读取的数据放在一张表中,与不常用的数据分隔开,获取更高的性能
SQL
了解什么是SQL(Structured Query Language) 结构化查询语言, 是操作数据标准的特定的开发语言.
【CRUD】
C Create 创建
R Read 读取,查询
U Update 更新,修改
D Delete 删除
核心:
数据库设计 C, 数据库查询 R
【重点】DQL 数据库查询语言
【重点】DML 数据库操作语言
【重点】DDL 数据库定义语言
DCL 数据库控制语言
DTL 数据库事务相关操作
MySQL概述
关系型数据库
MySQL 数据类型
数值类型
类型 | 大小 | 用途 |
---|---|---|
TINYINT | 1Bytes | 小整数值 |
SMALLINT | 2Bytes | 大整数值 |
MEDIUMINT | 3Bytes | 大整数值 |
INT | 4Bytes | 大整数值 |
BIGINT | 8Bytes | 极大整数值 |
FLOAT | 4Bytes | 单精度浮点 |
DOUBLE | 8Bytes | 双精度浮点 |
DECIMAL | 对DECIMAL(M,D) | 小数值 |
日期和时间类型
类型 | 大小 | 格式 | 用途 |
---|---|---|---|
DATE | 3 | YYYY-MM-DD | 日期值 |
TIME | 3 | HH:MM:SS | 时间值或持续时间 |
YEAR | 1 | YYYY | 年份值 |
DATETIME | 8 | YYYY-MM-DD hh:mm:ss | 混合日期和时间值 |
TIMESTAMP | 4 | YYYY-MM-DD hh:mm:ss | 混合日期和时间值,时间戳 |
字符串类型
类型 | 大小 | 用途 |
---|---|---|
CHAR | 0-255Bytes | 定长字符串 |
VARCHAR | 0-65535Bytes | 变长字符串 |
TINYBLOB | 0-255Bytes | 不超过255个字符的二进制字符串 |
TINYTEXT | 0-255Bytes | 短文本字符串 |
BLOB | 0-65535Bytes | 二进制形式的长文本数据 |
TEXT | 0-65535Bytes | 长文本数据 |
MEDIUMBLOB | 0-16777215Bytes | 二进制形式的中等长度文本数据 |
MEDIUMTEXT | 0-16777215Bytes | 中等长度文本数据 |
LONGBLOB | 0-4294967295Bytes | 二进制形式的极大文本数据 |
LONGTEXT | 0-4294967295Bytes | 极大文本数据 |
char(n) 和 varchar(n) 中括号中n 代表字符的个数,并不代表字节个数,比如 CHAR(30)就可以存储30个字符。
CHAR 和 VARCHAR 类型类似,但他们保存和检索的方式不同。他们的最大长度和是否尾部空格被保留等方面也不同。在存储或检索过程中不进行大小写转换。
BLOB 是一个二进制大对象,可以容纳可变数量的数据。有4种BLOB类型:TINYBLOB,BLOB, MEDIUMBLOB, LONGBLOB。他们的区别在于可容纳存储范围不同。
有四种TEXT类型:TINYTEXT ,TEXT, ,MEDIUMTEXT, LONGTEXT。对应的这四种BLOB类型,可存储的最大长度不同,可根据实际情况选择。
数据库管理基本操作 DML
【主要】增删改语句
登录mysql
cmd > mysql -hlocalhost -uroot -p123456
cmd > mysql -u root -p
Enter password: ******
展示当前数据库中所有的数据库
show databases;
创建数据库
create database dbName;
查询数据库创建过程中,SQL语句内容,查询创建数据库执行SQL语句情况
show create database dbName;
# +-------------+-----------------------------------------------------------------+
# | Database | Create Database |
# +-------------+-----------------------------------------------------------------+
# | javaee_2218 | CREATE DATABASE `javaee_2218` /*!40100 DEFAULT CHARACTER SET utf8 */ |
# +-------------+-----------------------------------------------------------------+
# 可以提供的信息是数据库创建使用的语句,以及当前数据库默认编码集。这里要求默认编码集为 utf8
创建数据库的过程中,约束限制编码集。
create database dbName character set latin1;
create database dbName character set gbk;
# +-------------+-----------------------------------------------------------------+
# | Database | Create Database |
# +-------------+-----------------------------------------------------------------+
# | javaee_2219 | CREATE DATABASE `javaee_2219` /*!40100 DEFAULT CHARACTER SET latin1 */
# +-------------+-----------------------------------------------------------------+
# +-------------+----------------------------------------------------------------+
# | Database | Create Database |
# +-------------+-----------------------------------------------------------------+
# | javaee_2220 | CREATE DATABASE `javaee_2220` /*!40100 DEFAULT CHARACTER SET gbk */ |
# +-------------+-----------------------------------------------------------------+
添加表数据
insert into tableName(fieldName1, fieldName2...) values (value1, value2...)
更新数据库表数据
update tableName set fieldName = value, fieldName = value ... [where condition]
删除数据库
-- 删除指定数据库
drop database dbName;
-- 删除表
delete form tableName [where condition]
更改数据库视图
use dbName;
# Database changed 提示: 接下来所有的[数据库] 操作都是在当前数据库中
select database();
# 查询当前操作的数据库是哪一个
【重点】数据库查询语句 DQL
条件查询
-- 通配符查询
select *
from tableName
where condition;
-- 字段查询
select fieldName [as 别名], filedName [别名]
from tableName
where condition;
-- distinct去重
select distinct fieldName
from tableName [where];
聚合函数
-- sum 总和
select sum(salary)
from table;
-- avg 求员工表所有员工的平均工资
select avg(coloumn)
from table;
-- max 找出员工表最高工资
select max(salary)
from table;
-- min 最低
select min(salary)
from table;
-- count 计数统计员工人数
select count(id)
from table;
-- * 通配符(选择全部),确定当前员工表有多少员工
select *
from table;
-- 在SQL中,1表示true,0表示false,记录所有行
select count(1)
from table;
分组查询
1. 语法
select 字段列表 from 表名 [where 条件] group by 分组字段名(可以多个分组字段名) [having 分组后过滤的条件]
select filedName secondName(别名,与查询内容不使用逗号隔开的名字) from 表名....
2. group by 多个分组字段
group by fieldName1, fieldName2... 中字段常被having使用。
-- 1. 首先按照 department_id 分组 -- 2. 然后按照 job_id 分组 -- 3. 计数员工人数 count(employee_id) select department_id, job_id, count(employee_id) from t_employees group by department_id, job_id; -- group by 可以将目标分组字段一并完成,顺序为分组操作相关顺序。
![]()
3. where 与 having 区别
执行时机不同: where是分组之前进行过滤,不满足where条件,不参与分组;
而having是对分组之后的结果进行过滤。
判断条件不同:where 不能使用聚合函数(sum ,count等),而having可以使用。
【注意】
执行顺序:where > 聚合函数 > having
分组之后,having查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义。
分组过滤查询
select fieldName
from tbName
where condition
group by 分组字段
having 分组之后的限定条件; -- 可以使用 where 替换
-- having 是在 group by 分组之后的条件限定关键字 (需要使用group by 中的关键字)。
排序查询
select fieldName
from tableName
order by fieldName1 asc(升序,默认), fieldName2 desc(降序)
-- 多字段排序查询,第一个字段相同,第二个字段进行比较...
分页查询/限定查询【限定查询结果条数,分页操作下标从0开始】
select *
from table
limit 5;
-- 限定查询结果为 5 条
-- limit offset, count
-- limit 10, 10 表示从下标10开始计数,数据行数10行
-- limit 0, 10 表示从下标 0 开始计数,数据行数10行
-- 分页,当数据量较大时,分页
-- 例如 一页有效数据 50个,一共10页
-- 格式总结:limit (pageCount - 1) * rowCount, rowCount; (多个查询)
-- pageCount 页码 rowCount 每一页多少行数据
-- 【项目中会完成】分页工具类
DQL 难点分析 【MySQL中下标,除了分页从0开始,其余都是从1开始】
from + 表名 从数据库的表中选择字段使用,与 select 字段无关。
select + 字段,从from的表中得到 字段对应的值 映射到最终结果表中。
where 约束 数据库中的表,所以可以使用表中所有字段,对表中数据进行约束。
【重点】因为having 是针对于 group by 分组后的表进行约束,所以只能查询group by 使用用来分组的字段值。
数据控制语言 DCL
用来管理数据库用户,控制数据库访问权限
查询用户
use mysql;
select * from user;
创建用户
create user '用户名'@'主机名' identified by '密码'
-- 主机名使用 % 说明在任意主机均可以访问该数据库
-- 'localhost' 表明只有在当前主机上才能访问该数据库
修改用户密码
alter user '用户名'@'主机名' identified with mysql_native_password by '新密码'
删除用户
drop user '用户名'@'主机名'
![image-20230321224728010](.\MySQL.assets\image-20230321224728010.png)
权限控制
-- 查询权限 'xm'@'%'
show grants for '用户名'@'主机名';
-- 授予权限
grant 权限列表 on 数据库名.表名 to '用户名'@'主机名'
-- 权限列表 all(全部权限privileges)
-- *.* 所有数据库的所有表
-- 撤销权限
revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名'
函数
MySQL字符串应用
-- 拼接多个字符串
-- concat(str1, str2, str3...)
select concat('今天', '郑州', '天气不错~~~')
-- 指定字符串,指定下标位置开始,要求指定字符个数,使用新字符串替换
-- insert(oldStr, off, len, newStr)
-- 数据库字符串下标从1开始
select insert('0123456', 0, 2, 'zz'); -- 字符串操作没有生效
select insert('0123456', 1, 2, 'zz'); -- 从第一个开始,长度2个
--有效
-- 字符串内容转大写
select upper('abc')
-- 字符串内容转小写
select lower('ABC')
-- 截取字符串,参数是指定操作字符串,off是指定下标位置开始,len是计数字符串字符个数
-- substring(str,off,len)
数值函数
日期函数
-- 第一个时间减去第二个时间
datediff('2021-10-01', '2021-12-01');
-- -60
流程控制函数
流程控制函数,可以在SQL语句找那个实现条件筛选,提高语句的效率。
-- if
select if(true, 'ok', 'error');
-- ifnull 'ok' 不为空返回 'ok'
select ifnull('ok','Default')
-- case when condition then return1 ...
-- 分数 >= 85 优秀 >= 60 及格 <60不及格
select id,
name,
math, -- 替换成 case when math >= 85 then '优秀' when math >= 60 then '及格' else '不及格' end
english,
chinese
from score
-- case [expr] when ... then ... when ... then ...
case fieldName when value1 then return1 when value2 then return2
约束
概述
用在创建表时,约束是作用于表中字段上的规则,用于限制表结构中所存储的数据。
目的:保证数据库中数据的正确性,有效性和完整性。
分类:
非空约束
not null
唯一约束
unique
主键约束
primary key
默认约束
default
自增约束 auto_increment (自增长)从1自动增长。
-- comment 用于注解
create table student1(
id int not null unique auto_increment primary key comment '学生ID',
name varchar(32) default '唐三' comment '学生姓名',
age int check(age > 0 and age <= 120) comment '年龄'
)
外键约束
在数据库层面,建立外键关联,保证数据的一致性和完整性。
语法
-- 创建时添加
create table 表名(
字段名 类型,
...,
[constraint] [外键名称] foreign key (外键字段名) references 主表(主表列名)
)
-- 修改添加
alter table tableName add constraint 外键名称 foreign key (tableName表中作为外键的字段名) references 主表(主表列名)
alter table 表名 drop foreign key 外键名称
alter table tableName add constr aint 外键名称 foreign key (外键字段) references 主表名(主表字段名) on update cascade on delete cascade;
【重点】多表查询(内联,左链接,右链接)
多表关系:
- 一对一
- 一对多
- 多对多
使用外键关联。
解决多表查询操作,表与表之间存在相对于的数据约束关系,数据限制关系,数据联系。
例如:员工表中的部门id 和 部门表中的 部门id 字段数据一一对应,可以通过多表链接获取,(员工表中数据)和(部门表中的数据)
基本格式
select fieldName1, fieldName2
from tbName1
连接符 tbName2
on 条件;
笛卡尔积【避免】
使用 条件约束 避免笛卡尔积
-- 栗子 select fieldNames from tableName1 [nickname], tableName2 [nickname]; -- 会出现笛卡尔积现象 -- 隐式内联使用where 语句可以优化 ... where table1.id = table2.id; --【注意】 如果表有别名那么不能使用表名了,只能使用别名操作。
-- 【导致笛卡尔积原因】因为没有任何的数据约束和限制,两个表查询目标字段数据一一匹配
-- 查询结果总行数 m * n
-- 应该使用 on+ 条件约束 对查询数据进行约束
select employee_id,first_name,department_name
from t_employees
inner join t_departments;
内链接(等值链接)
INNER JOIN:获取两个表中字段匹配关系的记录。
-- 隐式内连接 select fieldNames from tableName1, tableName2 where condition...; -- 显式内连接 select fieldNames from tableName1 [inner] join tableName2 on condition;
![]()
左外链接 【常用】
LEFT JOIN :匹配数据,获取左表(tableName1)中全部数据和两表的交集数据,即使右表没有数据。
select fieldNames from tableName1 left [outer] join tableName2 on condition;
右外链接
RIGHT JOIN:匹配数据,获取右表中全部数据和两表交集数据。与LEFT JOIN 相反。
select fieldNames from tableName1 right [outer] join tableName2 on condition;
![]()
自连接
自连接查询,可以是内查询,也可以是外查询
自查询表必须起别名。
select fieldNames from tableA [nicknameA] join tableB [nicknameB] on condition;
![]()
select a.name '员工', b.name '领导' from emp a, emp b where a.managerid = b.id -- 员工的 managerid => 领导的id 所以 a 表示员工表,b 表示领导表
联合查询-union,union all
对于union查询,就是把多次查询的结果直接合并起来,形成一个新的查询结果集。
select fieldNames from tableA ... union [all] select fieldNames from tableB ...; -- A的查询结果 和 B的查询结果直接拼接,所以列数必须相同。
子查询
基本格式
select fieldName
from tbNameA
where fieldName = (
select fieldName
from tbNameB
)
-- 子查询 (又称为嵌套查询)就是select 查询 嵌套 select 查询
子查询分类
根据子查询位置,分为:where 之后,from 之后,select 之后。
根据子查询结果不同分为:
标量子查询 (子查询结果为单个值)
-- 子查询结果是 id 字段值 -- Aid 和 Bid 有对应的关系 select * from tbA where Aid = ( select Bid from tbB where name = 'xx' )
列子查询 (子查询结果为一列 (多个元素) 使用 in all any关键字)
-- 找出和 xxx 同部门的员工 select * from t_employees where department_id in (select distinct department_id from t_employees where first_name = 'xxx'); -- field > all (子查询) 比所有的都大才可以返回 -- field > any (子查询) 比任意一个大就可以返回
行子查询 (子查询结果为一行(可以是多列))
-- 常用操作符 :=,<>,IN,NOT IN -- 查询与'xx' 的薪资及直属领导相同的员工信息 select salary, managerid from emp where name = 'xx'; select * from emp where salary = 10 and managerid = 1; -- 使用子查询 select * from emp where (salary, managerid) = (select salary, managerid from emp where name = 'xx');
表子查询 (子查询结果为多行多列)
-- 子查询作为临时表,作为其他查询结果的数据来源 -- 常用操作符 in -- 找出工资前五的员工信息 select * from t_employees order by salary desc; -- 将以上SQL结果,看作一个临时表,查询数据 -- 【仅演示】语法特征,不作为开发操作必备条件 select employee_id, first_name -- 必须是临时表中所包含的字段 from (select * from t_employees order by salary desc;) as tmp limit 5;
事务
事务简介
事务 是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,----这些操作要么同时成功,要么同时失败。
事务操作
事务栗子:
-- 栗子:转账操作 zs 给 ls 转账1000元 -- update tablename set fieldname = value, ...where condition -- 1. 查询zs 账号余额 select * from account where name = 'zs'; -- 2. 将 zs 账户余额-1000 update account set money = money - 1000 where name = 'zs' -- 3. 将 ls 账户余额+1000 update account set money = money - 1000 where name = 'ls' -- 查看/设置事务提交方式 select @@autocommit; -- 如果为 1 是自动提交事务,为 0 是手动提交事务。 set @@autocommit = 0; -- 设置为手动提交事务 -- 提交事务 commit; -- 回滚事务 rollback;
方式一 手动提交事务
-- 栗子的事务修改 :方式一 手动提交事务 select @@autocommit; set @@autocommit = 0; -- 然后执行语句 -- 1. 查询zs 账号余额 select * from account where name = 'zs'; -- 2. 将 zs 账户余额-1000 update account set money = money - 1000 where name = 'zs' 程序执行报错....故意设置错误 -- 3. 将 ls 账户余额+1000 update account set money = money - 1000 where name = 'ls' -- 执行成功,提交以上事务操作 commit; -- 执行出错,回滚事务 rollback;
方式二 【常用】自动提交事务 start transaction
-- 栗子的事务修改 :方式二 自动提交事务 set @@autocommit = 1 -- 开启事务 start transaction; -- 1. 查询zs 账号余额 select * from account where name = 'zs'; -- 2. 将 zs 账户余额-1000 update account set money = money - 1000 where name = 'zs' 程序执行报错....故意设置错误 -- 3. 将 ls 账户余额+1000 update account set money = money - 1000 where name = 'ls' -- 事务执行成功,提交 commit; -- 事务执行失败,回滚 rollback;
事务四大特性 ACID【面试问题】
- 原子性(atomicity):事务是不可分割的最小操作单位,要么全部成功,要么全部失败。
- 一致性(consistency):事务完成时,必须使所有的数据都保持一致。
- 隔离性(isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
- 持久性(durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
![]()
并发事务问题
脏读
B 事务 读取到 A 事务没有提交的事务 之前更新数据库的值,称为脏读。
不可重复读
幻读
事务A 本来想插入数据库中没有的数据,但是事务B 先执行了数据的插入操作,导致事务A 在插入数据时发现数据已经存在,不能在插入数据了,称为幻读现象。
事务隔离级别
Read uncommitted (Oracle默认)
可能出现所有的事务并发问题。
Read committed
只能解决脏读问题。
Repeatable Read (MySQL 默认)
只可能出现幻读问题。
Serializable
解决所有并发问题。
代码设置
-- 查看事务隔离级别
select @@transaction_isolation;
-- 设置事务隔离级别
-- session 会话级别:针对于当前客户端会话窗口有效
-- global 全局级别:针对于所有客户端会话窗口有效
set [session|global] transaction isolation level {READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE}
-- 例如:当前会话设置为 read uncommitted 级别
set session transaction isolation level read uncommitted;
![image-20230323002142555](.\MySQL.assets\image-20230323002142555.png)