MySQL+数据迁移

01-数据库开篇

1.什么是数据库?

  • 数据库就是存储数据的仓库
  • 数据库和Excel很像, Excel本质是一个文件, 数据库本质上也是一个文件,
  • 数据库和Excel很像, Excel文件中可以有很多的表, 一个数据库中也有很多的表
  • 数据库和Excel很像, Excel文件的每张表就是用来存储数据的, 数据库文件的每张表也是用来存储数据的
  • 所以学习数据库和学习Excel差不多,
    就是学习如何新建数据库文件,如何在数据库中新建表,如何往表中存储数据和操作表中的数据

2.什么是MySQL?

  • MySQL是由瑞典AB 公司开发"关系型"数据,目前属于 Oracle 旗下产品
  • 由于MySQL是开源免费的, 所以现在是市面上最受欢迎的数据库之一

3.什么是关系型数据库?

  • 3.1数据库萌芽阶段:

  • 最初程序员们把磁盘上的文件当做数据库来使用,但是由于文件不方便管理和不安全,
    所以后来就有了第一代数据库(层次模型数据库和网状模型数据库)

    • 网状模型数据库是层次模型数据库的升级版
    • 层次模型数据库类似于HTML结构, 是通过树状结构来保存管理数据的
    • 在层次模型数据库中有且仅有一个结点没有父结点,它就是根结点
    • 在层次模型数据库中其它结点只能有一个父结点
    • 在层次模型数据库中所有的子节点不能脱离父节点而单独存在
  • 后来由于层次模型和网状模型数据库的结构比较复杂,不利于数据库的维护和重建
    所以就有了第二代数据库(关系型数据库和非关系型数据库)

  • 3.2第一代数据库:

  • 层次模型数据库

    • 典型代表是IBM公司的IMS(Information Management System)数据库管理系统
      在这里插入图片描述
  • 网状模型数据库

    • 电信代表是HP公司的IMAGE、Cullinet Software公司的 IDMS、Univac公司的 DMS1100
  • 3.3第二代数据库:

  • 关系型数据库

    • 每个表都是独立的
    • 表与表之间通过公共字段来建立关系(例如ID字段)
    • 多表查询效率低
    • SQL Server/Oracle/MySQL
      在这里插入图片描述
  • 非关系型数据库

    • 解决了关系型数据库多表查询的效率问题
    • Redis、Mongodb等

02-数据库安装

1.安装包下载地址:
https://dev.mysql.com/downloads/mysql/

2.安装过程:
2.1选择自定义安装
2.2选择要安装的版本
2.3修改安装路径
2.4设置数据库密码
2.5其它一律下一步

3.测试是否安装成功
3.1配置环境变量, 将安装目录中bin文件夹的地址放到环境变量path中
3.2在终端输入 mysql --help 或者 mysql --version

03-数据库链接和断开

1.链接mysql服务器
mysql -h127.0.0.1 -P3306 -uroot -proot
-h 主机地址
-P 端口号
-u 用户名
-p 用户密码
注意点:
1.1如果MySQL服务器在本地, 主机地址可以省略
1.2如果服务器使用默认3306端口, 端口号可以省略

mysql -h127.0.0.1 -P3306 -uroot -proot 链接远程MySQL服务器
mysql -h127.0.0.1 -uroot -proot 链接远程MySQL服务器, 使用默认端口3306
mysql -uroot -proot 链接本地的MySQL服务器, 使用默认端口3306
mysql -uroot -p 密码采用暗文形式

最常用的方式:
mysql -h127.0.0.1 -uroot -p 链接远程MySQL服务器, 使用默认端口3306

2.退出链接
exit;
quit
\q

3.显示数据库
show databases;
注意: 后面有s

3.1 information_schema
保存着关于MySQL服务器所维护的所有其他数据库的信息。
如数据库名,数据库的表,表栏的数据类型与访问权限等
3.2 mysql
MySQL系统数据库, 保存了登录用户名,密码,以及每个用户的权限等等
3.3 performance_schema
用来保存数据库服务器性能的参数
3.4 sys
这个库是通过视图的形式把information_schema和performance_schema结合起来,查询出更加令人容易理解的数据

04-数据库SQL简介

1.如何使用MySQL
1.0学习MySQL和学习Excel差不多
使用Excel步骤: 安装Excel-创建Excel文件-创建表-在表中存储数据
使用MySQL步骤: 安装MySQL-创建数据库----创建表-在表中存储数据

1.1学习MySQL本质上就是学习数据库的增删改查(CRUD)
1.2在MySQL中主要有三种类型的增删改查, 分别是:
1.2.1数据库的增删改查
1.2.2表的增删改查
1.2.3数据的增删改查
MySQL中可以有0~N个数据库, 数据库中可以有0~N个表, 表中可以有0~N个数据

2.什么是SQL?
结构化查询语言(Structured Query Language)简称
是用来操作关系型数据库的一门语言
是一个关系型数据库通用的操作语言

注意点:
各大厂商为了更多的占用市场份额, 都会在标准的SQL基础上扩展一些自己的东西
例如: SQL Server 使用T-SQL
Oracle 使用PL/SQL
MySQL 使用MySQL

3.SQL语句功能划分

  • DDL:数据定义语句
    用来定义数据库对象:创建库,表,列等。
  • DML:数据操作语句
    用来操作数据库表中的记录
  • DQL:数据查询语句
    用来查询数据
  • DCL:数据控制语句
    用来定义访问权限和安全级别

4.SQL数据类型
和常见的编程语言一样, 数据库中存储的数据也是区分类型的
MySQL中支持的数据类型大致可以分为三类: 数值类型、字符串类型和日期和时间类型
https://dev.mysql.com/doc/refman/8.0/en/data-types.html
MySQL中常用的数据类型如下:
double:浮点型,例如double(5,2)表示最多5位,其中必须有2位小数,即最大值为999.99;
char:固定长度字符串类型; char(10) 'lnj ’
varchar:可变长度字符串类型;varchar(10) ‘lnj’
text:字符串类型;
blob:二进制类型;
date:日期类型,格式为:yyyy-MM-dd;
time:时间类型,格式为:hh:mm:ss
datetime:日期时间类型 yyyy-MM-dd hh:mm:ss
注意点: 在mysql中,字符串类型和日期类型都要用单引号括起来。‘lnj’ ‘2022-02-02’

05-数据库增删改查

1.创建数据库
create database [if not exists] 数据库名称 [charset=字符集];

示例一:
create database stu;
注意点: 以上语句, 如果MySQL中已经有了名称叫做stu的数据库, 再执行就会报错

示例二:
create database if not exists person;
注意点: 以上语句, 如果MySQL中已经有了名称叫做person的数据库, 并不会报错, 而是跳过这条语句

示例三:
create database if not exists it666 charset=utf8;
注意点: 为了避免将来读取的字符集和存储的字符集不一样导致乱码问题,
在创建数据库的时候, 我们还需要指定当前创建的数据库将来使用什么编码方式存储数据

2.如何查看数据库全局默认的编码
show variables like ‘character_set_%’;

3.如果查看某个数据库的编码
show create database person;

4.特殊的数据库名称处理
create database if not exists create charset=utf8;
注意点: 如果数据库的名称是SQL的关键字或者是一些特殊字符#~@*&…, 这个时候就需要用反引号括起来

1.如何删除数据库
drop database [if exists] 数据库名称;

示例一:
drop database stu;
注意点: 以上语句, 如果MySQL中没有要删除的数据库, 那么就会报错

示例二:
drop database if exists person;
注意点: 以上语句, 如果MySQL中没有要删除的数据库, 那么就会跳过, 并不会报错

1.如何修改数据库
alter database 数据库名称 charset=字符集;
alter database it666 charset=utf8;

1.如何查看数据库
show create database 数据库名称;
show databases;

新建-- C/增加
create database [if not exists] 数据库名称 [charset=字符集];

删除-- D/删除
drop database [if exists] 数据库名称;

修改-- U/修改
alter database 数据库名称 charset=字符编码

读取-- R/查看
show databases;
show create database 数据库名称;

06-表增删改查

注意点:
在对数据库的表进行操作的时候(增删改查), 都必须先告诉MySQL我们要操作的是哪一个数据库
use 数据库名称;

1.如何查看数据库中有哪些表?
show tables;

2.如何查看指定表的结构
desc 表名;

1.创建表
create table 表名(
字段名称 数据类型,
字段名称 数据类型,
字段名称 数据类型,
字段名称 数据类型,
);

示例一:
create table stu(
id int,
name text
);
注意点: 以上代码创建表, 如果表已经存在了, 那么就会报错

示例二:
create table if not exists person(
id int,
name text
);
注意点: 以上代码创建表, 没有就会创建一个新的, 有就会自动跳过

1.删除表
drop table 表名;
示例一:
drop table stu;
注意点: 以上语句, 如果删除的表不存在, 那么就会报错

示例二:
drop table if exists person;
注意点: 以上语句, 如果需要删除的表存在, 那么就直接删除, 如果不存在就跳过

1.修改表
6.0修改表名
rename table 原始名称 to 新的名称;
rename table stu to person;

6.1添加字段
alter table 表名 add 新增字段名称 新增字段数据类型 [位置];
alter table person add age int;
注意点: 默认情况下会将新增的字段放到原有字段的后面

alter table person add score float first;
注意点: 我们可以通过指定first将新增的字段放到原有字段的前面

alter table person add phone int after name;
注意点: 我们可以通过after指定将新增的字段放到哪个字段的后面

6.2删除字段
alter table 表名 drop 字段名称;
alter table person drop phone;

6.3修改字段
6.3.1修改字段的数据类型
alter table 表名 modify 需要修改的字段名称 新的数据类型
alter table person modify score double;

6.3.2修改字段的名称和数据类型
alter table 表名 change 原始字段名称 新的字段名称 新的数据类型;
alter table person change age addr text;

1.MySQL存储引擎

  • MySQL中的存储引擎就好比我们现实生活中的银行, 不同的银行提供的安全级别、服务水平、存储功能不一样
    和现实生活中一样在MySQL也有各种各样不同的银行, 这些银行我们称之为存储引擎
    和现实生活中一样MySQL中不同的存储引擎提供的安全级别、服务水平、存储功能等也不一样

  • MySQL中有三种存储引擎, 分别是:

    • MyISAM: 安全性低, 但不支持事务和外键, 适合频繁插入和查询的应用
    • InnoDB(默认): 安全性高, 支持事务和外键, 适合对安全性, 数据完整性要求较高的应用
    • Memory: 访问速度极快, 但不会永久存储数据, 适合对读写速度要求较高的应用
      create table stu(
      id int,
      name text
      )engine=引擎名称;

2.不同引擎本质

  • 前面我们说过数据库的本质就是文件, 所以我们可以先观察一下
  • 通过我们的观察, 我们发现只要创建一个数据库就会自动创建一个文件夹
  • 通过我们的观察, 我们发现只要创建一张表就会在指定的数据库文件夹中创建一个文件
  • 创建表的时候自动创建的这个文件就保存了这张表的结构

create table stu(
id int,
name varchar(20)
)engine=Memory;

InnoDB:
- 如果表的存储引擎是InnoDB, 那么只要创建表就会自动创建一个文件, 这个文件就保存了这张表的结构
- 如果往InnoDB的表中存储数据, 那么数据会被存储到ibdata1的文件中, 如果存储的数据比较多, 那么系统会自动再创建ibdata2, ibdata3, …文件

MyISAM:
- 如果表的存储引擎是MyISAM, 那么只要创建表就会自动创建三个文件
+ .sdi这个文件就保存了这张表的结构
+ .MYD这个文件就保存了这张表中存储的数据
+ .MYI这个文件就保存了这张表中的索引

Memory:
- 如果表的存储引擎是Memory, 那么只要创建表就会自动创建一个文件, 这个文件就保存了这张表的结构
- 注意点: 如果表的存储引擎是Memory, 那么就不会像InnoDB/MyISAM将数据保存到文件中了, 而是直接保存到内存中

alter table 表名 engine=引擎名称;
alter table stu engine=MyISAM;

07-数据增删改查

1.插入数据
insert into 表名 (字段名称1, 字段名称2) values (值1, 值2);

示例:
create table if not exists stu(
id int,
name varchar(20)
);
insert into stu (id, name) values (1, ‘lnj’);
#注意点: 在插入数据的时候指定的字段名称的顺序不用和表中的字段名称的顺序一致
insert into stu (name, id) values (‘zs’, 2);
注意点: 在插入数据的时候指定的取值顺序必须和指定的字段名称顺序一致
insert into stu (name, id) values (3, ‘ls’);
注意点: 如果插入数据时指定的取值顺序和表中的字段顺序是一致的, 那么可以不指定字段名称
insert into stu values (3, ‘ls’);
注意点: 我们可以通过values同时插入多条数据
insert into stu values (4, ‘ww’), (5, ‘zl’);

3.更新数据
update 表名 set 字段名称=值 [where 条件];

示例:
注意点: 如果在更新数据的时候没有指定条件, 那么就会更新整张表中的数据
update stu set score=77;
注意点: 如果在更新数据的时候指定了条件, 那么只会更新满足条件的数据
update stu set score=88 where name=‘ls’;
注意点: 在指定条件的时候, 我们可以通过AND来指定多个条件, AND===&&
update stu set score=100 where name=‘lnj’ AND id=5;

注意点: 在更新数据的时候是可以同时更新多个字段的
update stu set name=‘it666’, score=33 where id=5;

1.查询表
注意点: 以下方式会将表中所有的数据都查询出来, 所以性能比较差
注意点: 以下方式会将表中所有的数据都查询出来, 不能查询特定字段的值
select * from 表名;

以下才是查询数据完整的写法
select 字段名称1, 字段名称2 from 表名 [where 条件];
查询特定字段的数据
select name from stu;
#查询满足条件的数据
select * from stu where score > 60;
select id, name from stu where score > 60;
select * from stu where score = 77 || score = 88;
select * from stu where score in (77, 88);
select * from stu where score BETWEEN 77 AND 88;
select * from stu where score IS NOT NULL;
select * from stu where score IS NULL;

where支持的运算符
=(等于)、!=(不等于)、<>(不等于)、<(小于)、<=(小于等于)、>(大于)、>=(大于等于);
IN(set);固定的范围值
BETWEEN…AND;值在什么范围
IS NULL;(为空) IS NOT NULL(不为空)
AND;与
OR;或
NOT;非
LIKE: 模糊查询

1.删除数据
delete from 表名 [where 条件];

删除满足条件的数据
delete from stu where score > 60;
$ 删除所有的数据
delete from stu;

08-数据类型

1.MySQL为什么要提供数据类型?
如果没有数据类型, 那么MySQL就不知道需要分配多大的存储空间来存储你的数据
过大会浪费会造成资源浪费, 体积变大效率变低, 过小可能导致数据溢出不能完整的保存数据
所以MySQL提供数据类型的目的是为了让我们合理的分配存储空间, 降低数据库的体积
为了让我们合理的分配存储空间, 完整的保存数据
为了更好的对数据库进行优化

2.MySQL中有哪些数据类型?

  • 整型类型/浮点类型/定点类型/字符类型/文本类型/枚举类型/集合类型/日期类型/布尔类型

3.整数类型 - 专门用来保存整数的
TINYINT 1 字节 (-128,127) (0,255) 小整数值
SMALLINT 2 字节 (-32 768,32 767) (0,65 535) 大整数值
MEDIUMINT 3 字节 (-8 388 608,8 388 607) (0,16 777 215) 大整数值
INT或INTEGER 4 字节 (-2 147 483 648,2 147 483 647) (0,4 294 967 295) 大整数值
BIGINT 8 字节 (-9,223,372,036,854,775,808,9 223 372 036 854 775 807) (0,18 446 744 073 709 551 615) 极大整数值

注意点:
- MySQL中的整型和其它编程语言的整型一样, 也区分有符号和无符号
+ 默认情况下整型就是有符号的
+ 我们可以在数据类型的后面加上 unsigned 来将数据类型变成无符号的
- 在设计数据库的时候一定要合理的使用数据类型
+ 例如: 我们要保存一个人的年龄 (整数)
+ 我们应该使用TINYINT类型, 因为人最多活到255岁已经上天了, 所以使用最小的整型即可
+ 如果使用其它的整型, 就会造成资源浪费, 数据库体积变大, 效率变低…
- 在保存数据的时候, 如果超出了当前数据类型的范围, 那么就会报错
- 在设置整型的时候, 还可以设置整型数据将来显示的位宽
+ 例如: 现在设置将来显示整型的位宽是2, 现在存储的数据1, 那么将来查询出来的数据就会显示成 1;
+ 2020-2-3 – 2020-02-03
+ 注意点: 如果存储的数据没有指定的位宽宽, 那么就会自动补空格或者0, 如果大于或者等于了指定的位宽, 那么毛都不做
create table person(
id int,
age tinyint
);
insert into person values (1, -128);
insert into person values (1, 127);
insert into person values (1, 128); #报错

create table person2(
id int,
age tinyint unsigned
);
insert into person values (1, -128); #报错
insert into person values (1, 127);
insert into person values (1, 128);

create table person3(
id int,
age tinyint(2) zerofill
);
insert into person values (1, 1); #01
insert into person values (1, 12); #12
insert into person values (1, 123); #123
2.浮点类型 - 专门用来保存小数的
FLOAT(m, d) 4 字节 单精度
DOUBLE(m, d) 8 字节 双精度
m总位数, d小数位数

  • float和double的区别

    • 占用存储空间大小不一样
    • 默认保留的小数位数不同
    • 保存数据的有效精度也不同
  • 浮点类型特点

    • 和其它编程语言中一样, 浮点类型是不准确的
    • 所以在企业开发中千万不要使用浮点数来保存用户的准确(珍贵)信息(RMB)

示例一: 默认保留的小数位数不同
create table person(
id int,
weight FLOAT,
height DOUBLE
);
insert into person values (1, 1.12345678901234567890, 1.12345678901234567890);
weight: 1.12346
height: 1.1234567890123457

示例二: 手动指定小数的总位数和小数部分的位数
create table person2(
id int,
weight FLOAT(10, 6),
height DOUBLE(10, 6)
);
insert into person2 values (1, 1.12345678901234567890, 1.12345678901234567890);
weight: 1.123457
height: 1.123457

示例三: 保存数据的有效精度也不同
create table person3(
id int,
weight FLOAT(20, 19),
height DOUBLE(20, 19)
);
insert into person3 values (1, 1.12345678901234567890, 1.12345678901234567890);
weight: 1.123456-8357467651000
height: 1.123456789012345-7000
3.定点类型 - 也是用于存储小数的
decimal(M, D)
m总位数, d小数位数
定点类型的本质: 是将数据分为两个部分来存储, 每个部分都是整数
所以定点数不要滥用, 因为非常消耗资源

create table person4(
id int,
weight decimal(21, 20),
height decimal(21, 20)
);
insert into person4 values (1, 1.12345678901234567890, 1.12345678901234567890);
weight: 1.12345678901234567890
height: 1.12345678901234567890
4.字符类型 - 专门用来存储字符的
CHAR(size) 0-255 字节 定长字符串
VARCHAR(size) 0-65535字节 变长字符串

  • char和varchar区别
    • 能够保存数据的容量不一样
    • char不会回收多余的字符, 要多少给多少
    • varchar会回收多余的字符, 用多少给多少
      • 例如: 通过 char(2)存储存储数据’a’, 存储的结果是’ a’;
      • 例如: 通过 varchar(2)存储存储数据’a’, 存储的结果是’a’;

示例一:
create table person(
id int,
name1 char(2),
name2 varchar(2)
);
insert into person values (1, ‘a’, ‘b’);
insert into person values (1, ‘12’, ‘34’);
insert into person values (1, ‘abc’, ‘def’); #只要超出申请的范围就会报错

示例二:
注意点: 由于是字符类型, 所以传递值建议用单引号’’
注意点: VARCHAR理论上可以存储65535个字符, 但是实际会随着当前数据库的字符集改变
create table person2(
id int,
name1 char(255),
name2 varchar(255)
);
65535 / 3 = 21845, 由于utf8一个字符占用3个字节, 所以varchar在utf8的表中最多只能存储21845个字符
65535 / 2 = 32767, 由于gbk一个字符占用2个字节,所以varchar在gbk的表中最多只能存储32767个字符
create table person3(
id int,
name1 char(255),
name2 varchar(65535)
)charset=gbk;
Column length too big for column ‘name2’ (max = 21845); use BLOB or TEXT instead
Column length too big for column ‘name2’ (max = 32767); use BLOB or TEXT instead
5.大文本类型
5.1.MySQL中每一行存储的数据是有大小限制的, 每一行最多只能存储65534个字节
create table person(
#name1 char(3),
name2 varchar(21844) #在UTF8中相当于65535个字节
)charset=utf8;
Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs

5.2.大文本类型
TINYTEXT 0-255字节 短文本字符串
TEXT 0-65535字节 长文本数据
MEDIUMTEXT 0-16777215字节 中等长度文本数据
LONGTEXT 0-4294967295字节 极大文本数据
create table person2(
name1 char(3),
name2 TEXT #不会报错, 因为没有超出显示, 实际只占用10个字节
)charset=utf8;
注意点:
大文本类型在表中并不会实际占用所能保存的字节数, 而是利用10个字节引用了实际保存数据的地址
6.枚举类型
和其它编程语言一样, 如果某个字段的取值只能是几个固定值中的一个, 那么就可以使用枚举
enum(值1, 值2, …);

create table person(
id int,
gender enum(‘男’, ‘女’, ‘妖’)
);
insert into person values (1, ‘火’); #会报错
insert into person values (1, ‘男’); #不会报错
insert into person values (2, ‘女’); #不会报错
insert into person values (3, ‘妖’); #不会报错

注意点:

  • MySQL中的枚举类型和其它的编程语言一样, 底层都是使用整型来实现的
    • 和其它编程语言不太一样的是, 其它编程语言的枚举都是从0开始的, 而MySQL的枚举是从1开始的
      select gender+0 from person;
  • 由于MySQL的枚举底层是使用整型实现的, 所以我们在赋值的时候除了可以赋值固定的几个值其中的一个意外
    我们还可以赋值对应的整数
    insert into person values (4, 1); #不会报错
    insert into person values (4, 4); #会报错
    7.集合类型
    和编程开发中一样, 如果某个字段的取值只能是几个固定值中的几个, 那么就可以使用集合类型
    set(值1, 值2, …)

create table person(
id int,
hobby set(‘篮球’,‘足球’,‘高尔夫球’,‘足浴’)
);
insert into person values (1, ‘篮球,足球,高尔夫球’); #不会报错
insert into person values (1, ‘橄榄球’); #会报错

insert into person values (2, ‘篮球’); #不会报错 1
insert into person values (3, ‘足球’); #不会报错 2
insert into person values (4, ‘高尔夫球’); #不会报错 4
insert into person values (5, ‘足浴’); #不会报错 8
注意点:

  • MySQL的集合类型也是使用整型来实现的
    select hobby+0 from person;
  • MySQL的集合类型是按照2(n)的方式来实现的
    2(0) = 1
    2(1) = 2
    2(2) = 4
    2(3) = 8
    8.布尔类型 - 专门用来保存真假的

create table person(
id int,
flag boolean
);
insert into person values (1, ‘男’); #会报错
insert into person values (1, true); #不会报错
insert into person values (2, false); #不会报错

注意点:

  • MySQL中的布尔类型也是使用整型来实现的, 0就表示假, 1就表示真
    • 底层的本质是因为MySQL是使用C/C++来实现的, 所以就是’非零即真’
      insert into person values (3, 1); #不会报错
      insert into person values (4, 0); #不会报错
      insert into person values (5, 2); #不会报错
      8.日期类型 - 专门用来保存时间的
      DATE 3字节 YYYY-MM-DD 日期值
      TIME 3字节 HH:MM:SS 时间值或持续时间
      DATETIME 8字节 YYYY-MM-DD HH:MM:SS 混合日期和时间值
      注意点: 在存储时间的时候, 需要用单引号将时间括起来
      create table person(
      id int,
      filed1 DATE,
      filed2 TIME,
      filed3 DATETIME
      );
      insert into person values (1, ‘2020-02-02’, ‘14:18:23’, ‘2020-02-02 14:18:23’);

09-数据库完整性

1.什么是数据的完整性?
保证保存到数据库中的数据都是正确的。

2.如何保证数据完整性?

  • 数据的完整性可以分为三类: 实体完整性、域完整性、参照完整性
  • 无论是哪一种完整性都是在创建表时给表添加约束即可

3.实体完整性
3.1什么是实体?
表中的一行数据就是一个实体(entity)
3.2如何保证实体完整性?
保证实体完整性就是保证每一行数据的唯一性

4.实体完整性的约束类型
主键约束(primary key)
唯一约束(unique)
自动增长列(auto_increment)

5.主键约束(primary key)
主键用于唯一标识表中的每一条数据, 和现实生活中的身份证很像

create table person2(
id int primary key,
name varchar(20)
);
insert into person2 values (1, ‘lnj’);
insert into person2 values (2, ‘lnj’);

主键的特征:

  • 如果将某一个字段设置成了主键, 那么这个字段的取值就不能重复了
  • 如果将某一个字段设置成了主键, 那么这个字段的取值就不能是null了
  • 一张表中只能有一个主键, 不能出现多个主键
    create table person3(
    id int primary key,
    name varchar(20) primary key
    );
  • 我们除了可以在字段数据类型后面添加primary key, 将这个字段变成主键以外,
    还可以通过在最后写上primary key(字段名称)的方式来指定主键
    create table person3(
    id int,
    name varchar(20),
    primary key(id)
    );
    insert into person3 values (1, ‘lnj’);
    insert into person3 values (1, ‘lnj’);
    1.什么是联合主键?
    我们通过将表中的某个永远不重复的字段设置为主键, 从而达到保证每一行数据的唯一性(实体完整性)
    但是在企业开发中有时候我们可能找不到不重复的字段, 此时我们还可以通过联合主键的方式来保证每一行数据的唯一性
    联合主键就是同时将多个字段作为一个主键来使用

|name| age|
|lnj | 88 |
|zs | 88 |
|lnj | 33 |

create table person(
name varchar(20),
age int,
primary key(name, age)
);
insert into person values (‘lnj’, 88);
insert into person values (‘lnj’, 88);

注意点:
联合主键并不是添加多个主键, 而是将多个字段的值作为主键来使用
也就是过去我们指定id为主键, 那么id的取值不能重复
而现在如果我们指定 name和age为主键, 那么name+age的值不能重复
1.唯一约束(unique)
唯一约束用于保证某个字段的值永远不重复
create table person(
id int unique,
name varchar(20)
);
insert into person values (1, ‘lnj’);
insert into person values (1, ‘lnj’);
主键和唯一键异同:

  • 唯一约束和主键约束一样, 被约束的字段的取值都不能够重复
  • 主键在一张表中只能有一个, 而唯一约束在一张表中可以有多个
    create table person2(
    id int unique,
    name varchar(20) unique
    );
    insert into person2 values (1, ‘lnj’);
    insert into person2 values (2, ‘lnj’);
  • 主键的取值不能为Null, 而唯一约束的取值可以是Null
    1.自动增长约束(auto_increment)
    自动增长约束的作用是让某个字段的取值从1开始递增, 从而保证实体完整性

create table person(
id int auto_increment,
name varchar(20)
);
#Incorrect table definition; there can be only one auto column and it must be defined as a key
注意点:
如果某个字段是自动增长的, 那么这个字段必须是主键才可以
create table person(
id int auto_increment primary key,
name varchar(20)
);
insert into person values (1, ‘lnj’);
如果仅仅是主键, 那么取值不能是null, 但是如果主键还是自动增长的, 那么取值就可以是null或者default

在企业开发中我们应该如何选择主键
最少性: 能用一个字段作为主键, 就不要使用多个字段
稳定性: 能用不被操作(修改)的字段作为主键, 就不要使用会被操作的字段作为主键
一般情况下我们会定义一个名称叫做id的字段, 并且这个字段是整型的, 并且这个字段是自动增长的来作为主键
1.如何修约束
1.1如何修改主键约束
alter table 表名 add primary key(字段);
create table person(
id int,
name varchar(20)
);
alter table person add primary key(id);
insert into person values (1, ‘lnj’);

1.2如何修改唯一约束
alter table 表名 add unique(字段);
create table person2(
id int,
name varchar(20)
);
alter table person2 add unique(name);
insert into person2 values (1, ‘lnj’);

1.3如何修改自动增长约束
alter table 表名 modify 字段名称 数据类型 auto_increment;
create table person3(
id int,
name varchar(20)
);
alter table person3 modify id int auto_increment;
insert into person3 values (null, ‘lnj’);
1.域完整性
1.1什么是域?
一行数据中的每个单元格都是一个域
1.2如何保证域的完整性?
保证域的完整性就是保证每个单元格数据的正确性
-使用正确的数据类型
+ 例如: 人的年龄不可能超过255岁, 而且不能是负数, 所以我们就可以使用 TINYINT UNSIGNED
+ 例如: 人的性别只能是男/女或者妖, 所以我们就可以使用枚举类型
+ 例如: 要存储比较多的文字, 为了保证不超出每一行最大的存储限制, 我们就可以使用大文本类型
-使用非空约束(not null)
-使用默认值约束(default)

示例:
create table person(
id int,
name varchar(20) not null
);
insert into person values (1, null);

create table person2(
id int,
name varchar(20) default ‘it666’
);
insert into person2 values (1, null);
insert into person2 values (1, default);
insert into person2 values (1, ‘zs’);
注意点: 哪怕设置了默认值, 传入null之后也不会使用默认值
1.参照完整性
参照完整性又称引用完整性, 主要用于保证多表之间引用关系的正确性

2.为什么要创建多张表?
示例: 定义一张表保存2个学生3门课程的成绩
|---------------------------------|
| id | name | gender | km | score |
| 1 | 张三 | 男 |语文| 100 |
| 2 | 张三 | 男 |数学| 99 |
| 3 | 张三 | 男 |英语| 98 |
| 4 | 李四 | 女 |语文| 60 |
| 5 | 李四 | 女 |数学| 59 |
| 6 | 李四 | 女 |英语| 58 |
|---------------------------------|
如果将所有的数据都放到一张表中, 会出现大量冗余数据
所以为了降低数据库的体积, 提升数据库的效率, 我们需要根据自身需求对表进行拆分

|--------------------| |-----------------------|
| id | name | gender | | id | km | score | uid |
| 1 | 张三 | 男 | | 1 |语文| 100 | 1 |
| 2 | 李四 | 女 | | 2 |数学| 99 | 1 |
|--------------------| | 3 |英语| 98 | 1 |
| 4 |语文| 60 | 2 |
| 5 |数学| 59 | 2 |
| 6 |英语| 58 | 2 |
|-----------------------|

3.什么时候会出现冗余数据?

  • 表与表之间的关系可以分为三种: 一对一、一对多、多对多
    3.1一对一(一般不需要拆分):
  • 一夫一妻制
    |-----------------------------|
    | id | name | gender | mateId |
    | 1 | 张三 | 男 | 2 |
    | 2 | 李四 | 女 | 1 |
    | 3 | 王五 | 男 | 4 |
    | 4 | 赵六 | 女 | 2 |
    |-----------------------------|

3.2一对多(一般需要拆分):

  • 一个人有多个汽车
  • 一个班有多个学生
  • 一个人有多们成绩
    |---------------------------------|
    | id | name | gender | km | score |
    | 1 | 张三 | 男 |语文| 100 |
    | 2 | 张三 | 男 |数学| 99 |
    | 3 | 张三 | 男 |英语| 98 |
    | 4 | 李四 | 女 |语文| 60 |
    | 5 | 李四 | 女 |数学| 59 |
    | 6 | 李四 | 女 |英语| 58 |
    |---------------------------------|

|--------------------| |-----------------------|
| id | name | gender | | id | km | score | uid |
| 1 | 张三 | 男 | | 1 |语文| 100 | 1 |
| 4 | 李四 | 女 | | 2 |数学| 99 | 1 |
|--------------------| | 3 |英语| 98 | 1 |
| 4 |语文| 60 | 2 |
| 5 |数学| 59 | 2 |
| 6 |英语| 58 | 2 |
|-----------------------|
3.3多对多(一般需要拆分):

  • 一个学生有多个老师, 一个老师有多个学生
    |--------------------------------------------|
    | id | stuName | gender | teacherName | 性别 |
    | 1 | 张三 | 男 | 王五 | 男 |
    | 2 | 张三 | 男 | 赵六 | 女 |
    | 3 | 张三 | 男 | 周七 | 男 |
    | 4 | 李四 | 女 | 王五 | 男 |
    | 5 | 李四 | 女 | 赵六 | 女 |
    | 6 | 李四 | 女 | 周七 | 男 |
    |--------------------------------------------|

|-----------------------| |---------------------| |-----------------------|
| id | stuName | gender | | stuId | teacherId | | id | stuName | gender |
| 1 | 张三 | 男 | | 1 | 1 | | 1 | 王五 | 男 |
| 2 | 李四 | 女 | | 1 | 2 | | 2 | 赵六 | 女 |
|-----------------------| | 1 | 3 | | 3 | 周七 | 男 |
| 2 | 1 | |-----------------------|
| 2 | 2 |
| 2 | 3 |
|---------------------|
1.如何保证参照完整性?
默认情况下表与表之间是独立存在的, 不会相互影响
也正是因为如此, 默认情况下也不会检查表与表之间的依赖关系
所以为了保证表与表之间参照完整性, 我们可以通过’外键’来保证参照完整性

create table stu(
id int auto_increment primary key,
name varchar(20),
gender enum(‘男’,‘女’,‘妖’)
);
create table grade(
id int auto_increment primary key,
km varchar(20),
score double,
uid int
);

insert into stu values (null, ‘zs’, ‘男’);
insert into stu values (null, ‘ls’, ‘女’);

insert into grade values (null, ‘语文’, 100, 1);
insert into grade values (null, ‘数学’, 99, 1);
insert into grade values (null, ‘英语’, 98, 1);
insert into grade values (null, ‘语文’, 100, 3);
insert into grade values (null, ‘数学’, 99, 3);
insert into grade values (null, ‘英语’, 98, 3);

2.什么是外键?
如果一张表中有一个字段指向了别一张表中的主键,就将该字段叫做外键
例如: 成绩表中的uid引用了学生表中的id, 那么成绩表中的uid我们就称之为外键
|--------------------| |-----------------------|
| id | name | gender | | id | km | score | uid |
| 1 | 张三 | 男 | | 1 |语文| 100 | 1 |
| 2 | 李四 | 女 | | 2 |数学| 99 | 1 |
|--------------------| | 3 |英语| 98 | 1 |
| 4 |语文| 60 | 2 |
| 5 |数学| 59 | 2 |
| 6 |英语| 58 | 2 |
|-----------------------|

create table grade2(
id int auto_increment primary key,
km varchar(20),
score double,
uid int,
foreign key(uid) references stu(id)
);
insert into grade2 values (null, ‘语文’, 100, 1);
insert into grade2 values (null, ‘数学’, 99, 1);
insert into grade2 values (null, ‘英语’, 98, 1);
insert into grade2 values (null, ‘语文’, 100, 3);
insert into grade2 values (null, ‘数学’, 99, 3);
insert into grade2 values (null, ‘英语’, 98, 3);

3.外键注意点:

  • 只有InnoDB的存储引擎才支持外键约束
  • 外键的数据类型必须和指向的主键一样
  • 在一对多的关系中, 外键一般定义在多的一方(一个学生有多门成绩, 那么外键定义在成绩表中)
  • 定义外键的表我们称之为从表, 被外键引用的表我们称之为主表

4.创建表时定义外键
foreign key(外键字段名称) references 主表名称(主表主键名称);
1.如何动态添加外键
alter table 从表名称 add foreign key(外键字段名称) references 主表名称(主表主键名称);
create table grade(
id int auto_increment primary key,
km varchar(20),
score double,
uid int
);
insert into grade values (null, ‘语文’, 100, 3);
alter table grade add foreign key(uid) references stu(id);
insert into grade values (null, ‘语文’, 100, 3);

2.如何查看外键是谁
show create table 从表名称;
show create table grade;
CREATE TABLE grade (
id int NOT NULL AUTO_INCREMENT,
km varchar(20) DEFAULT NULL,
score double DEFAULT NULL,
uid int DEFAULT NULL,
PRIMARY KEY (id),
KEY uid (uid),
CONSTRAINT grade_ibfk_1 FOREIGN KEY (uid) REFERENCES stu (id)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
注意点:

  • CONSTRAINT grade_ibfk_1 FOREIGN KEY (uid) REFERENCES stu (id)含义
    • 将uid变成外键, 外键的名称是grade_ibfk_1
    • uid的取值引用的是stu这张表中的id字段的值

3.如何动态删除外键
alter table 从表名称 drop foreign key 外键名称;
alter table grade drop foreign key grade_ibfk_1;
insert into grade values (null, ‘语文’, 100, 3);
1.外键的操作:

  • 严格操作: (前面讲解的都是严格操作)
    • 主表不存在对应数据,从表不允许添加
      • insert into grade values (null, ‘语文’, 100, 3);
    • 从表引用着数据,主表不允许删除
      • delete from stu where id=1;
    • 从表引用这数据, 主表不允许修改
      • update stu set id=3 where id=1;
  • 置空操作(null) :
    • 在企业开发中, 我们可能必须要删除主表中的数据, 但是如果主表被删除了从表就不完整了
    • 所以在企业开发中, 我们可以通过置空操作, 在删除主表数据的同时删除从表关联的数据

create table grade2(
id int auto_increment primary key,
km varchar(20),
score double,
uid int,
foreign key(uid) references stu(id) on delete set null
);
insert into grade values (null, ‘语文’, 100, 1);
delete from stu where id=1;

  • 级联操作(cascade):
    • 在企业开发中, 我们可能必须要修改主表中的数据, 但是如果主表被修改了从表就不完整了
    • 所以在企业开发中, 我们可以通过’级联操作’, 在修改主表数据的同时修改从表关联的数据
      create table grade3(
      id int auto_increment primary key,
      km varchar(20),
      score double,
      uid int,
      foreign key(uid) references stu(id) on update cascade
      );
      insert into grade values (null, ‘语文’, 100, 1);
      update stu set id=1 where id=3;

[constraint 外键名称] foreign key(外键字段) references 主表(主键)[主表删除的动作][主表更新的动作]
一般情况下主表删除时从表置空, 主表更新时从表级联
1.多对多外键
学生表 关系表 教师表
|-----------------------| |---------------------| |-----------------------|
| id | stuName | gender | | stuId | teacherId | | id | stuName | gender |
| 1 | 张三 | 男 | | 1 | 1 | | 1 | 王五 | 男 |
| 2 | 李四 | 女 | | 1 | 2 | | 2 | 赵六 | 女 |
|-----------------------| | 1 | 3 | | 3 | 周七 | 男 |
| 2 | 1 | |-----------------------|
| 2 | 2 |
| 2 | 3 |
|---------------------|

create table stu(
id int auto_increment primary key,
name varchar(20),
gender enum(‘男’,‘女’,‘妖’)
);
insert into stu values (null, ‘张三’, ‘男’);
insert into stu values (null, ‘李四’, ‘女’);

create table teacher(
id int auto_increment primary key,
name varchar(20),
gender enum(‘男’,‘女’,‘妖’)
);
insert into teacher values (null, ‘王五’, ‘男’);
insert into teacher values (null, ‘赵六’, ‘女’);
insert into teacher values (null, ‘周七’, ‘男’);

create table rel(
stuId int,
teacherId int
);
insert into rel values (1, 1);
insert into rel values (1, 2);
insert into rel values (1, 3);
insert into rel values (2, 1);
insert into rel values (2, 2);
insert into rel values (2, 3);

insert into rel values (3, 1);#报错
insert into rel values (1, 4);#报错

alter table rel add foreign key(stuId) references stu(id);
alter table rel add foreign key(teacherId) references teacher(id);

10-单表数据查询

1.单表查询
select * from 表名; #查询表中所有数据
select 字段1, 字段2 from 表名; #查询表中指定字段数据
select [* || 字段] from 表名 [where 条件]; #查询表中满足条件的数据

2.什么是结果集?
通过查询语句查询出来的结果我们就称之为结果集
结果集以表的形式将查询的结果返回给我们

注意点:
结果集返回的表和查询的表不是同一张表
被查询的表是真实存在的, 是存储在磁盘上的
而结果集不是真实存在的, 是存储到内存中的

3.如何给结果集的字段别名?

  • 查询指定字段数据时, 我们可以通过as给指定字段取别名
    SELECT name as MyName, age as MyAge FROM stu;

4.什么是字段表达式?

  • 查询数据的时候, 除了可以查询指定字段的数据以外, 我们还可以查询表达式的结果
    SELECT 6+6;

5.什么是伪表?

  • 字段表达式虽然能够查询出表达式的结果, 但是不符合MySQL的规范
  • 所以我们可以通过伪表(dual)的方式让字段表达式符合MySQL的规范
    SELECT 6+6 from dual;
    1.模糊查询
    格式:
    select 字段 from 表名 where 字段 like ‘条件’;

_通配符: 表示任意一个字符
%通配符: 表示任意0~n个字符

a_c: abc / adc
abc,adc,abbc,ac
_a_c: 1abc / 3adc
1abc,abc1,2abbc,3adc

a%c:abc / adc / abbc / ac
abc,adc,abbc,ac
%a%c: 1abc / 2abbc / 3adc
1abc,abc1,2abbc,3adc

select * from stu where name like ‘z_’;
select * from stu where name like ‘z__’;
select * from stu where name like ‘z_%’;
1.排序 order by
select 字段 from 表名 order by 字段 [asc | desc];
select * from stu order by age; #默认按照升序进行排序
select * from stu order by age asc; # 升序排序
select * from stu order by age desc; # 降序排序

select * from stu order by age desc, score asc; #如果年龄相同, 那么还可以继续按照其它字段来排序
1.聚合函数:
count(); 统计
select count() from stu;
select count(
) from stu where score >= 60;

sum(); 求和
select sum(id) from stu;

avg(); 求平均值
select avg(id) from stu; # 21 / 6 = 3.5
select avg(score) from stu;

max(); 获取最大值
select max(score) from stu;

min(); 获取最小值
select min(score) from stu;

2.数值类
rand(); #生成随机数
select rand() from dual;
select * from stu order by rand();

round()#四舍五入
select round(3.1) from dual;
select round(3.5) from dual;

ceil(); #向上取整
select ceil(3.1) from dual;

floor(); #向下取整
select floor(3.9) from dual;

truncate(); #截取小数位
select truncate(3.1234567, 2) from dual;

3.字符串类
ucase(); #转换为大写
select ucase(‘hello world’) from dual;

lcase(); #转换为小写
select lcase(‘HELLO WORLD’) from dual;

left(); #从左边开始截取到指定的位置
select left(‘1234567890’, 3) from dual;

right();#从右边开始截取到指定的位置
select right(‘1234567890’, 3) from dual;

substring(); #从指定位置开始截取指定个字符
select substring(‘1234567890’, 3, 5) from dual;

1. 数据分组 group by
select 分组字段 || 聚合函数 from 表名 group by 分组字段;

  • 需求: 要求统计表中一共有多少个城市
    select city from stu;
    select city from stu group by city;

  • 需求: 要求统计每个城市中有多少个人
    select city, count(*) from stu group by city;

注意点:

  • 在对数据进行分组的时候, select 后面必须是分组字段或者聚合函数, 否则就只会返回第一条数据
    select city from stu group by city;
    select name from stu group by city;
    select city, group_concat(name) from stu group by city;

1.条件查询 having:

  • having和where很像都是用来做条件查询的
  • 但是where是去数据库中查询符合条件的数据, 而having是去结果集中查询符合条件的数据

select * from stu where city=‘北京’;
select * from stu having city=‘北京’;

select name, age from stu where city=‘北京’;
select name, age from stu having city=‘北京’;
#Unknown column ‘city’ in ‘having clause’

需求: select city from stu group by city;
需求: select city, avg(score) from stu group by city;
需求: select city, avg(score) as average from stu group by city;
需求: select city, avg(score) as average from stu group by city having average>=60;

1.分页 limit:
select 字段 from 表 limit 索引, 个数;
select * from stu limit 0, 3;
select * from stu limit 3, 3;

1. 查询选项
select [查询选项] 字段名称 from 表名;
all: 显示所有查询出来的数据[默认]
distinct: 去除结果集中重复的数据之后再显示

select name from stu;
select all name from stu;
select distinct name from stu;
注意点:
如果是通过distinct来对结果集中重复的数据进行去重
那么只有所有列的数据都相同才会去重
select name, score from stu;
select distinct name, score from stu;

完整的查询语句
select [查询选项] 字段名称 [from 表名] [where 条件] [order by 排序] [group by 分组] [having 条件] [limit 分页];

11-多表数据查询

1.多表查询

  • 多表查询只需要在单表查询基础上增加一张表即可
    select * from 表名1, 表名2;

select * from stu, grade;
注意点:

  • 默认情况下多表查询的结果是笛卡尔集

2. union作用
在纵向上将多张表的结果结合起来返回给我们
select * from 表名1 union select * from 表名2;

select id, name from stu union select id, score from grade;
注意点:

  • 使用union进行多表查询, 返回的结果集的表头的名称是第一张表的名称
  • 使用union进行多表查询, 必须保证多张表查询的字段个数一致
    #select id, name from stu union select id, score, stuId from grade;
    #The used SELECT statements have a different number of columns
  • 使用union进行多表查询, 默认情况下会自动去重
    #select id, name from stu union select id, name from person;
  • 使用union进行多表查询, 如果不想自动去重, 那么可以在union后面加上all
    #select id, name from stu union all select id, name from person;

1.表的连接查询

  • 将多张表中’关联的字段’‘连接’在一起查询我们称之为’表的连接查询’
  • 大白话: 查询多张表中满足条件的数据
    1.1内连接 inner join
    select * from stu, grade where stu.id = grade.stuId;
    select * from 表名1 inner join 表名2 on 条件;
    select * from stu inner join grade on stu.id = grade.stuId;
    注意点:
  • 在进行多表查询的时候, 如果想查询指定的字段, 那么必须在字段名称前面加上表名才行
    #select stu.id, stu.name, grade.score from stu inner join grade on stu.id = grade.stuId;
  • 在内连接中只会返回满足条件的数据

1.2外连接
1.2.1左外连接 left join

  • 在左外连接中, 左边的表是不看条件的, 无论条件是否满足, 都会返回左边表中所有的数据
  • 在左外连接中, 只有右边的表会看条件, 对于右边的表而言, 只有满足条件才会返回对应的数据
    #select stu.id, stu.name, grade.score from stu left join grade on stu.id = grade.stuId;
    在以上的查询语句中stu表在左边, grade表在右边
    所以stu表不看条件, 只有grade表看条件

1.2.2右外连接 right join

  • 在右外连接中, 右边的表是不看条件的, 无论条件是否满足, 都会返回右边表中所有的数据
  • 在右外连接中, 只有左边的表会看条件, 对于左边的表而言, 只有满足条件才会返回对应的数据
    #select stu.id, stu.name, grade.score from stu right join grade on stu.id = grade.stuId;
    在以上的查询语句中stu表在左边, grade表在右边
    所以grade表不看条件, 只有stu表看条件

1.3交叉连接 cross join

  • 如果没有指定条件, 那么返回笛卡尔集
    #select stu.id, stu.name, grade.score from stu cross join grade;
  • 如果指定了条件, 那么就等价于内连接
    #select stu.id, stu.name, grade.score from stu cross join grade on stu.id = grade.stuId;

1.4全连接 full join(MySQL不支持全连接)

1.自然连接(natural)
自然连接是用来简化’内连接和外连接’的
如果多张表需要判断的条件字段名称一致, 那么不用编写条件, 自然连接会自动判断
1.1自然内连接
select * from 表名1 inner join 表名2 on 条件;
select * from stu inner join grade on stu.id = grade.stuId;
select * from 表名1 natural join 表名2;
select * from stu natural join grade;

1.2自然外连接
1.2.1自然左外连接
select * from stu natural left join grade;

1.2.2自然右外连接
select * from stu natural right join grade;

注意点:

  • 如果没有指定条件, 也没有同名的字段, 那么就会返回笛卡尔集
  • 在自然连接中, 返回的结果集会自动优化, 会自动去除重复的判断字段

1.using关键字
如果多张表需要判断的条件字段名称一致, 那么除了可以使用自然连接来简化以外
还可以使用using关键字来简化
1.1内连接
select * from stu inner join grade on stu.stuId = grade.stuId;
select * from stu inner join grade using(stuId);
1.2外连接
1.2.1左外连接
select * from stu left join grade on stu.stuId = grade.stuId;
select * from stu left join grade using(stuId);
1.2.2右外连接
select * from stu right join grade on stu.stuId = grade.stuId;
select * from stu right join grade using(stuId);

1.子查询

  • 将一个查询语句查询的结果作为另一个查询语句的条件来使用
  • 将一个查询语句查询的结果作为另一个查询语句的表来使用

2.将一个查询语句查询的结果作为另一个查询的条件来使用
2.1标准子查询(返回的结果只有一个)
select stuId from grade where score = 100;
select name from stu where stuId = 3;

select name from stu where stuId = (select stuId from grade where score = 100);

2.2非标准子查询(返回的结果有多个)
select stuId from grade where score >= 60;
select name from stu where stuId = 3 OR stuId = 1;
select name from stu where stuId in(3, 1);

select name from stu where stuId in(select stuId from grade where score >= 60);

3.将一个查询语句查询的结果作为另一个查询的表来使用
select name, city, score from person where score >= 60;
select name, city, score from (select name, city, score from person where score >= 60) as t;
注意点:
如果要将一个查询语句查询的结果作为另一个查询的表来使用, 那么必须给子查询起一个别名

12-事务

1.事务基本概念

  • MySQL中的事务主要用于处理容易出错的数据。
  • 事务可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
  • 事务用来管理 insert,update,delete 语句
  • MySQL中只有使用了 Innodb 数据库引擎的表才支持事务。

1.事物语法
开启事务: start transaction
提交事务: commit
回滚事务: rollback

示例一:
create table bank(
id int unsigned auto_increment primary key,
cardid varchar(4),
name varchar(20),
money int
);
insert into bank values (null, ‘1001’, ‘zs’, 1000), (null, ‘1002’, ‘ls’, 1000);
第一种: 先扣再增加
update bank set money=money-1000 where cardid=‘1002’;
update bank set money=money+1000 where cardid=‘1001’;
第二种: 先增加再扣
update bank set money=money+1000 where cardid=‘1001’;
update bank set money=money-1000 where cardid=‘1002’;

start transaction; // 开启事务
update bank set money=money-1000 where cardid=‘1002’;
rollback; // 任务失败, 回滚到开启之前状态
update bank set money=money+1000 where cardid=‘1001’;
commit; // 任务成功, 提交开启事务之后所有操作

注意点:
事务的本质是开启事务的时候拷贝一张一模一样的表
然后执行相关的操作都是在拷贝的这张表中做操作
如果失败了, 如果执行了rollback, 那么系统就会自动删除拷贝的这张表
所以失败了不会影响到原有的数据
如果成功了, 如果执行了commit, 那么系统就会自动利用拷贝表中最新的数据覆盖原有表中的数据
所以成功了会影响到原有的数据

1.事务回滚点
savepoint 回滚点名称
rollback to 回滚点名称

start transaction;
insert into bank values (null, ‘1003’, ‘333’, 33333);
savepoint abc;
insert into bank values (null, ‘1003’, ‘444’, 44444);
savepoint def;
insert into bank values (null, ‘1003’, ‘555’, 55555);
rollback to abc;

1.事务特点:

  • 原子性(关注的是状态):
    事务开启后的所有操作,要么全部成功,要么全部失败,不可能出现部分成功的情况
    事务执行过程中如果出错,哪怕我们不手动回滚, 系统也会自动帮我们回滚

  • 一致性(关注数据可见性):
    事务开始前和结束后,数据库的完整性约束没有被破坏
    例如 A向B转账,不可能A扣了钱,B却没收到

  • 持久性:
    事务完成后,事务对数据库的所有操作是永久的, 操作完成之后就不能再回滚

  • 隔离性:
    数据库允许多个并发事务同时对其数据进行读写和修改的能力,
    隔离性可以防止多个事务并发时由于交叉执行而导致数据的不一致。

1.事务隔离级别
读未提交(read uncommitted): 一个事务可以读取另一个未提交事务的数据
读提交(read committed): 一个事务要等另一个事务提交后才能读取数据
可重复读(repeatable read): 一个事务范围内多个相同的查询返回相同的结果
串行化(serializable): 前面一个事务没有执行完后面一个事务不能执行


  • 查看隔离级别:
    • 全局的: select @@global.transaction_isolation;
    • 当前会话的: select @@transaction_isolation;
  • 设置隔离级别:
    • 全局的: set global transaction isolation level 级别名称;
    • 当前会话: set session transaction isolation level 级别名称;

  • 脏读
    能读取到其它事务没有提交的数据
    示例:
    A客户端:
    set session transaction isolation level read uncommitted;
    start transaction;
    update bank set money=money-1000 where cardId=‘1002’;
    select * from bank;
    B客户端:
    set session transaction isolation level read uncommitted;
    select * from bank;

  • 解决办法(read committed)
    A客户端:
    set session transaction isolation level read committed;
    start transaction;
    update bank set money=money-1000 where cardId=‘1002’;
    select * from bank;
    B客户端:
    set session transaction isolation level read committed;
    select * from bank;


  • 不可重复读
    一个事务范围内多次查询的结果不同
    示例:
    A客户端:
    set session transaction isolation level read committed;
    start transaction;
    select * from bank;
    B客户端:
    set session transaction isolation level read committed;
    start transaction;
    update bank set money=money-1000 where cardId=‘1002’;
    commit;
    A客户端:
    select * from bank;
    commit;
  • 解决办法(repeatable read)

  • 重复读
    一个事务范围内多次查询的结果相同
    A客户端:
    set session transaction isolation level repeatable read;
    start transaction;
    select * from bank;
    B客户端:
    set session transaction isolation level repeatable read;
    start transaction;
    update bank set money=money-1000 where cardid=‘1002’;
    commit;
    A客户端:
    select * from bank;
    commit;
  • 解决办法(serializable)

  • 幻读
    读到到的结果并不是最终的结果
    A客户端:
    set session transaction isolation level serializable;
    start transaction;
    select * from bank;
    B客户端:
    set session transaction isolation level serializable;
    start transaction;
    update bank set money=money-1000 where cardid=‘1002’;
    commit;
    A客户端:
    select * from bank;
    commit;

对应关系
事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted) 是 是 是
不可重复读(read-committed) 否 是 是
可重复读(repeatable-read) 否 否 是
串行化(serializable) 否 否 否

13-视图

1.视图基本概念

  • 视图本质就是将结果集缓存起来
  • 由于结果集是一张虚拟的表, 所以视图也是一张虚拟的表
  • 由于结果集是建立在表的基础上的, 所以视图也是建立在表的基础上的

2.视图的作用:

  • 视图可以用来简化SQL语句
  • 视图可以用来隐藏表的结构
  • 视图可以用来提升数据安全性

3.创建视图语法
create view 视图名称 as select 语句;

1.视图数据操作
– SELECT name, city FROM person;
– CREATE VIEW person_view as SELECT name, city FROM person;
– SELECT * FROM person_view;
– INSERT INTO person_view values (‘it666’, ‘武汉’);
– UPDATE person_view set city=‘香港’ WHERE name=‘it666’;
DELETE FROM person_view WHERE name=‘it666’;
注意点:
由于视图保存的是结果集, 由于结果集是基于原始表的
所以操作视图中的数据, 本质上操作的是原始表中的数据

2.修改视图内容
alter view 视图名称 as select 语句;
alter view person_view as select name, score from person;

3.删除视图
drop view [if exists] 视图名;

1.视图完整语句
create [algorithm={merge||temptable||undefined}]
view 视图名称
as select 语句
[with check option];

2.视图算法
merge: 合并式(替代式)算法

  • 将视图的语句和外层的语句合并之后再执行
  • 改算法允许更新数据
    – SELECT name, city FROM person;
    – CREATE algorithm=merge VIEW person_view1 AS SELECT name, city FROM person;
    – SELECT * FROM person_view1;
    – SELECT * FROM (SELECT name, city FROM person) as t;

temptable: 临时表(具代式)算法

  • 将视图生成一个临时表, 再执行外层的语句
  • 该算法不允许更新数据
    – CREATE algorithm=temptable VIEW person_view2 AS SELECT name, city FROM person;
    – SELECT * FROM person_view2;
    – (SELECT name, city FROM person) as t;
    – SELECT * FROM t;

undefined: 未定义算法
由MySQL自己决定使用如上的哪一种算法, 默认就是undefined
一般情况下会自动选择merge算法
CREATE VIEW person_view3 AS SELECT name, city FROM person;
CREATE ALGORITHM=UNDEFINED DEFINER=root@localhost SQL SECURITY DEFINER VIEW person_view3 AS select person.name AS name,person.city AS city from person

3.视图限制(with check option)
在with check option的选项下
1.要保证数据update之后也要符合where的条件
2.要保证insert之后的数据能被视图查询出来
3.对于delete,有无 with check option都一样
4.对于没有where字句的视图,使用with check option是多余的

  • 默认情况下哪怕插入的数据和更新的数据不符合创建视图条件, 我们也是可以通过视图来插入和更新的
    – SELECT name, city, score FROM person WHERE score >= 60;
    – CREATE VIEW person_view AS SELECT name, city, score FROM person WHERE score >= 60;
    – INSERT INTO person_view values(‘it666’, ‘台湾’, 33);
    – UPDATE person_view set score=55 WHERE name=‘ww’;

  • 如果想让插入和更新的数据必须符合创建视图的条件, 那么就可以在创建视图的时候添加限制条件
    – SELECT name, city, score FROM person WHERE score >= 60;
    – CREATE VIEW person_view AS SELECT name, city, score FROM person WHERE score >= 60 with check option;
    – INSERT INTO person_view values(‘it666’, ‘台湾’, 33); #报错, 由于不符合条件
    – UPDATE person_view set score=55 WHERE name=‘ww’; #报错, 由于不符合条件

1.视图更新限制
1.1如果视图的算法是merge算法, 那么可以更新视图
1.2如果没有指with check option, 那么无论数据符不符合创建视图条件都可以更新
1.3如果指定了with check option, 那么只有符合创建视图条件才可以更新
1.4除此之外由于视图是一张虚拟表, 视图是基于原始表的, 更新视图的本质就是更新原始表
所以只有原始表中存在的原始数据才可以更新, 通过其它方式生成的数据都不可以更新

– SELECT city, avg(score) as avgScore FROM person GROUP BY city;
– CREATE VIEW person_view AS SELECT city, avg(score) as avgScore FROM person GROUP BY city;
– SELECT * FROM person_view;
– UPDATE person_view set avgScore=11 WHERE city=‘北京’;

2.更新限制
聚合函数
DISTINCT关键字
GROUP BY子句
HAVING子句
UNION运算符
FROM子句包含多张表
SELECT语句中应用了不可更新的形势图

14-预处理

1.预处理:
所谓的预处理技术,最初也是由MySQL提出的一种减轻服务器压力的一种技术!

  • 传统mysql处理流程
    1, 在客户端准备sql语句
    select * from stu where id=1;
    select * from stu where id=2;
    2, 发送sql语句到MySQL服务器
    3, MySQL服务器对sql语句进行解析(词法,语法), 然后编译, 然后执行该sql语句
    4, 服务器将执行结果返回给客户端
    弊端:

  • 哪怕多次传递的语句大部分内容都是相同的, 每次还是要重复传递

  • 哪怕语句是相同的, 每次执行之前还是要先解析、编译之后才能执行

  • 预处理的处理流程:
    1, 在客户端准备预处理sql语句
    prepare 预处理名称 from ‘sql语句’;
    prepare stmt from ‘select * from stu where id=?;’;
    2, 发送预处理sql语句到MySQL服务器

  1. MySQL服务器对预处理sql语句进行解析(词法,语法), 但不会执行
  2. 在客户端准备相关数据
    set @id=1;
  3. MySQL服务器对数据和预处理sql编译, 然后执行该sql语句
    execute stmt using @id;
  4. 服务器将执行结果返回给客户端
    优点:
  • 只对sql语句进行了一次解析
  • 重复内容大大减少(网络传输更快)

预处理演示:
prepare stmt from ‘select * from stu where id=?;’;
set @id=1;
execute stmt using @id;
set @id=2;
execute stmt using @id;

如何在MySQL中定义变量
全局变量: @变量名称
局部变量:
给变量赋值: set @变量名称=值;

15-存储过程

1.什么是存储过程?
存储过程和其它编程语言的函数很像, 可以用于封装一组特定功能的SQL语句集
用户通过’call 存储过程的名称()’ 来调用执行它。

2.存储过程基本语法
2.1定义
create procedure 存储过程名称(形参列表)
begin
// sql语句
// … …
end;

示例:
create procedure show_stu()
begin
select * from stu;
end;

示例:
create procedure show_stu_by_id(stuId int)
begin
select * from stu where id=stuId;
end;

2.2调用
call 存储过程名称(参数);

3.查看存储过程
3.1查看MySQL中所有存储过程
show procedure status;
3.2查看指定数据库中的存储过程
show procedure status where db=‘db_name’;
3.3查看指定存储过程的源代码
show create procedure show_stu;

4.删除存储过程
drop procedure show_stu;

1.如何在MySQL中定义变量

  • 全局变量:
    定义: @变量名称;
    赋值: set @全局变量名称=值;
    select 字段名称 into @全局变量名称 from 表名;
  • 局部变量:
    定义: declare 变量名称 数据类型;
    赋值: set 局部变量名称=值;
    select 字段名称 into 局部变量名称 from 表名;

2.全局变量
set @stuId = 2;
set @stuName = ‘’;
select name into @stuName from stu where id=@stuId;
select @stuName from dual;

3.局部变量
局部变量只能在存储过程和函数中定义, 所以也称之为存储过程变量
create procedure show_stu3()
begin
declare stuId int default 1;
declare stuName varchar(255);
# set stuId = 2;
select name into stuName from stu where id = stuId;
select stuName from dual;
end;

1.存储过程参数:

  • MySQL存储过程中的参数分为:
    • in 输入参数[默认]
    • out 输出参数
    • inout 输入输出参数

示例一: 输入参数
外界传递给我们的参数
create procedure show_stu_by_id(in stuId int)
begin
select * from stu where id=stuId;
end;

示例二: 输出参数
存储过程中返回给外界的参数
MySQL存储过程中不能使用return返回值, 需要通过参数来向外返回值
create procedure show_stu_by_id(in stuId int)
begin
set stuName = ‘’;
select name into stuName from stu where id=stuId;
return stuName; # 报错
end;

create procedure show_stu_by_id2(in stuId int, out stuName varchar(255))
begin
select name into stuName from stu where id=stuId;
end;

set @stuName = ‘’;
call show_stu_by_id2(1, @stuName);
select @stuName from dual;

示例三: 输入输出参数
同时具备了输入参数和输出参数所有功能
create procedure show_stu_by_id3(inout data int)
begin
select age into data from stu where id=data;
end;

set @data = 1;
call show_stu_by_id3(@data);
select @data from dual;

16-自定义函数

1.什么是自定义函数
自定义函数和存储过程很像, 只不过自定义函数不需要手动通过call调用
而是和其它的聚合函数一样会在SQL语句中自动被调用
例如: select avg(score) from stu;
例如: select count(*) from stu where age >=18;

2.创建自定义函数
create function 函数名(形参列表) returns 数据类型 函数特征
begin
sql语句;
… …
return 值;
end;

函数特征
1 DETERMINISTIC 不确定的
2 NO SQL 没有SQl语句,当然也不会修改数据
3 READS SQL DATA 只是读取数据,不会修改数据
4 MODIFIES SQL DATA 要修改数据
5 CONTAINS SQL 包含了SQL语句

3.调用函数
select 函数名称(参数) from dual;

示例:
create function fn_add(a int, b int) returns int DETERMINISTIC
begin
declare sum int default 0;
set sum = a + b;
return sum;
end;

示例:
create function check_stu(stuId int) returns varchar(255) DETERMINISTIC
begin
declare stuName varchar(255) default ‘’;
select name into stuName from stu where id=stuId;
return stuName;
end;

1.查看函数
1.1查看所有函数
show function status;
1.2查看指定数据库中的函数
show function status where db=‘db_name’;
1.3查看函数源代码
show create function show_stu;

2.删除存储过程
drop function show_stu;

1.IF语句
if 条件表达式 then
… …
elseif 条件表达式 then
… …
else
… …
end if;

示例一:
create function fn_test(age int) returns varchar(255) DETERMINISTIC
begin
declare result varchar(255) default ‘’;
if age >= 18 then
set result = ‘成年人’;
else
set result = ‘未成年人’;
end if;
return result;
end;

示例二:
create function fn_test2(score int) returns varchar(255) DETERMINISTIC
begin
declare result varchar(255) default ‘’;
if score < 0 || score > 100 then
set result = ‘没有这个分数’;
elseif score < 60 then
set result = ‘不及格’;
elseif score < 80 then
set result = ‘良好’;
else
set result = ‘优秀’;
end if;
return result;
end;

2.CASE语句
case
when 条件表达式 then
… …
when 条件表达式 then
… …
end case;

示例:
create function fn_test3(score int) returns varchar(255) DETERMINISTIC
begin
declare result varchar(255) default ‘’;
case
when score=100 then
set result = ‘还需努力’;
when score=0 then
set result = ‘不需要努力了’;
end case;
return result;
end;

3.循环语句
while 条件表达式 do
… …
end while;

示例: 1 + n的和 / 1 + 2 + 3 + 4 + 5
create function fun_test4(num int)returns int DETERMINISTIC
begin
declare sum int default 0;
declare currentIndex int default 1;

while currentIndex <= num do
set sum = sum + currentIndex;
set currentIndex = currentIndex + 1;
end while;

return sum;
end;

repeat
… …
until 条件表达式 end repeat;

示例:
create function fun_test6(num int)returns int DETERMINISTIC
begin
declare sum int default 0;
declare currentIndex int default 1;

repeat
set sum = sum + currentIndex;
set currentIndex = currentIndex + 1;
until currentIndex > num end repeat;

return sum;
end;

17-索引.html

需求: 往数据库里存储一万条数据
实现方案:
1.写一万条insert into语句
2.将insert into语句封装到存储过程或者函数中
将来怎么使用?
是配合其它SQL语句使用, 还是单独使用
单独使用–存储过程
配合其它SQL语句使用–自定义函数

create procedure add_stus(num int)
begin
declare currentId int default 0;
declare currentAge int default 0;
declare currentName varchar(255) default ‘’;

while currentId < num do
set currentId = currentId + 1;
set currentAge = floor(rand() * 30);
set currentName = concat(‘it’, currentAge);
insert into stu values(currentId,currentName,currentAge);
end while;
end;

call add_stus(10000); #48.428s
注意点:
以上封装存在的问题, 默认情况下每生成一条插入语句, 就会立即执行这条插入的语句
所以整个过程我们生成了一万条插入语句, 我们解析了一万条插入的语句, 我们编译了一万条插入的语句, 我们执行了一万条插入的语句
所以整个过程就比较耗时

create procedure add_stus2(num int)
begin
declare currentId int default 0;
declare currentAge int default 0;
declare currentName varchar(255) default ‘’;

set autocommit = 0;
while currentId < num do
set currentId = currentId + 1;
set currentAge = floor(rand() * 30);
set currentName = concat(‘it’, currentAge);
insert into stu values(currentId,currentName,currentAge);
end while;
commit;

end;

call add_stus2(10000); #1.713s
注意点:
只要在循环前面加上set autocommit = 0;, 在循环后面加上commit;
那么就不会生成一条插入语句就执行一条插入语句了
会等到所有的插入语句都生成之后, 再统一的解析, 统一的编译, 统一的执行

create procedure add_stus3(num int)
begin
set @currentId = 0;
set @currentAge = 0;
set @currentName = ‘’;
prepare stmt from ‘insert into stu values(?,?,?);’;

while @currentId < num do
set @currentId = @currentId + 1;
set @currentAge = floor(rand() * 30);
set @currentName = concat(‘it’, @currentAge);
execute stmt using @currentId, @currentName, @currentAge;
end while;

end;

call add_stus3(10000); #2.048s

create procedure add_stus4(num int)
begin
set @currentId = 0;
set @currentAge = 0;
set @currentName = ‘’;
prepare stmt from ‘insert into stu values(?,?,?);’;

set autocommit = 0;
while @currentId < num do
set @currentId = @currentId + 1;
set @currentAge = floor(rand() * 30);
set @currentName = concat(‘it’, @currentAge);
execute stmt using @currentId, @currentName, @currentAge;
end while;
commit;

end;

1.什么是索引?

  • 索引就相当于字典中的目录(拼音/偏旁部首手)
    有了目录我们就能通过目录快速的找到想要的结果.
  • 但是如果没有目录(拼音/偏旁部首手), 没有索引
    那么如果想要查找某条数据就必须从前往后一条一条的查找
  • 所以索引就是用于帮助我们提升数据的查询速度的

2.索引的优缺点和使用原则
2.1优点
+ 大大加快数据的查询速度
+ 没有任何限制, 所有MySql字段都可以用作索引
2.2缺点
+ 索引是真实存在的会占空间, 会增加数据库体积
+ 如果对作为索引的字段进行增删修操作, 系统需要花费时间去更新维护索引
2.3原则
+ 对经常用于查询的字段应该创建索引(作为where条件字段、作为group by分组的字段, 作为order by排序的字段)
+ 对于主键和外键系统会自动创建索引, 无序我们手动创建
+ 对于数据量小的表不需要刻意使用索引
3.索引分类
+ 单值索引: 将某个字段的值作为索引
+ 复合索引: 将多个字段的值作为索引
+ 唯一索引(唯一键): 索引列中的值必须是唯一的,但是允许为空值
+ 主键索引:是一种特殊的唯一索引,不允许有空值
+ … …

1.查看当前查询是否使用索引
1.1查询没有索引的表
SELECT * FROM stu WHERE id=999999; #0.695s
1.2查询有索引的表
SELECT * FROM stu2 WHERE id=999999; #0.008s
1.3如何查看当前的查询语句有没有用到索引
EXPLAIN 查询语句;
如果返回的结果集中的key有值, 那么就表示当前的查询语句中用到了索引
如果返回的结果集中的key没有值, 那么就表示当前的查询语句中没有用到索引

2.如何添加索引
2.1给表设置主键, 只要设置了主键, 那么系统就会自动创建对应的索引
2.2给表设置外键, 只要设置了外键, 那么系统就会自动创建对应的索引
2.3给表设置唯一键, 只要设置了某一个字段的取值是唯一的, 也会自动创建对应的索引

2.4创建表的时候指定给哪个字段添加索引
create table test1(
id int,
name varchar(20),
index idx_name(id) #创建索引
);
2.5创建好表之后再给指定字段添加索引
create table test2(
id int,
name varchar(20),
);
create index idx_name on test2(id); #创建索引

create table test3(
id int,
name varchar(20),
);
alter table test3 add index idx_name(id);

3.删除索引
drop index 索引名称 on 表名;

1.什么是索引算法?
索引算法决定了如何创建索引
索引算法决定了如何查找索引对应的数据

传统查找:
1, 2, 3, 4, 5

BTree查找:
4
|-------------|
2 6
|----|----| |----|----|
1 3 5 7

BTree索引:
BTree索引是基于平衡多叉排序树实现的, 能够缩短查找的次数

Hahs索引:
哈希索引是基于哈希表实现的, 只能用于memory存储引擎, 可以一次性定位到指定数据
https://dev.mysql.com/doc/refman/8.0/en/index-btree-hash.html

18.如何在Node程序中操作MySQL数据库

1.如何在Node程序中操作MySQL数据库?
我们都知道操作MySQL数据库就是连接MySQL服务器, 给MySQL服务器发送指令
在NodeJS中我们可以借助第三方库来连接MySQL服务器, 给MySQL服务器发送指令
1.1 mysql驱动库
https://www.npmjs.com/package/mysql
npm install mysql
1.2 mysql2驱动库
https://www.npmjs.com/package/mysql2
npm install mysql

2.由于node第三方库还不支持最新的mysql加密, 所以我们需要修改为旧版本加密方式
2.1、更改加密方式:
mysql> ALTER USER ‘root’@‘localhost’ IDENTIFIED BY ‘password’ PASSWORD EXPIRE NEVER;
2.2、更改密码
mysql> ALTER USER ‘root’@‘localhost’ IDENTIFIED WITH mysql_native_password BY ‘root’;

// 1.导入MySQL第三方驱动库
const mysql = require(‘mysql2’);
// 2.进行连接配置
var connection = mysql.createConnection({
host : ‘127.0.0.1’, // MySQL服务器地址
port : 3306,
user : ‘root’, // MySQL服务用户名
password : ‘root’, // MySQL服务密码
database : ‘demo’ // 需要操作的那个数据库的名称

});
// 3.连接MySQL服务器
connection.connect();

// 4.给MySQL服务器发送指令
connection.query(‘select * from stu where id=1’, (error, results, fields) => {
if (error){
console.log(error);
return
}
console.log(results);
});

// 5.释放连接
connection.end();

19.Sequelize

1.什么是Sequelize?
Sequelize是一个基于Promise的NodeJS ORM模块

2.什么是ORM?
ORM(Object-Relational-Mapping)是对象关系映射
对象关系映射可以把JS中的类和对象, 和数据库中的表和数据进行关系映射
映射之后我们就可以直接通过类和对象来操作数据表和数据了, 就不用编写SQL语句了
ORM有效的解决了直接在NodeJS中编写SQL不够直观, 不够高效, 容易出错等问题

3.如何映射?
在Sequelize中JS中的一个类(一个模型)就对应数据库中的一张表
在Sequelize中JS中的一个对象就对应表中的一条数据(一条记录)
在Sequelize中JS中的一个对象的属性就对应一条数据的一个字段

|---------------------------|
| id | name | age |
| 1 | zs | 18 |
| 2 | ls | 19 |
|---------------------------|

// 1.创建一张表
cosnt 模型名称 = Sequelize.define(‘表名’, {
id: int,
name: varchar(255),
age: int
})
// 2.创建一条记录
let zs = 模型名称.build({
id: 1,
name: zs,
age: 18
})

zs.id

// 3.操作表和数据
只要是通过Sequelize定义的模型(类), 那么Sequelize就会自动给这个模型添加很多操作表和数据的方法
以后我们就可以直接通过模型操作表, 通过模型创建出来的对象操作数据

20.Sequelize的基本使用

1.Sequelize基本使用

2.什么是数据库连接池?
默认情况下有一个人要使用数据库, 那么就必须创建一个连接
默认情况下有一个人不用数据库了, 为了不占用资源, 那么就必须销毁一个连接
但是频繁的创建和销毁连接是非常消耗服务器性能的, 所以为了提升服务器性能就有了连接池

数据库连接池是负责分配、管理和释放数据库连接,
它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个

// 1.导入Sequelize
const Sequelize = require(‘sequelize’);
// 2.配置连接信息

第一个参数: 要操作的数据库名称
第二个参数: 数据库用户名
第三个参数: 数据库密码
第四个参数: 其它的配置信息

const sequelize = new Sequelize(‘demo’, ‘root’, ‘root’, {
host: ‘127.0.0.1’, // MySQL服务器地址
port: 3306, // MySQL服务器端口号
// 注意点: Sequelize不仅仅能操作MySQL还能够操作其它类型的数据库
dialect: ‘mysql’, // 告诉Sequelize当前要操作的数据库类型
pool: {
max: 5, // 最多有多少个连接
min: 0, // 最少有多少个连接
idle: 10000, // 当前连接多久没有操作就断开
acquire: 30000, // 多久没有获取到连接就断开
}
});

// 3.测试配置是否正确
sequelize.authenticate()
.then(()=>{
console.log(‘ok’);
})
.catch((err)=>{
console.log(err);
});

21-Sequelize-创建表

// 1.导入Sequelize
const Sequelize = require(‘sequelize’);
// 2.配置连接信息

第一个参数: 要操作的数据库名称
第二个参数: 数据库用户名
第三个参数: 数据库密码
第四个参数: 其它的配置信息

const sequelize = new Sequelize(‘demo’, ‘root’, ‘root’, {
host: ‘127.0.0.1’, // MySQL服务器地址
port: 3306, // MySQL服务器端口号
// 注意点: Sequelize不仅仅能操作MySQL还能够操作其它类型的数据库
dialect: ‘mysql’, // 告诉Sequelize当前要操作的数据库类型
pool: {
max: 5, // 最多有多少个连接
min: 0, // 最少有多少个连接
idle: 10000, // 当前连接多久没有操作就断开
acquire: 30000, // 多久没有获取到连接就断开
}
});

1.字段说明常用属性
type: 字段类型
primaryKey: 是否是主键
autoIncrement: 是否自动增长
allowNull: 是否允许为空
unique: 是否必须唯一
defaultValue: 默认值

2.额外配置常用属性
timestamps: 是否自动添加createdAt/updateAt字段
freezeTableName: 是否禁止自动将表名修改为复用
tableName: 是否自定义表名
indexes: [ // 指定索引
{
name: ‘’, // 索引名称
fields: [’’], // 索引字段名称
}
]

第一个参数: 用于指定表的名称
第二个参数: 用于指定表中有哪些字段
第三个参数: 用于配置表的一些额外信息

注意点:
1.sequelize在根据模型创建表的时候, 会自动将我们指定的表的名称变成复数
2.sequelize在根据模型创建表的时候, 会自动增加两个字段 createAt/updateAt

let User = sequelize.define(‘user’, {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.STRING, // varchar(255)
allowNull: false,
unique: true
},
age: {
type: Sequelize.TINYINT,
defaultValue: 66
},
gender: {
type: Sequelize.ENUM([‘男’, ‘女’, ‘妖’]),
defaultValue: ‘妖’
}
}, {
freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
// tableName: ‘student’, // 自定义表名
timestamps: false, // 不需要自动创建createAt/updateAt这两个字段
indexes: [ // 指定索引
{
name: ‘idx_age’, // 索引名称
fields: [‘age’], // 索引字段名称
}
]
});

// 注意点: 默认定义好一个模型之后并不会自动创建对应的表
// 我们需要通过调用连接对象的sync方法来执行同步
// 只有同步之后才会自动根据模型创建对应的表
sequelize.sync();

22-Sequelize-创建数据

// 1.导入Sequelize
const Sequelize = require(‘sequelize’);

(async ()=>{
// 2.配置连接信息

第一个参数: 要操作的数据库名称
第二个参数: 数据库用户名
第三个参数: 数据库密码
第四个参数: 其它的配置信息

const sequelize = new Sequelize('demo', 'root', 'root', {
    host: '127.0.0.1', // MySQL服务器地址
    port: 3306, // MySQL服务器端口号
    // 注意点: Sequelize不仅仅能操作MySQL还能够操作其它类型的数据库
    dialect: 'mysql', // 告诉Sequelize当前要操作的数据库类型
    pool: {
        max: 5, // 最多有多少个连接
        min: 0, // 最少有多少个连接
        idle: 10000, // 当前连接多久没有操作就断开
        acquire: 30000, // 多久没有获取到连接就断开
    }
});

// 3.创建模型
let User = sequelize.define('user', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING, // varchar(255)
        allowNull: false,
        unique: true
    },
    age: {
        type: Sequelize.TINYINT,
        defaultValue: 66
    },
    gender: {
        type: Sequelize.ENUM(['男', '女', '妖']),
        defaultValue: '妖'
    }
}, {
    freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
    // tableName: 'student', // 自定义表名
    timestamps: false, // 不需要自动创建createAt/updateAt这两个字段
    indexes: [ // 指定索引
        {
            name: 'idx_age', // 索引名称
            fields: ['age'], // 索引字段名称
        }
    ]
});
// sequelize.sync();

// 4.创建一条数据(一条记录)
// let u = new User();
// u.name = 'zs';
// u.age = 33;
// u.gender = '男';

注意点:
创建好一条数据之后, 默认情况下不会立即同步到表中
如果想立即同步到表中, 那么必须调用对象的save方法
注意点:
本质上让MySQL执行SQL语句是一个异步的操作,
所以在sequelize中大部分的方法都是异步方法
注意点:
通过模型类创建出来的对象中有一个dataValues的属性, 这个属性就保存了一条记录所有的信息
调用save方法保存完数据之后, sequelize会自动更新对应的对象, 将最新的数据更新进去

// console.log('保存之前:', u);
// await u.save();
// console.log('保存之后:', u);

// 下面这段代码 = 创建对象 + save();
let u = await User.create({
    name: 'ww',
    age: 18,
    gender: '女'
});
console.log(u.dataValues);

})();

23-Sequelize-操作数据

// 1.导入Sequelize
const Sequelize = require(‘sequelize’);

(async ()=>{
// 2.配置连接信息
/*
第一个参数: 要操作的数据库名称
第二个参数: 数据库用户名
第三个参数: 数据库密码
第四个参数: 其它的配置信息
* */
const sequelize = new Sequelize(‘demo’, ‘root’, ‘root’, {
host: ‘127.0.0.1’, // MySQL服务器地址
port: 3306, // MySQL服务器端口号
// 注意点: Sequelize不仅仅能操作MySQL还能够操作其它类型的数据库
dialect: ‘mysql’, // 告诉Sequelize当前要操作的数据库类型
pool: {
max: 5, // 最多有多少个连接
min: 0, // 最少有多少个连接
idle: 10000, // 当前连接多久没有操作就断开
acquire: 30000, // 多久没有获取到连接就断开
}
});

// 3.创建模型
let User = sequelize.define('user', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING, // varchar(255)
        allowNull: false,
        unique: true
    },
    age: {
        type: Sequelize.TINYINT,
        defaultValue: 66
    },
    gender: {
        type: Sequelize.ENUM(['男', '女', '妖']),
        defaultValue: '妖'
    }
}, {
    freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
    // tableName: 'student', // 自定义表名
    timestamps: false, // 不需要自动创建createAt/updateAt这两个字段
    indexes: [ // 指定索引
        {
            name: 'idx_age', // 索引名称
            fields: ['age'], // 索引字段名称
        }
    ]
});
// sequelize.sync();

// 4.创建一条数据(一条记录)

// let u = new User();
// u.name = 'zs';
// u.age = 33;
// u.gender = '男';
// await u.save();

// 下面这段代码 = 创建对象 + save();
let u = await User.create({
    name: 'ww',
    age: 18,
    gender: '女'
});
console.log(u.dataValues);


// 5.查询数据
// let u = await User.findByPk(2);
// console.log(u);

// 6.修改数据
// u.name = 'it666';
// await u.save();

// User.update({
//     name: 'zs'
// },{
//     where: {
//         id: 2
//     }
// })

// 7.删除数据
// await u.destroy();
User.destroy({
    where: {
        id: 3
    }
});

})();

24-Sequelize-条件查询

// 1.导入Sequelize
const Sequelize = require(‘sequelize’);

(async ()=>{
// 2.配置连接信息

第一个参数: 要操作的数据库名称
第二个参数: 数据库用户名
第三个参数: 数据库密码
第四个参数: 其它的配置信息

const sequelize = new Sequelize('demo', 'root', 'root', {
    host: '127.0.0.1', // MySQL服务器地址
    port: 3306, // MySQL服务器端口号
    // 注意点: Sequelize不仅仅能操作MySQL还能够操作其它类型的数据库
    dialect: 'mysql', // 告诉Sequelize当前要操作的数据库类型
    pool: {
        max: 5, // 最多有多少个连接
        min: 0, // 最少有多少个连接
        idle: 10000, // 当前连接多久没有操作就断开
        acquire: 30000, // 多久没有获取到连接就断开
    }
});

// 3.创建模型
let User = sequelize.define('user', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING, // varchar(255)
        allowNull: false,
        unique: true
    },
    age: {
        type: Sequelize.TINYINT,
        defaultValue: 66
    },
    gender: {
        type: Sequelize.ENUM(['男', '女', '妖']),
        defaultValue: '妖'
    }
}, {
    freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
    // tableName: 'student', // 自定义表名
    timestamps: false, // 不需要自动创建createAt/updateAt这两个字段
    indexes: [ // 指定索引
        {
            name: 'idx_age', // 索引名称
            fields: ['age'], // 索引字段名称
        }
    ]
});
// sequelize.sync();

// 4.查询
// 4.1查询多条数据
// let us = await User.findAll();
// console.log(us);
// console.log(us.map(u => u.dataValues));

// 4.2查询指定列数据
// let us = await User.findAll({
//     attributes: ['name', 'age']
// });
// console.log(us.map(u => u.dataValues));

// 4.3条件查询
let us = await User.findAll({
    where: {
        // id: 1
        // age: {
        //     [Sequelize.Op.gte] : 33
        // },
        // id: 7
        [Sequelize.Op.or]: {
            age: 33,
            id: 7
        }
    }
});
console.log(us.map(u => u.dataValues));

})();

25-Sequelize-分页和排序

// 1.导入Sequelize
const Sequelize = require(‘sequelize’);

(async ()=>{
// 2.配置连接信息
/*
第一个参数: 要操作的数据库名称
第二个参数: 数据库用户名
第三个参数: 数据库密码
第四个参数: 其它的配置信息
* */
const sequelize = new Sequelize(‘demo’, ‘root’, ‘root’, {
host: ‘127.0.0.1’, // MySQL服务器地址
port: 3306, // MySQL服务器端口号
// 注意点: Sequelize不仅仅能操作MySQL还能够操作其它类型的数据库
dialect: ‘mysql’, // 告诉Sequelize当前要操作的数据库类型
pool: {
max: 5, // 最多有多少个连接
min: 0, // 最少有多少个连接
idle: 10000, // 当前连接多久没有操作就断开
acquire: 30000, // 多久没有获取到连接就断开
}
});

// 3.创建模型
let User = sequelize.define('user', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING, // varchar(255)
        allowNull: false,
        unique: true
    },
    age: {
        type: Sequelize.TINYINT,
        defaultValue: 66
    },
    gender: {
        type: Sequelize.ENUM(['男', '女', '妖']),
        defaultValue: '妖'
    }
}, {
    freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
    // tableName: 'student', // 自定义表名
    timestamps: false, // 不需要自动创建createAt/updateAt这两个字段
    indexes: [ // 指定索引
        {
            name: 'idx_age', // 索引名称
            fields: ['age'], // 索引字段名称
        }
    ]
});
// sequelize.sync();

// 4.分页
// let us = await User.findAll({
//     offset: 2 // 跳过多少条数据
// });
// console.log(us.map(u => u.dataValues));

// let us = await User.findAll({
//     limit: 2 // 取多少条数据
// });
// console.log(us.map(u => u.dataValues));

// let us = await User.findAll({
//     offset: 2, // 跳过多少条数据
//     limit: 2 // 取多少条数据
// });
// console.log(us.map(u => u.dataValues));

// 5.排序
let us = await User.findAll({
    order: [
        // ['id', 'desc']
        ['age', 'desc'],
        ['id', 'desc']
    ]
});
console.log(us.map(u => u.dataValues));

})();

26-Sequelize-关联查询上

// 1.导入Sequelize
const Sequelize = require(‘sequelize’);

(async ()=>{
// 2.配置连接信息
/*
第一个参数: 要操作的数据库名称
第二个参数: 数据库用户名
第三个参数: 数据库密码
第四个参数: 其它的配置信息
* */
const sequelize = new Sequelize(‘demo’, ‘root’, ‘root’, {
host: ‘127.0.0.1’, // MySQL服务器地址
port: 3306, // MySQL服务器端口号
// 注意点: Sequelize不仅仅能操作MySQL还能够操作其它类型的数据库
dialect: ‘mysql’, // 告诉Sequelize当前要操作的数据库类型
pool: {
max: 5, // 最多有多少个连接
min: 0, // 最少有多少个连接
idle: 10000, // 当前连接多久没有操作就断开
acquire: 30000, // 多久没有获取到连接就断开
}
});

// 3.创建模型
let User = sequelize.define('user', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING, // varchar(255)
        allowNull: false,
        unique: true
    },
    age: {
        type: Sequelize.TINYINT,
        defaultValue: 66
    },
    gender: {
        type: Sequelize.ENUM(['男', '女', '妖']),
        defaultValue: '妖'
    }
}, {
    freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
    // tableName: 'student', // 自定义表名
    timestamps: false // 不需要自动创建createAt/updateAt这两个字段
});

let Book = sequelize.define('book', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING, // varchar(255)
        allowNull: false,
        unique: true
    },
    price: {
        type: Sequelize.DOUBLE,
        defaultValue: 66
    },
    uId: {
        type: Sequelize.INTEGER,
        allowNull: false
    }
}, {
    freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
    // tableName: 'student', // 自定义表名
    timestamps: false // 不需要自动创建createAt/updateAt这两个字段
});

// sequelize.sync();
// 4.建立查询关系
/*
注意点: 只要建立了人和书的关系, 那么在查询人的时候, 就可以把拥有的那本书也查询出来
        只要建立了书和人的关系, 那么在查询书的时候, 就可以把书属于哪个人也查询出来
        如果没有建立相关的关系, 那么就不能查询出相关的内容
* */
User.hasOne(Book, { // hasOne 谁拥有一个谁/ 一个人拥有一本书
    foreignKey: 'uId',
    sourceKey: 'id'
});
Book.belongsTo(User, { // belongsTo 谁属于一个谁 / 一本书属于一个人
    foreignKey: 'uId',
    sourceKey: 'id'
});

// 5.关联查询
/*
let u = await User.findOne({
    where: {
        id: 1
    },
    // 注意点: 只要建立了表与表之间的关系, 那么在查询人的时候,就可以把这个人拥有的那本书也查询出来了
    include: {
        model: Book
    }
});
console.log(u.dataValues.book.dataValues);
 */
let b = await Book.findOne({
    where: {
        id: 1
    },
    // 注意点: 只要建立了表与表之间的关系, 那么在查询书的时候,就可以把这本书属于哪一个人也查询出来了
    include: {
        model: User
    }
});
console.log(b.dataValues.user.dataValues);

})();

27-Sequelize-关联查询中

// 1.导入Sequelize
const Sequelize = require(‘sequelize’);

(async ()=>{
// 2.配置连接信息
/*
第一个参数: 要操作的数据库名称
第二个参数: 数据库用户名
第三个参数: 数据库密码
第四个参数: 其它的配置信息
* */
const sequelize = new Sequelize(‘demo’, ‘root’, ‘root’, {
host: ‘127.0.0.1’, // MySQL服务器地址
port: 3306, // MySQL服务器端口号
// 注意点: Sequelize不仅仅能操作MySQL还能够操作其它类型的数据库
dialect: ‘mysql’, // 告诉Sequelize当前要操作的数据库类型
pool: {
max: 5, // 最多有多少个连接
min: 0, // 最少有多少个连接
idle: 10000, // 当前连接多久没有操作就断开
acquire: 30000, // 多久没有获取到连接就断开
}
});

// 3.创建模型
let User = sequelize.define('user', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING, // varchar(255)
        allowNull: false,
        unique: true
    },
    age: {
        type: Sequelize.TINYINT,
        defaultValue: 66
    },
    gender: {
        type: Sequelize.ENUM(['男', '女', '妖']),
        defaultValue: '妖'
    }
}, {
    freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
    // tableName: 'student', // 自定义表名
    timestamps: false // 不需要自动创建createAt/updateAt这两个字段
});

let Book = sequelize.define('book', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING, // varchar(255)
        allowNull: false,
    },
    price: {
        type: Sequelize.DOUBLE,
        defaultValue: 66
    },
    uId: {
        type: Sequelize.INTEGER,
        allowNull: false
    }
}, {
    freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
    // tableName: 'student', // 自定义表名
    timestamps: false // 不需要自动创建createAt/updateAt这两个字段
});

// sequelize.sync();
// 4.建立查询关系
User.hasMany(Book, { // 一个人拥有多本书
    foreignKey: 'uId',
    sourceKey: 'id'
});
Book.belongsTo(User, { // 一本书属于一个人
    foreignKey: 'uId',
    sourceKey: 'id'
})
// 5.关联查询
/*
let u = await User.findOne({
    where: {
        id: 1
    },
    include: {
        model: Book
    }
});
console.log(u.dataValues.books.map(b=>b.dataValues));
 */
let b = await Book.findOne({
    where: {
        id: 3
    },
    include: {
        model: User
    }
});
console.log(b.dataValues.user.dataValues);

})();

28-Sequelize-建立表关系

// 1.导入Sequelize
const Sequelize = require(‘sequelize’);

(async ()=>{
// 2.配置连接信息
/*
第一个参数: 要操作的数据库名称
第二个参数: 数据库用户名
第三个参数: 数据库密码
第四个参数: 其它的配置信息
* */
const sequelize = new Sequelize(‘demo’, ‘root’, ‘root’, {
host: ‘127.0.0.1’, // MySQL服务器地址
port: 3306, // MySQL服务器端口号
// 注意点: Sequelize不仅仅能操作MySQL还能够操作其它类型的数据库
dialect: ‘mysql’, // 告诉Sequelize当前要操作的数据库类型
pool: {
max: 5, // 最多有多少个连接
min: 0, // 最少有多少个连接
idle: 10000, // 当前连接多久没有操作就断开
acquire: 30000, // 多久没有获取到连接就断开
}
});

// 3.创建模型
let User = sequelize.define('user', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING, // varchar(255)
        allowNull: false,
        unique: true
    },
    age: {
        type: Sequelize.TINYINT,
        defaultValue: 66
    },
    gender: {
        type: Sequelize.ENUM(['男', '女', '妖']),
        defaultValue: '妖'
    }
}, {
    freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
    // tableName: 'student', // 自定义表名
    timestamps: false // 不需要自动创建createAt/updateAt这两个字段
});

let Book = sequelize.define('book', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING, // varchar(255)
        allowNull: false,
    },
    price: {
        type: Sequelize.DOUBLE,
        defaultValue: 66
    },
    userId: {
        type: Sequelize.INTEGER,
        allowNull: false,
        references: {
            model: User,
            key: 'id'
        }
    }
}, {
    freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
    // tableName: 'student', // 自定义表名
    timestamps: false // 不需要自动创建createAt/updateAt这两个字段
});

// 4.建立查询关系
/*
User.hasMany(Book, { // 一个人拥有多本书
    foreignKey: 'uId',
    sourceKey: 'id'
});
Book.belongsTo(User, { // 一本书属于一个人
    foreignKey: 'uId',
    sourceKey: 'id'
});
 */
User.hasMany(Book);
Book.belongsTo(User);
// 注意点: 想要sequelize自动创建外键, 那么就必须在写完上述的两句代码之后再执行同步方法, 才会自动创建对应的外键
sequelize.sync();

// 5.关联查询
/*
let u = await User.findOne({
    where: {
        id: 1
    },
    include: {
        model: Book
    }
});
console.log(u.dataValues.books.map(b=>b.dataValues));
 */
/*
let b = await Book.findOne({
    where: {
        id: 3
    },
    include: {
        model: User
    }
});
console.log(b.dataValues.user.dataValues);
 */

})();

29-Sequelize-关联查询下

// 1.导入Sequelize
const Sequelize = require(‘sequelize’);

(async ()=>{
// 2.配置连接信息
/*
第一个参数: 要操作的数据库名称
第二个参数: 数据库用户名
第三个参数: 数据库密码
第四个参数: 其它的配置信息
* */
const sequelize = new Sequelize(‘demo’, ‘root’, ‘root’, {
host: ‘127.0.0.1’, // MySQL服务器地址
port: 3306, // MySQL服务器端口号
// 注意点: Sequelize不仅仅能操作MySQL还能够操作其它类型的数据库
dialect: ‘mysql’, // 告诉Sequelize当前要操作的数据库类型
pool: {
max: 5, // 最多有多少个连接
min: 0, // 最少有多少个连接
idle: 10000, // 当前连接多久没有操作就断开
acquire: 30000, // 多久没有获取到连接就断开
}
});

// 3.创建模型
let Student = sequelize.define('student', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING, // varchar(255)
        allowNull: false,
        unique: true
    }
}, {
    freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
    // tableName: 'student', // 自定义表名
    timestamps: false // 不需要自动创建createAt/updateAt这两个字段
});

let Teacher = sequelize.define('teacher', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING, // varchar(255)
        allowNull: false,
        unique: true
    }
}, {
    freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
    // tableName: 'student', // 自定义表名
    timestamps: false // 不需要自动创建createAt/updateAt这两个字段
});

let Relation = sequelize.define('relation', {
    studentId: {
        type: Sequelize.INTEGER,
        allowNull: false,
        references: {
            model: Student,
            key: 'id'
        }
    },
    teacherId: {
        type: Sequelize.INTEGER,
        allowNull: false,
        references: {
            model: Student,
            key: 'id'
        }
    }
}, {
    freezeTableName: true, // 告诉sequelize不需要自动将表名变成复数
    // tableName: 'student', // 自定义表名
    timestamps: false // 不需要自动创建createAt/updateAt这两个字段
});
// 4.建立查询关系
Student.belongsToMany(Teacher, { // 一个学生属于多个老师
    through: Relation
});
Teacher.belongsToMany(Student, { // 一个老师属于多个学生
    through: Relation
});

sequelize.sync();
// 5.关联查询
/*
let s = await Student.findOne({
    where: {
        id: 1
    },
    include: {
        model: Teacher
    }
});
console.log(s.dataValues.teachers.map(t=>t.dataValues));
 */
let t = await Teacher.findOne({
    where: {
        id: 1
    },
    include: {
        model: Student
    }
});
console.log(t);

})();

30-Sequelize-cli-创建数据库

1.什么是Sequelize-CLI?

  • 在编程开发中为了能够更好的管理代码, 我们可以使用Git来管理我们的代码,
    实现对代码变更的追踪, 实现在各个不同版本之间切换
  • 在数据库开发中为了能够更好的管理数据库, 我们也可以使用数据库迁移工具来管理我们的数据库,
    实现对数据库变更的追踪, 实现在各个不同版本之间切换
  • Sequelize-CLI就是一款数据库迁移工具, 能够让我们追踪数据库的变更, 在各个不同版本之间随意切换

2.如何使用Sequelize-CLI?
npm i sequelize sequelize-cli mysql2 -s
npx sequelize --help

3.初始化Sequelize-CLI
npx sequelize init

  • config: 数据库配置文件, 用于告诉CLI如何连接数据库
  • models: 数据库模型文件, 用于告诉CLI如何创建表
  • migrations: 数据库迁移文件, 用于记录数据库不同版本变更
  • seeders: 数据库种子文件, 用于编写测试数据

npx sequelize db:create

// 修改环境变量
set NODE_ENV=test

31-Sequelize-cli-创建表

1.创建模型
npx sequelize model:generate --name xxx --attributes key:type

2.根据模型创建表
npx sequelize db:migrate

3.回退到某个时刻
npx sequelize db:migrate:undo // 回退到上一个版本
npx sequelize db:migrate:undo:all // 回退所有
npx sequelize db:migrate:undo --name=20200329045955-create-book.js // 回退指定操作

32-Sequelize-cli-修改表

1.如何修改表?
使用Sequelize-CLI管理数据库的目的就是为了监控数据库的变化
所以我们不能直接修改表的结构, 如果要修改, 必须通过migration文件修改
这样我们就能记录修改操作, 就能追踪修改过程, 就能回退到指定版本

2.修改表步骤
2.1 通过 migration:generate 创建迁移文件
2.2 在迁移文件中编写修改的内容
2.3 通过 db:migrate 执行编写好的迁移文件

33-Sequelize-cli-种子文件

0.什么是种子文件?
Sequelize-CLI中的种子文件是专门用于编写测试数据的
我们知道在Sequelize-CLI中不同的阶段会创建不同的数据库
但是刚创建出来的数据库是没有数据的, 所以为了方便我们在不同的阶段演示和调试
我们可以在种子文件中提前编写测试数据, 以便于我们在不同的阶段中使用

1.创建种子文件
npx sequelize seed:generate --name xxx

2.执行种子文件
npx sequelize db:seed --seed=[fileName, …]; // 执行指定文件
npx sequelize db:seed:all // 执行所有种子文件

3.记录种子文件操作记录
“seederStorage”: “sequelize”

4.回退到某个时刻
npx sequelize db:seed:undo --seed=[fileName, …]; // 回退指定种子操作
npx sequelize db:seed:undo:all // 回退所有

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值