我们作为前端人员不需要过多深入数据库,但需要了解一些,便于node开发
数据库简介
复习:数据库事务
简称:事务。是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含有以下两个目的:
为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。
当事务被提交给了DBMS(数据库管理系统),则DBMS(数据库管理系统)需要确保该事务中的所有操作都成功完成且其结果被永久保存在数据库中,如果事务中有的操作没有成功完成,则事务中的所有操作都需要被回滚,回到事务执行前的状态;同时,该事务对数据库或者其他事务的执行无影响,所有的事务都好像在独立的运行。
但在现实情况下,失败的风险很高。在一个数据库事务的执行过程中,有可能会遇上事务操作失败、数据库系统/操作系统失败,甚至是存储介质失败等情况。这便需要DBMS对一个执行失败的事务执行恢复操作,将其数据库状态恢复到一致状态(数据的一致性得到保证的状态)。为了实现将数据库状态恢复到一致状态的功能,DBMS通常需要维护事务日志以追踪事务中所有影响数据库数据的操作。
事务是作为一个逻辑单元执行的一系列操作,一个逻辑工作单元必须有四个属性,称为 ACID(原子性、一致性、隔离性和持久性)属性,只有这样才能成为一个事务:
1. 原子性:事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。
2. 一致性:
- 事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。
- 事务结束时,所有的内部数据结构(如 B 树索引或双向链表)都必须是正确的。
- 隔离性:
- 由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。这称为可串行性,因为它能够重新装载起始数据,并且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。
持久性:事务完成之后,它对于系统的影响是永久性的。该修改即使出现系统故障也将一直保持。
数据库能干什么
持久的存储数据:数据存储在硬盘文件中
- 备份和恢复数据
- 快速的存取数据
- 权限控制
数据库的类型
关系型数据库
特点:以表和表的关联构成的数据结构
- 学生表、教室表、账号密码表等
优点:
- 能表达复杂的数据关系;
- 具有强大的查询语言,能精确查找到想要的数据
缺点:
- 读写性能比较差,尤其是海量数据的读写
- 数据结构比较死板(比较稳定,不能动态的增加一列)
用途:存储结构复杂的数据。后端比较常用
代表:Oracle、Sql Server、MySql
非关系型数据库
特点:以及其简单的结构存储数据
- 文档型:一个用户就是一篇文档
- 键值对
优点:
- 格式灵活
- 海量数据的读写效率很高
缺点:
- 难以表示复杂的数据结构
- 对于复杂查询效率不好
用途:存储结构简单的数据
代表:MongoDB,Redis,Membase
面向对象数据库
略,java,c#等后端开发者使用,可以把内存中的对象,经过适配,原封不动的存入数据库
术语
DB:database 数据库
DBA:database administrator 数据库管理员
DBMS:database management system 数据库管理系统,比如:mysql
DBS:database system 数据库系统,DBS包含DB、DBA、DBMS
☆mysql的安装
安装与使用流程
- mysql特点:关系型数据库、瑞典MySQL AB(已被Oracle收购)、开源、轻量、快速
- 安装MySQL
- 下载:官方下载源、腾讯下载源(这下载快一些),mac与windows安装流程有些区别
- 安装一个 Server only 就够我们的node开个服务用了
- 有些依赖要安装,它会帮我们自动安装
- 会有个超级管理员密码设置
- 基本是点next就行
- 要在服务(本地)中开启mysql的本地系统服务。在cmd: services.msc里找,可设为开机自动启动
- 有个网络服务,我不知道要不要开
- 然后把mysql的mysql server的bin目录加到系统环境变量的Path中
- 使用
- mysql -uroot -p:进入mysql命令交互,然后需要输入那会设置的root密码
- show variables like “character_set_%”;
- 如果是gbk编码,就要改成utf-8
- 在my.ini修改,它是个隐藏文件,在 C:\Program Files\MySQL\MySQL Server 5.7
- 设置mysql客户端默认字符集:default-character-set=utf8
- 服务端使用的字符集默认为8比特编码的latin1字符集:character-set-server=utf8mb4
- mb4代表着utf-8最多可以用四个字节表示,因为默认汉字是3个字节嘛,但生僻字可能显示不了
- 要把my.ini放到mysql的安装目录,C:\Program Files\MySQL\MySQL Server 5.7,然后关闭再重新启动mysql服务
- net stop mysql57
- net start mysql57
- show databases:查看当前拥有的数据库,有5个默认的,千万不要动!
- 总用命令行来操作数据库,会很麻烦,所以要用到:Navicat图形化界面,百度找破解版。吾爱破解中有
- 点击连接->MySQL,起个连接名,输入刚才的root密码
- 一般在查询界面,新建查询,写mysql语句
mysql忘记root密码如何解决
我是mysql server5.7的版本,按照如下方法解决了
最近笔者的一台mysql服务器忘记了超级管理员root密码,而mysql修改密码必须得知道旧密码的情况下才能进行,真的很郁闷,找了很多方法都无 效,最终找到了解决方法,拿来分享,希望能给同样遭遇的朋友带来帮助。只要你的服务器操作系统是Windows XP/2000/2003/NT都可以使用这个方法来重新修改mysql超级管理员root的密码。
1、停止mysql服务
开始→运行→输入cmd,打开命令提示符窗口,然后输入net stop mysql,相关截图如下所示: 2、在命令提示符窗口中进入mysql安装目录中的bin目录
在命令提示符窗口中分别输入以下两条命令,每输入一条命令按回车键:
d:
cd www\mysql\bin 3、进入mysql安全模式(这样不用输入密码就能进入mysql数据库)
输入mysqld-nt –skip-grant-tables,此时屏幕上可能没啥反应,不用管它,放在一边接着往下做,相关截图如下所示: 4、重新打开一个cmd命令提示符窗口,开始→运行→输入cmd,在新打开命令提示符窗口中输入mysql -uroot -p,然后提示输入密码,不用输入密码,直接按回车键,相关截图如下所示: 5、输入以下命令开始修改超级管理员root密码(注意:命令中mysql.user中间有个“点”;命令中的htmer.com即为新密码,根据你的实际情况,换成你的新密码即可)
update mysql.user set password=PASSWORD(‘htmer.com’) where User=’root’; 6、刷新权限表,输入如下命令
flush privileges; 7、退出,输入如下命令
quit 8、重新启动mysql服务(也可以直接重启服务器)
①打开任务管理器,将mysql-nt.exe这个进程结束,相关截图如下所示: ②在命令提示符中输入net start mysql即可重新启动mysql服务了,
数据库设计
SQL
- Structured Query Language 结构化查询语言
- 大部分关系型数据,拥有着基本一致的SQL语法
- 分支:
- DDL:Data Definition Language 数据定义语言
- 操作数据库对象:库、表、视图、存储过程
- DML:Data Manipulation Language 数据操控语言,是我们后续学习的重点
- 操作数据库中的记录。添加、删除行啥的
- DCL:Data Control Language 数据控制语句
- 操作用户权限
- DDL:Data Definition Language 数据定义语言
数据库中sql语句不区分大小写!sql中的对象名称要用反引号
xxxx
;管理库
有些操作进行后需要刷新一下。
创建库
create database `test`;
切换当前库
use databaseName;-- 图形化界面可能还在当前库,但是执行期间肯定是会切过去的
删除库
drop database databaseName;
管理表
创建表:创完之后,若没显示,就刷新一下表
- 字段:字段名、字段类型、长度、不是null、自增、默认值等
- 字段类型:bit: 1位, 0/1,false/true。int:32位,整数。decimal(M,N):能精确计算的实数,M是总的数字位数,N是小数位数。char(n):固定长度位为n的字符。varchar(n):长度可变,最大长度为n位。text:大量的字符。date:仅日期。datetime:日期或时间。time:仅时间
- 字段:字段名、字段类型、长度、不是null、自增、默认值等
- 修改表:alter table 库名.表名;
- 删除表:drop table 库名.表名;
主键和外键
主键:根据设计原则,每张表都要有主键
主键必须满足的要求:存在且唯一、不能更改、无业务含义(因为有业务含义就可能是变化的数据)
uuid(); 这个函数就可以每次生成一个全局的主键
外键:用于产生表关系的列
外键列会连接到另一张表(或自己)的主键。被引用成为了外键的表,无法肆意删除!
外键可以为空,此时就没有产生关系了
表关系
- 一对一:一个A对应一个B,一个B对应一个A
- 例如:用户和用户信息
- 把任意一张表的主键同时设置为外键
- 一对多:一个A对应多个B,一个B对应一个A,A和B是一对多,B和A是多对一
- 例如:班级和学生,用户和文章
- 在多的那一端(学生)的表上设置外键,对应到另一张表(班级)的主键
- 多对多:一个A对应多个B,一个B对应多个A
- 例如:学生和老师
- 需要新建一张关系表,关系表至少包含两个外键,分别对应到两张表
三大设计范式
- 要求数据库表的每一列,都是不可分割的原子数据项
- 非主键列必须依赖于主键列(比如:广告和学生信息,没意义啊,不能这样)
- 非主键列必须直接依赖于主键列(比如:班级名称和学生信息,也不对,因为它直接依赖的是班级信息)
表记录的增删改
DML:
- 增:CREATE
- 查:Retrieve
- 改:UPDATE
- 删:DELETE
连起来,简称为:CRUD
-- 增加语句
INSERT INTO `student`(stuno,`name`, birthday,phone,classid)
VALUES('500', '成哥', '1900-1-1', '13344445555', 2),
('501', '邓哥', '1900-1-2', '13344445556', 2);
--删除行
DELETE FROM student
WHERE `name`='袁哥';
--改变行
UPDATE student SET `name`='邓旭名'
WHERE id=12;
☆单表基本查询
MySQL的语句一共分为11步,如下图所标注的那样,最先执行的总是FROM操作,最后执行的是LIMIT操作。其中每一个操作都会产生一张虚拟的表,这个虚拟的表作为一个处理的输入,只是这些虚拟的表对用户来说是透明的,但是只有最后一个虚拟的表才会被作为结果返回。如果没有在语句中指定某一个子句,那么将会跳过相应的步骤。
ps: 在mysql中having其实应该在select之后,sql中是having在前。
实际运行顺序如下:
FORM: 对FROM的左边的表和右边的表计算笛卡尔积。产生虚表VT1
ON: 对虚表VT1进行ON筛选,只有那些符合<join-condition>的行才会被记录在虚表VT2中。
JOIN: 如果指定了OUTER JOIN(比如left join、 right join),那么保留表中未匹配的行就会作为外部行添加到虚拟表VT2中,产生虚拟表VT3, rug from子句中包含两个以上的表的话,那么就会对上一个join连接产生的结果VT3和下一个表重复执行步骤1~3这三个步骤,一直到处理完所有的表为止。
WHERE: 对虚拟表VT3进行WHERE条件过滤。只有符合<where-condition>的记录才会被插入到虚拟表VT4中。
GROUP BY: 根据group by子句中的列,对VT4中的记录进行分组操作,产生VT5.
CUBE | ROLLUP: 对表VT5进行cube或者rollup操作,产生表VT6.
SELECT: 执行select操作,选择指定的列,插入到虚拟表VT8中。
-- mysql中having是在select之后的
HAVING: 对虚拟表VT6应用having过滤,只有符合<having-condition>的记录才会被 插入到虚拟表VT7中。
DISTINCT: 对VT8中的记录进行去重。产生虚拟表VT9.
ORDER BY: 将虚拟表VT9中的记录按照<order_by_list>进行排序操作,产生虚拟表VT10.
LIMIT:取出指定行的记录,产生虚拟表VT11, 并将结果返回。
查询会遍历物理表的行,来提取我们要的数据。查询结果是结果表,结果表来自于物理表,但不同于物理表。
select可以与下述关键字联用:
- 定义别名(as是可选的):select ismal as 性别 from employee
- select *:星号表示查询所有列
- case…end…:对某一列的数据进行进一步的处理
- distinct:对select之后的虚拟表去重。注意!:distinct后面所有的字段的值全相同时,才去重!
select id, name,
case ismale
when 1 then 男
else 女
end sex,-- sex是该列的结果表中的新列名
when salary>=10000 then 高
when salary>=5000 then 中
else 低
end salary_level
from employee;
where: 对结果表进行进一步筛选,where后面可跟如下字段:
- =:是否相等
- in:in后面可以跟一个范围值
- is/is not:is null,is not null,这样就可以查是不是null了
/>=/</<=:比较大小
- between:在…之间,比如:between 24000 and 36000
- like:包含…,后面是模糊匹配表达式,类似正则。
- 比如:where name like 吴_%; 下划线是任意一个字符,%就是后面随便是啥
- and/or:多个条件的并列
SELECT * from employee
WHERE `name` like '张%' and (ismale=0 and salary>=12000
or
birthday>='1996-1-1');
order by:asc升序,desc降序。它在select语句之后
- limit n, m; :最后执行,表示跳过前面n条数据,连续取m条数据 ```sql — 查询user表,得到账号为admin,密码为123456的用户 — 登录
SELECT * from user
WHERE loginid = ‘admin’ and loginpwd = ‘123123’;
— 查询员工表,按照员工的入职时间降序排序,并且使用分页查询 — 查询第3页,每页5条数据 — limit (page-1)*pagesize, pagesize
SELECT * FROM employee ORDER BY employee.joinDate desc LIMIT 10,5
— 查询工资最高的女员工
SELECT * FROM employee WHERE ismale = 0 ORDER BY salary desc limit 0,1;
<a name="TgXsP"></a>
# 联表查询
内连接:指连接结果仅包含符合连接条件的行,参与连接的两个表都应该符合连接条件。<br />外连接:连接结果不仅包含符合连接条件的行同时也包含自身不符合条件的行。包括左外连接、右外连接和全外连接。
1. 全连接:笛卡尔积,M * N + N * M。
1. 自连接需要给别名
2. 左连接,左外连接,...left join...on...:on后面是判断条件
1. 哪怕找不到左表也会有一行记录,只是有的数据是null而已
3. 右连接,右外连接,...right join...on...:用法同上,左改为右即可
3. 内连接,inner join:**不满足条件的不会有行记录**
```sql
-- 1. 创建一张team表,记录足球队
-- 查询出对阵表
SELECT t1.name 主场, t2.name 客场
FROM team as t1, team as t2
WHERE t1.id != t2.id;
-- 2. 显示出所有员工的姓名、性别(使用男或女显示)、入职时间、薪水、所属部门(显示部门名称)、所属公司(显示公司名称)
SELECT e.`name` 员工姓名,
case ismale when 1 then '男' else '女' end 性别,
e.joinDate 入职时间,
e.salary 薪水,
d.`name` 部门名称,
c.`name` 公司名称
FROM employee e
inner join department d on e.deptId = d.id
inner join company c on d.companyId = c.id
-- 3. 查询腾讯和蚂蚁金服的所有员工姓名、性别、入职时间、部门名、公司名
SELECT e.`name` 员工姓名,
case ismale when 1 then '男' else '女' end 性别,
e.joinDate 入职时间,
e.salary 薪水,
d.`name` 部门名称,
c.`name` 公司名称
FROM employee e
inner join department d on e.deptId = d.id
inner join company c on d.companyId = c.id
WHERE c.`name` in ('腾讯科技', '蚂蚁金服')
-- 4. 查询渡一教学部的所有员工姓名、性别、入职时间、部门名、公司名
SELECT e.`name` 员工姓名,
case ismale when 1 then '男' else '女' end 性别,
e.joinDate 入职时间,
e.salary 薪水,
d.`name` 部门名称,
c.`name` 公司名称
FROM employee e
inner join department d on e.deptId = d.id
inner join company c on d.companyId = c.id
WHERE c.`name` like '%渡一%' AND d.`name` = '教学部';
-- 5. 列出所有公司员工居住的地址(要去掉重复)
select DISTINCT location from employee;
子查询
☆函数和分组
内置函数
数学
- ABS(x) 返回x的绝对值
- CEILING(x) 返回大于x的最小整数值
- FLOOR(x) 返回小于x的最大整数值
- MOD(x,y) 返回x/y的模(余数)
- PI() 返回pi的值(圆周率)
- RAND() 返回0到1内的随机值
- ROUND(x,y) 返回参数x的四舍五入的有y位小数的值
-
聚合—最常用
聚合函数一般跟在select语句中
聚合函数后面不能有其他普通列字段!不要写: select avg(salary) as avg, id 这种代码,salary id是两个字段 AVG(col) 返回指定列的平均值
- COUNT(col) 返回指定列中非NULL值的个数,这个函数用的最多!
- count(*) 尽量不要用,占内存,它把每行的所有字段都数了,只要有一个字段不为null,就算存在
- MIN(col) 返回指定列的最小值
- MAX(col) 返回指定列的最大值
- SUM(col) 返回指定列的所有值之和
可以写多个聚合函数,但是聚合函数后面,一定不要跟普通列字段!!
SELECT count(id) as 员工数量,
avg(salary) as 平均薪资,
sum(salary) as 总薪资,
min(salary) as 最小薪资
FROM employee;
字符
- CONCAT(s1,s2…,sn) 将s1,s2…,sn连接成字符串
- CONCAT_WS(sep,s1,s2…,sn) 将s1,s2…,sn连接成字符串,并用sep字符间隔
- TRIM(str) 去除字符串首部和尾部的所有空格
- LTRIM(str) 从字符串str中切掉开头的空格
-
日期
CURDATE()或CURRENT_DATE() 返回当前的日期:2021-9-21
- CURTIME()或CURRENT_TIME() 返回当前的时间:20:01:30
- TIMESTAMPDIFF(part, date1,date2) 返回date1到date2之间相隔的part值,part是用于指定的相隔的年或月或日等,part可取值如下:
- MICROSECOND,SECOND,MINUTE,HOUR,DAY,WEEK,MONTH,QUARTER,YEAR
自定义函数
分组
按xxx分组,分组后,只能查询分组的列和聚合列!
-- 查询员工分布的居住地,以及每个居住地有多少名员工
-- 天府三街 3
SELECT location, count(id) as empnumber
FROM employee
GROUP BY location
HAVING empnumber>=40
-- 查询所有薪水在10000以上的员工的分布的居住地,然后仅得到聚集地大于30的结果
SELECT location, count(id) as empnumber
FROM employee
WHERE salary>=10000
GROUP BY location
HAVING count(id)>=30
SQL标准要求HAVING必须引用GROUP BY子句中的列或用于总计函数中的列。不过,MySQL支持对此工作性质的扩展,并允许HAVING引用SELECT清单中的列和外部子查询中的列。
如果HAVING子句引用了一个意义不明确的列,则会出现警告。在下面的语句中,col2意义不明确,因为它既作为别名使用,又作为列名使用:
mysql> SELECT COUNT(col1) AS col2 FROM t GROUP BY col2 HAVING col2 = 2;
标准SQL工作性质具有优先权,因此如果一个HAVING列名既被用于GROUP BY,又被用作输出列清单中的起了别名的列,则优先权被给予GROUP BY列中的列。
复习:mysql中语句优先级
from
join on
where
group by
select distinct
having
order by
limit
练习题:
-- 1. 查询渡一每个部门的员工数量
SELECT d.`name`, COUNT(e.id) as number
FROM company as c INNER JOIN department as d on c.id = d.companyId
INNER JOIN employee as e on d.id = e.deptId
WHERE c.`name` like '%渡一%'
GROUP BY d.id, d.`name`;
-- 2. 查询每个公司的员工数量
SELECT c.`name`, COUNT(e.id) as number
FROM company as c INNER JOIN department as d on c.id = d.companyId
INNER JOIN employee as e on d.id = e.deptId
GROUP BY c.id, c.`name`
-- 3. 查询所有公司5年内入职的居住在万家湾的女员工数量,没有就显示0
SELECT c.`name`, CASE WHEN r.number is NULL THEN 0 ELSE r.number END as number
FROM company c LEFT JOIN (SELECT c.id, c.`name`, COUNT(e.id) as number
FROM company as c INNER JOIN department as d on c.id = d.companyId
INNER JOIN employee as e on d.id = e.deptId
WHERE TIMESTAMPDIFF(YEAR,e.joinDate,CURDATE())<=5
AND e.location like '%万家湾%'
AND e.sex === 0
GROUP BY c.id, c.`name`) as r on c.id = r.id
-- 4. 查询渡一所有员工分布在哪些居住地,每个居住地的数量
SELECT e.location, count(e.id) as empnumber
FROM company as c INNER JOIN department as d on c.id = d.companyId
INNER JOIN employee as e on d.id = e.deptId
WHERE c.`name` LIKE '%渡一%'
GROUP BY e.location
-- 5. 查询员工人数大于200的公司信息
SELECT * FROM company
WHERE id in (
SELECT c.id
FROM company as c INNER JOIN department as d on c.id = d.companyId
INNER JOIN employee as e on d.id = e.deptId
GROUP BY c.id, c.`name`
HAVING count(e.id)>=200
)
-- 6. 查询渡一公司里比它平均工资高的员工
SELECT e.*
FROM company as c INNER JOIN department as d on c.id = d.companyId
INNER JOIN employee as e on d.id = e.deptId
WHERE c.`name` LIKE '%渡一%' AND
e.salary>(
-- 查询渡一的平均薪资
SELECT avg(e.salary)
FROM company as c INNER JOIN department as d on c.id = d.companyId
INNER JOIN employee as e on d.id = e.deptId
WHERE c.`name` LIKE '%渡一%'
)
-- 7. 查询渡一所有名字为两个字和三个字的员工对应人数
SELECT CHAR_LENGTH(e.`name`) as 姓名长度, COUNT(E.ID) as 员工数量
FROM company as c INNER JOIN department as d on c.id = d.companyId
INNER JOIN employee as e on d.id = e.deptId
WHERE c.`name` LIKE '%渡一%'
GROUP BY CHAR_LENGTH(e.`name`)
HAVING 姓名长度 in (2,3)
-- 8. 查询每个公司每个月的总支出薪水,并按照从低到高排序
SELECT c.`name`, SUM(e.salary) as sumofsalary
FROM company as c INNER JOIN department as d on c.id = d.companyId
INNER JOIN employee as e on d.id = e.deptId
GROUP BY c.id, c.`name`
ORDER BY sumofsalary
视图
根据查询,缓存了表格,在这个缓存表中查。这个缓存表就是视图,视图的表在内存中,而不在硬盘里!
操作视图属于DDL,一个视图就是一个对象
CREATE VIEW 库名.视图名 AS 查询语句-- 这个就是创建视图;删除 修改 增加啥的看api吧
使用视图,就可以减少sql语句,从而提升了网络传输的速度。