数据库系统与Node.js
关系型数据库的范式
范式即为数据库设计标准
第一范式 1NF
定义
- 字段不可再分
举例
- 我们要存储体检者的双眼视力
- 那么应该存储左眼视力和右眼视力两个字段
- 即user表里应该有left_eye 和 right_eye
- 而不能把他们存在一个字段
第二范式 2NF
定义
- 基于1NF的基础上,要有键(键可以由多个字段组合)
- 所有字段分别完全依赖于键
- 如果键是多个字段组合,则不允许部分依赖于该键
依赖关系
- 给出键,就能确定唯一字段的值
- 如给出学号,就能确定唯一姓名,反之则不行
- 则称为姓名完全依赖于学号
第三范式 3NF
定义
- 一张表里不能存在两层依赖
- 给出学号,就能确定系名:系名依赖于学号
- 给出系名,就能确定系主任:系主任依赖于系名
- 所以,系主任间接依赖于学号
解决办法
- 把系名和系主任单独建表
总结
第一范式:属性不可分割
第二范式:字段完全依赖于键
第三范式:字段没有间接依赖于键
BC范式:键中的属性也不存在间接依赖
数据库设计经验
高内聚
- 把相关的字段放到一起,不相干的分开建表
- 如果两个字段能够单独建表,那就单独建表
低耦合
- 如果两个表之间存在弱关系
- 一对一可放在一个表,也可两个表加外键
- 一对多一般用外键
- 多对多建立中间表
一对一
- 假设一个学生只能加入一个班级
可以把班级放在学生表里
- 学生id:1001 姓名:小明 班级id:4002
- 班级 id:4002 名称:入门1班
也可以单独建立关联表
- 学生id:1001 姓名:小明
- 学生班级:id:2003 学生id:1001 班级id:4002
- 班级id:4002 名称:入门1班
一对多
- 假设一个作者能写多本书
单独建立关系表
- 作者id:1001 姓名:土豆
- 出版:id:2001 作者id:1001 书id:4002,出版社id
- 出版:id:2002 作者id:1001 书id:4003,出版id
- 书 id:4002 名称:斗马大陆
多对多
- 假设一个学生可以加入多个班级
- 当然每个班级也有多个学生
单独建立关系表
- 学生id:1001 姓名:小明
- 学生班级:id:2001 学生id:1001 班级id:4002,有效期
- 学生班级:id:2002 学生id:1001 班级id:4003,有效期
- 班级id:4002 名称:断章班
什么时候建立关联表
当关联自身存在属性时
- 比如关联的有效期,有效期为一年
- 比如关联的级别,店铺会员分为vip1~6
JOIN 连接表
- inner join、left join、right join、full outer join
语法
创建数据库
CREATE DATABASE db1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
show databases;
use db1;
创建表
CREATE TABLE user(id serial,name text);
CREATE TABLE staffs(id serial,name text);
CREATE TABLE orders(id serial,user_id bigint unsigned,staff_id bigint unsigned,amount int unsigned);
创建记录
insert into user (name) values ('XiaoMing');
insert into user (name) value ('XiaoHong');
insert into orders (user_id,staff_id,amount) values (1,2,100);
使用 inner join
select user.name as uname,orders.amount as amount from user inner join orders on user.id = orders.user_id;
输出结果
+----------+--------+
| uname | amount |
+----------+--------+
| XiaoMing | 100 |
inner join:只保留共同的部分,其他的不显示
Left join : 保留右边的 null ,以保证左边都显示
Right join:保留左边的null,以保证右边的都显示
Full outer join:保留两边的null,保证两边都显示
缓存字段
- 假设一个博客包含多个评论comments
如何获取博客的评论数
- 在blog表上加上一个comment_count 字段
- 每次添加 comment 则 +1
- 每次删除 comment 则 -1
事务
有些操作必须一次完成
- 用户评论之后,要做两件事
- 第一步,在comments 表新增记录
- 第二步,在blogs 表将对应的comment_count+1
- 如果第一步执行力,第二部麻衣执行 数据就乱了
使用事务
- start transaction;
- 语句1; 语句2; 语句3;
- commit;
- 只要有依据出错,则全部都不生效
MSYQL 存储引擎
命令 SHOW ENGINES;
常用的
- InnoDB - 默认,目前版本是新版 InnoDB
- MyISAM - 拥有较高的插入、查询速度、但是不支持事务
- Memory - 内存中,快速访问数据
- Archive - 只支持insert 和 select
InnoDB
- InnoDB 是事务型数据库的首选,支持事务、遵循ACID、支持行锁和外键
索引
语法
- CREATE UNIQUE INDEX index1 ON users(name(100))
- show index in users;
用途
- 提供查询效率
- where xxx > 100 那么就哭创建xxx的索引
- where xxx > 100 and yyy > 200,可以创建xxx, yyy的索引
Node.js 操作数据库
安装Docker
Max Docker
- 根据官方教程下载.dmg并安装即可
- 设置国内镜像https://docker.mirrors.ustc.edu.cn (设置完重启Docker)
- 默认需要注册才能下载
Windows
- 注册 https://hub.docker.com/
- 下载 Docker for windows installer (需要登录)
- 区别docker --version 成功返回版本号
- 设置国内镜像https://docker.mirrors.ustc.edu.cn (设置完重启Docker)
- 确保docker run hello-world 输出 Hello from Docker
- 如果因为Hyper-V等原因无法安装新版,可以使用旧版
Docker 安装 MySQL
步骤
- 进入Docker 上面 MySQL的主页
- 选择版本,如5.7.28或者8.0.18
- 使用docker run 命令启动容器
- name 是容器的名字
- MYSQL_ROOT_PASSWORD是密码
- tag 是版本号,这里选5.7.27
- 再加一个端口映射 -p 3306:3306
- 最终命令 docker run --name mysql1 -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -d mysql:5.7.28
Docker常用命令
命令
- 运行 docker ps 查看容器运行状态
- 运行 docker kill mysql1 关闭容器
- 运行 docker container start mysql1 开启刚刚关掉的容器
- 运行 docker rm mysql1 删除容器,必要时可以加 -f 强制删除
- 运行 docker run 启动新容器
注意
- docker 运行的容器,默认不会持久化
- 也就是说如果容器被删掉了,那么数据也就没了
如何连接mysql
Docker exec 命令
- docker exec -it mysql1 bash
- 这句命令会进入容器,容器里有一个Linux系统
- 然后就可以在这个系统里使用mysql
MySQL命令
- mysql -u root -p 回车,输入密码
- 命令 show databases;可以查看数据库列表
- 命令use xxx; 选择操作那个数据库
- 命令 show tables; 查看所有表
- 命令 select * from CHARACTER_SETS; 查看表内容
什么是数据库
数据库 Database
- 将大量数据保存起来,通过计算机加工而成的可以进行高效访问的数据集合称为数据库
- 根据存储格式不同,数据库一般被分为:关系型数据库 和 面向对象数据库、XML数据库、键值存储数据库、层次数据库
数据库管理系统DBMS
- 用来管理数据库的系统称呼为数据库管理系统
- 如MySQL、PostgreSQL、SQL Server、DB2、Oracle
DBMS的结构
mysql命令就是一个客户端,而mysql背后还有一个server在24小时不间断运行着
node.js操作数据库
yarn add mysql
var mysql = require('mysql');
var connection = mysql.createConnection({
host: '47.101.150.38',
user: 'root',
password: '123456',
});
connection.connect();
//CREATE TABLE IF NOT EXISTS user;
connection.query('CREATE DATABASE IF NOT EXISTS feng CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;', function (error, results, fields) {
if (error) throw error;
console.log('创建数据库');
console.log(results);
});
connection.query('use feng')
connection.query('CREATE TABLE IF NOT EXISTS user(name text,age int); ', function (error, results, fields) {
if (error) throw error;
console.log('创建表');
console.log(results);
});
connection.end();