1、主要内容
这一章的主要内容如下
1. DQL:查询语句
1. 排序查询
2. 聚合函数
3. 分组查询
4. 分页查询
2. 约束
3. 多表之间的关系
4. 范式
5. 数据库的备份和还原
* 语法:order by 子句
* order by 排序字段1 排序方式1 , 排序字段2 排序方式2...
* 排序方式:
* ASC:升序,默认的。
* DESC:降序。
* 注意:
* 如果有多个排序字段与排序条件,则当前边字段的条件值一样时,才会判断第二个字段的条件。
聚合函数:将一列数据作为一个整体,进行纵向的计算。(前面我们介绍的是横向的计算)
1. count:计算个数
1. 一般选择非空的列:主键
2. count(*)
2. max:计算最大值
3. min:计算最小值
4. sum:计算和
5. avg:计算平均值
* 注意:聚合函数的计算,排除null值。
解决方案:
1. 选择不包含非空的列进行计算
2. IFNULL函数
分组查询
1. 语法:group by 分组字段;
2. 注意:
1. 分组之后查询的字段:分组字段、聚合函数
2. where 和 having 的区别?
1. where 在分组之前进行限定,如果不满足条件,则不参与分组。having在分组之后进行限定,如果不满足结果,则不会被查询出来
2. where 后不可以跟聚合函数,having可以进行聚合函数的判断。
因为聚合函数的值是查询之后才有,而where是在查询之前判断。而having是在查询分组之后对分组限定,此时聚合函数以及查询出来,可以进行判断。
分页查询
1. 语法:limit 开始的索引,每页查询的条数;
2. 公式:开始的索引 = (当前的页码 - 1) * 每页显示的条数
-- 每页显示3条记录
SELECT * FROM student LIMIT 0,3; -- 第1页
SELECT * FROM student LIMIT 3,3; -- 第2页
SELECT * FROM student LIMIT 6,3; -- 第3页
3. limit 是一个MySQL"方言"
查询的相应代码如下
/*首先,我们创建一张新表stu,并插入数据*/
CREATE TABLE people (
id INT,-- 编号
NAME VARCHAR(20), -- 姓名
age INT, -- 年龄
sex VARCHAR(5),-- 性别
address VARCHAR(100),-- 地址
math INT, -- 数学
english INT -- 英语
);
INSERT INTO people(id,NAME,age,sex,address,math,english) VALUES (1,'马云',55,'男','
杭州',66,78),(2,'马化腾',45,'女','深圳',98,87),(3,'马景涛',55,'男','香港',56,77),(4,'柳岩',20,'女','湖南',76,65),
(5,'柳青',20,'男','湖南',86,NULL),(6,'刘德华',57,'男','香港',99,99),
(7,'马德',22,'女','香港',99,99),(8,'德玛西亚',18,'男','南京',56,65);
SELECT * FROM people; -- 查询整体新表
-- ----------------------------------------排序查询
-- 按照数学成绩升序排序(升序可以不写,默认排序方式是升序)
SELECT * FROM people ORDER BY math ASC;
-- 按照数学成绩降序排序
SELECT * FROM people ORDER BY math DESC;
-- 按照数学成绩升序排序,如果数学成绩一样,则按照英语成绩升序排名
SELECT * FROM people ORDER BY math ASC , english ASC;
-- ----------------------------------------聚合函数
-- 计算表里面一共有多少个学生(通过姓名)
SELECT COUNT(NAME) FROM people;
-- 计算表里面一共有多少个英语成绩
SELECT COUNT(english) FROM people;
# 我们发现空的英语成绩没有被计算进来,既聚合函数的计算,排除null值。
# 这样不太合理,而我们统计个数如果缺少某一个,就显得不合理
-- IFNULL函数解决——将null值替换为0,这样计算个数的时候便会将其计算进来
SELECT COUNT(IFNULL(english,0)) FROM people;
-- 选择非空的列进行个数的计算:1)一般选择非空的列:主键 ;2)count(*)既只要某一行有一个数据不为null,就可以纵向加1
SELECT COUNT(*) FROM people;# count(*) 不推荐
SELECT COUNT(id) FROM people;# 主键(非null)推荐
-- 计算数学成绩最大值
SELECT MAX(math) FROM people;
-- 计算数学成绩平均值
SELECT AVG(math) FROM people;
-- 计算数学成绩之和
SELECT SUM(math) FROM people;
-- ----------------------------------------分组查询
-- 按照性别分组,然后分别查询男女同学的数学平均分
SELECT sex,AVG(math) AS '数学平均分' FROM people GROUP BY sex;
# 我们进行分组后查询的只能是:分组字段、聚合函数
# 假如我们按照sex进行分组,我们就是按“男”“女”将整体分为2组,分别将“男”“女”看为整体,再去查询个体信息(如name)就没有意义
# 如果我们分组后硬要查询个体信息,也只会显示“男”“女”2组2行(查询聚合函数也只会显示“男”“女”2组2行),这是没有意义的
-- 按照性别分组,然后分别查询男女同学的数学平均分,男女的人数
SELECT sex,AVG(math),COUNT(id) FROM people GROUP BY sex;
-- 按照性别分组,然后分别查询男女同学的数学平均分,男女的人数。要求:分数低于70分不参与分组
-- 既限定参加分组的条件
SELECT sex,AVG(math),COUNT(id) FROM people WHERE math>70 GROUP BY sex;# 在分组之前限定参与分组的条件
-- 想看分组之后人数大于2个人的组(既分组之后,对所分的组再进行条件限定)
SELECT sex,AVG(math),COUNT(id) FROM people WHERE math>70 GROUP BY sex HAVING COUNT(id)>2;# 在分组之后,再对所分组进行条件限定
# 当然,这里也可以给聚合函数起别名,判断起来方便
SELECT sex,AVG(math),COUNT(id) AS peoNums FROM people WHERE math>70 GROUP BY sex HAVING peoNums>2;
-- ----------------------------------------分页查询
-- 每页显示3条记录
SELECT * FROM people LIMIT 0,3; # 第1页
SELECT * FROM people LIMIT 3,3; # 第2页
-- 公式:开始的索引=(当前的页码-1)*每页显示的条数(为了显示多页,开始的索引是要动态变化的)
SELECT * FROM people LIMIT 6,3; # 第3页
# 如果最后一页内容不够,有多少条数据就显示多少条
3、约束
概念: 对表中的数据进行限定,保证数据的正确性、有效性和完整性。
SQL约束的分类如下(4类)
分类:
1. 主键约束:primary key
2. 非空约束:not null
3. 唯一约束:unique
4. 外键约束:foreign key
3.1 非空约束
非空约束:not null,值不能为null(限制某一列的数据值不能为null)
1. 创建表时添加约束
CREATE TABLE stu(
id INT,
NAME VARCHAR(20) NOT NULL -- name为非空
);
2. 创建表完后,添加非空约束
ALTER TABLE stu MODIFY NAME VARCHAR(20) NOT NULL;
3. 删除name的非空约束
ALTER TABLE stu MODIFY NAME VARCHAR(20); -- 直接修改列的数据类型,不加not null即可
非空约束(not null)的代码示例如下
-- 同样先创建一个新的表
-- ----------------------------------在创建表的时候添加约束
CREATE TABLE stu(
id INT,
NAME VARCHAR(20) NOT NULL -- 给name添加非空约束
);
-- 查询整个表
SELECT * FROM stu;
# 如果我们添加了姓名为null的数据,保存的时候会报错“Field 'name' doesn't have a default value”,重新查询null数据并没有加进去
-- ----------------------------------创建表后再添加非空约束
-- 我们先删除表的非空约束——直接修改name这一列,将这一列替换为新的name列,新的name列不添加非空约束
ALTER TABLE stu CHANGE NAME NAME VARCHAR(20); -- 或者直接modify修改数据类型,不添加非空约束即可
ALTER TABLE stu MODIFY NAME VARCHAR(20);
# 删除非空约束后又可以向里面添加name为null的数据
-- 我们直接使用修改表的列的语句,修改列的数据类型或者一整列添加非空约束即可
ALTER TABLE stu MODIFY NAME VARCHAR(15) NOT NULL; #修改列的数据类型添加not null(modify)
ALTER TABLE stu CHANGE NAME NAME VARCHAR(16) NOT NULL; #修改整列添加not null(change)
#同样,name添加非空约束后添加null的name值会报错
# 注意,SQLyog中如果想要删除或者添加某一行,必须从“只读模式“改为另一个模式,才能执行删除
3.2 唯一约束
唯一约束:unique,值不能重复
1. 创建表时,添加唯一约束
CREATE TABLE stu(
id INT,
phone_number VARCHAR(20) UNIQUE -- 添加了唯一约束
);
* 注意mysql中,唯一约束限定的列的值可以有多个null
2. 删除唯一约束(注意唯一约束不能使用modify删除)
ALTER TABLE stu DROP INDEX phone_number;
3. 在创建表后,添加唯一约束(与添加非空约束是一样的)
ALTER TABLE stu MODIFY phone_number VARCHAR(20) UNIQUE;
添加唯一约束代码
-- 同样先创建一个新的表
-- ----------------------------------在创建表的时候添加唯一约束
CREATE TABLE stu(
id INT,
phone_number VARCHAR(20) UNIQUE -- 添加唯一约束,设置手机号字段不能重复
);
# 我们给表添加2行phone_number相同的数据,保存发现报错“Duplicate entry '18812345678' for key 'phone_number'”
# 我们添加2行phone_number为null的数据,保存发现这样不会报错,是可以的。注意mysql中,唯一约束限定的列的值可以有多个null。
-- ----------------------------------在创建表之后添加唯一约束
ALTER TABLE stu MODIFY phone_number VARCHAR(20) UNIQUE; #添加唯一约束可以以这种形式添加,但是删除就不能这样删除
-- ----------------------------------删除唯一约束
#alter table stu modify phone_number varchar(20); # 添加相同的phone_number保存后仍然报错,说明这种方式不能删除唯一约束
ALTER TABLE stu DROP INDEX phone_number; #删除phone_number列的索引——唯一约束有时候也叫做唯一索引
# 这样就可以添加phone_number相同的数据
-- ----------------------------------
-- 查询整个表
SELECT * FROM stu;
-- 删除整张表
DROP TABLE stu;
-- ----------------------------------
3.3 主键约束
主键约束:primary key。
1. 注意:
1. 含义:非空且唯一
2. 一张表只能有一个字段为主键
3. 主键就是表中记录的唯一标识
2. 在创建表时,添加主键约束
create table stu(
id int primary key,-- 给id添加主键约束
name varchar(20)
);
3. 删除主键
-- 错误 alter table stu modify id int ;
ALTER TABLE stu DROP PRIMARY KEY;
4. 创建完表后,添加主键
ALTER TABLE stu MODIFY id INT PRIMARY KEY;
5. 自动增长:(一般配合主键使用)
1. 概念:如果某一列是数值类型的,使用 auto_increment 可以来完成值得自动增长
2. 在创建表时,添加主键约束,并且完成主键自增长
create table stu(
id int primary key auto_increment,-- 给id添加主键约束
name varchar(20)
);
3. 删除自动增长
ALTER TABLE stu MODIFY id INT;
4. 添加自动增长
ALTER TABLE stu MODIFY id INT AUTO_INCREMENT;
主键约束的相应代码如下
-- 同样先创建一个新的表
-- --------------------------------------------------在创建表的时候添加主键约束
CREATE TABLE stu(
id INT PRIMARY KEY,-- 添加主键约束
NAME VARCHAR(20)
);
# 添加主键约束后,如果将主键id设置为null,或者添加2个相同的主键,都会报错
-- --------------------------------------------------在创建表之后添加主键约束
ALTER TABLE stu MODIFY id INT PRIMARY KEY;
-- ----------------------------------------------------删除主键约束
#alter table stu modify id int; # 发现主键约束没有删除,说明这种方法不能删除主键约束
ALTER TABLE stu DROP PRIMARY KEY; #由于主键只有一个,因此我们删除的时候不需要指定具体的字段
#删除主键约束不需要加index,区别于删除唯一约束“alter table stu drop index phone_number;”
-- ------------------------------------------------------------------------------------自动增长
-- 在创建表时,添加主键约束,并且完成主键自增长
CREATE TABLE stu(
id INT PRIMARY KEY AUTO_INCREMENT,-- 添加主键约束,并指定主键自动增长
NAME VARCHAR(20)
);
# 添加主键自动增长后,我们在添加数据的时候不需要再为主键赋值,它会自动增长
INSERT INTO stu VALUES(NULL,'张柳');
# 发现主键赋值为null不会报错,因为自动增长为其赋值;自动增长的数据只与上一条记录的值相关。
-- 删除自动增长
ALTER TABLE stu MODIFY id INT;
-- 添加自动增长
ALTER TABLE stu MODIFY id INT AUTO_INCREMENT;
# 自动增长的添加与删除与非空约束的添加与删除相同
-- ----------------------------------
-- 查询整个表
SELECT * FROM stu;
-- 删除整张表
DROP TABLE stu;
-- ----------------------------------
3.4 外键约束
外键约束:foreign key,让表与表产生关系,从而保证数据的正确性。(什么是外键约束——见视频11_约束_外键约束 解析)
1. 在创建表时,可以添加外键
* 语法:
create table 表名(
....
外键列.
constraint 外键名称 foreign key (从表的外键列名称) references 主表名称(主表列名称) -- 外键一般关联主表的主键列,也可以关联主表那些被唯一约束所修饰的列
需要注意的是,外键名称与从表外键列名称是不同的,外键列名称是与主表主键关联的一列,用来关联2个表。而外键名称是对外键的命名。
);
2. 删除外键
ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;
与删除唯一约束以及删除主键约束略有不同,注意区分
3. 创建表之后,添加外键
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称);
4. 级联操作(级联操作的意义——见**视频12_约束_级联操作**,级联的使用要谨慎 )
1. 添加级联操作
语法:ALTER TABLE 表名 ADD CONSTRAINT 外键名称
FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称) ON UPDATE CASCADE ON DELETE CASCADE ;
2. 分类:
1. 级联更新:ON UPDATE CASCADE
2. 级联删除:ON DELETE CASCADE
外键约束相应的代码如下
-- 创建一张employee表
CREATE TABLE emp (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30),
age INT,
dep_name VARCHAR(30), -- 部门名称
dep_location VARCHAR(30) -- 部门地址
);
-- 添加数据
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('张三', 20, '研发部', '广州');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('李四', 21, '研发部', '广州');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('王五', 20, '研发部', '广州');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('老王', 20, '销售部', '深圳');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('大王', 22, '销售部', '深圳');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('小王', 18, '销售部', '深圳');
# 查询后发现数据有冗余:前3人都在研发部广州,后3人都在销售部深圳
# 那么这样我们应该做表的拆分,一张表存放员工信息,另一张表存放部门信息,让员工表和部门表关联
-- ----------------------------------------------------------------------------------分为2张表
-- 解决方案:分成 2 张表
-- 创建部门表(id,dep_name,dep_location)
-- 一方,主表
CREATE TABLE department(
id INT PRIMARY KEY AUTO_INCREMENT,
dep_name VARCHAR(20),
dep_location VARCHAR(20)
);
-- 创建员工表(id,name,age,dep_id)
-- 多方,从表
CREATE TABLE employee(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT,
dep_id INT -- 外键对应主表的主键
);
-- 添加 2 个部门
INSERT INTO department VALUES(NULL, '研发部','广州'),(NULL, '销售部', '深圳');
SELECT * FROM department;
-- 添加员工,dep_id 表示员工所在的部门
INSERT INTO employee (NAME, age, dep_id) VALUES ('张三', 20, 1);
INSERT INTO employee (NAME, age, dep_id) VALUES ('李四', 21, 1);
INSERT INTO employee (NAME, age, dep_id) VALUES ('王五', 20, 1);
INSERT INTO employee (NAME, age, dep_id) VALUES ('老王', 20, 2);
INSERT INTO employee (NAME, age, dep_id) VALUES ('大王', 22, 2);
INSERT INTO employee (NAME, age, dep_id) VALUES ('小王', 18, 2);
SELECT * FROM employee;
/*
我们将员工和部分分为2张表,然后department使用id标识,而employee表使用dep_id标识部门
这样看似将2个表关联起来,但是如果我们删除某一个department的某一行(既删除某一个部门,比如部门1),
但是员工表里面的部门1的人还存在。
正确的删除部门的方法应该是先把部门1里面的人删除,然后再删除部门1
*/
-- 我们需要将employee表的dep_id字段关联department表的id(主键)字段,这样就需要使用到外键约束
-- ---------------------------------------------------------------------------添加外键约束
-- 创建部门表(id,dep_name,dep_location) 一:一个部门有多个员工
-- 一方,主表(其他表,如员工表,要受到部门主表的约束)
CREATE TABLE department(
id INT PRIMARY KEY AUTO_INCREMENT,
dep_name VARCHAR(20),
dep_location VARCHAR(20)
);
-- 创建员工表(id,name,age,dep_id) 多:一个部门有多个员工
-- 多方,从表
CREATE TABLE employee(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT,
dep_id INT, -- 外键列
# 下面我们将外键列与主表对应的主键关联起来
CONSTRAINT depa_emp_fk FOREIGN KEY (dep_id) REFERENCES department(id)
# 格式:constraint 外键名称 foreign key (从表的外键列名称) references 主表名称(主表列名称)
-- employee表需要关联department表,department表需要先创建,否则报错
);
-- 这里同样再添加数据:代码省略
/*
这个时候我们再尝试删除department的id=1的行,发现不能删除,会报错,
因为系统在删除的时候发现id=1被另一张表employee用外键引用,所以限制它进行删除。
我们在员工表里面再添加一行数据,指定部门id为不存在的3,同样会因为外键的约束而报错
这样我们通过外键就可以保证数据的正确性
*/
-- ------删除外键
ALTER TABLE employee DROP FOREIGN KEY depa_emp_fk;#删除后2个表之间的约束消失
-- 创建表之后再添加外键
ALTER TABLE employee ADD CONSTRAINT depa_emp_fk FOREIGN KEY (dep_id) REFERENCES department(id);# 约束又回来了
-- ---------------------------------------------------------------------------级联操作
#如果我们想修改主表department的主键id = 1的值,由于他与从表employee的dep_ip相关联,不能对其进行修改
# 如果硬要改,得将employee的dep_id = 1的行的dep_ip先修改为null,再修改department的id=1的值,再回来修改dep_ip。很麻烦
UPDATE employee SET dep_id=NULL WHERE dep_id=1;
UPDATE department SET id = 3 WHERE id=1;
UPDATE employee SET dep_id=3 WHERE dep_id IS NULL; -- 注意,判断某个值是否为null需要用is null
-- 我们期望的是department一修改,employee也跟着修改
-- 创建表之后再添加外键,并设置级联更新与级联删除
ALTER TABLE employee DROP FOREIGN KEY depa_emp_fk;-- 同样先删除外键(除了手动测试,视频3.30介绍了另一种是否删除外键的方法)
-- 随后再添加外键
ALTER TABLE employee ADD CONSTRAINT depa_emp_fk FOREIGN KEY(dep_id) REFERENCES department(id)
ON UPDATE CASCADE ON DELETE CASCADE;
# 添加级联后改变department表的id,发现employee的dep_id 也随之改变,删除也是相同的
-- ----------------------------------
-- 查询整个表
SELECT * FROM emp;
SELECT * FROM department;
SELECT * FROM employee;
-- 删除整张表
DROP TABLE emp;
DROP TABLE department;
DROP TABLE employee;
-- ----------------------------------
4、数据库的设计——多表之间的关系
多表之间的关系分类如下
1. 一对一(了解):
* 如:人和身份证
* 分析:一个人只有一个身份证,一个身份证只能对应一个人
2. 一对多(多对一):
* 如:部门和员工
* 分析:一个部门有多个员工,一个员工只能对应一个部门
3. 多对多:
* 如:学生和课程
* 分析:一个学生可以选择很多门课程,一个课程也可以被很多学生选择
多表之间的实现关系:
1. 一对多(多对一):
* 如:部门和员工
* 实现方式:在多的一方建立外键,指向一的一方的主键。
2. 多对多:(这一部分的逻辑较难理解,结合视频15理解)
* 如:学生和课程
* 实现方式:多对多关系实现需要借助第三张中间表。中间表至少包含两个字段,这两个字段作为第三张表的外键,分别指向两张表的主键
分析多对多关系:
如果看做一个学生对应多门课程,我们需要在课程表(多)设计外键,指向学生的sid,这样我们发现,一个课程被sid=1标识,就不能被sid=2再次标识,既一门课只能被一个学生选择;如果看做一门课程对应多个学生,我们需要在学生表(多)设计外键,指向课程的cid,又发现,一个学生被cid=1标识,就不能再次被cid=2标识,也就是说一个学生只能选择一门课。
如果我们使用第三张表作为中间表,且设置2个外键(多),分别指向2张表的主键(1),这样如图所示就可以表示出多对多的关系。因为2张表的“一”都映射到第三张表的“多”上了。
第三张表2个外键的组合称之为“联合主键”,联合主键不能重复,否则就不合理了!
3. 一对一(了解):一对一关系一般合成一张表
* 如:人和身份证
* 实现方式:一对一关系实现,可以在任意一方添加唯一外键指向另一方的主键。
此处外键必须是唯一的,也就是说一个主表的主键,只能对应一个从表的外键。比如在学生表设置外键cid(多),那么cid只能对应学生id=1或者id=2,且一个id也只能对应一个cid。本来如果是多对多(学生多,身份证1),那么一个身份证id可以对应多个学生的id,也就是外键cid可以多次赋值为1或者2。但是在这里,一个cid只能对应一个id。
一对多案例:需求:一个旅游线路分类中有多个旅游线路。(这一部分可以参考——PDFMySQL约束与设计——21页——6.5)
-- 创建旅游线路分类表 tab_category(类别选项卡)
-- cid 旅游线路分类主键,自动增长
-- cname 旅游线路分类名称非空,唯一,字符串 100
CREATE TABLE tab_category(
cid INT PRIMARY KEY AUTO_INCREMENT,
cname VARCHAR(100) NOT NULL UNIQUE
);
-- 添加旅游线路分类数据:这里我们不给cid赋值,因为它会自动增长,所以会自动从1开始标号
-- 但是我们发现cid不是从1开始(不知道为啥)
INSERT INTO tab_category(cname) VALUES ('周边游'),('出境游'),('国内游'),('港澳游');
SELECT * FROM tab_category;-- 查询一下tab_category表
DROP TABLE tab_category;
-----------------------------------------------
-- 创建旅游线路表 tab_route
/*
rid 旅游线路主键,自动增长
rname 旅游线路名称非空,唯一,字符串 100
price 价格
rdate 上架时间,日期类型
cid 外键,所属分类
*/
CREATE TABLE tab_route(
rid INT PRIMARY KEY AUTO_INCREMENT,
rname VARCHAR(100) NOT NULL UNIQUE,
price DOUBLE,
rdate DATE,
cid INT,
-- 下面我们创建从表tab_route外键列字段cid所指定的外键,这里外键可以不命名
FOREIGN KEY (cid) REFERENCES tab_category(cid)
);
-- 添加旅游线路数据:这里我们不给rid赋值,因为它会自动增长,所以会自动从1开始标号
-- 这里rid从1开始自动增加,而tab_category表不是(暂时不知道为啥)
INSERT INTO tab_route(rname , price , rdate , cid) VALUES
('【厦门+鼓浪屿+南普陀寺+曾厝垵 高铁 3 天 惠贵团】尝味友鸭面线 住 1 晚鼓浪屿', 1499,'2018-01-27', 1),
('【浪漫桂林 阳朔西街高铁 3 天纯玩 高级团】城徽象鼻山 兴坪漓江 西山公园', 699, '2018-02-22', 3),
('【爆款¥1699 秒杀】泰国 曼谷 芭堤雅 金沙岛 杜拉拉水上市场 双飞六天【含送签费 泰风情 广州往返 特价团】', 1699, '2018-01-27', 2),
('【经典•狮航 ¥2399 秒杀】巴厘岛双飞五天 抵玩【广州往返 特价团】', 2399, '2017-12-23',2),
('香港迪士尼乐园自由行 2 天【永东跨境巴士广东至迪士尼去程交通+迪士尼一日门票+香港如心海景酒店暨会议中心标准房 1 晚住宿】', 799, '2018-04-10', 4);
SELECT * FROM tab_route;
DROP TABLE tab_route;
多对多案例:需求:一个用户收藏多个线路,一个线路被多个用户收藏
(参考参考——PDFMySQL约束与设计——23 页——6.6)
-----对于多对多的关系我们需要增加一张中间表来维护他们之间的关系
/*
创建用户表 tab_user
uid 用户主键,自增长
username 用户名长度 100,唯一,非空
password 密码长度 30,非空
name 真实姓名长度 100
birthday 生日
sex 性别,定长字符串 1
telephone 手机号,字符串 11
email 邮箱,字符串长度 100
*/
CREATE TABLE tab_user(
uid INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(100) NOT NULL UNIQUE,
PASSWORD VARCHAR(30) NOT NULL,
NAME VARCHAR(100),
birthday DATE,
sex VARCHAR(1) DEFAULT '男',
telephone VARCHAR(11),
email VARCHAR(100)
);
INSERT INTO tab_user VALUES
(1, 'cz110', 123456, '老王', '1977-07-07', '男', '13888888888', '[email protected]'),
(2, 'cz119', 654321, '小王', '1999-09-09', '男', '13999999999', '[email protected]');
SELECT * FROM tab_user;
-- ---------------------------------
-- 创建旅游线路表 tab_route
/*
rid 旅游线路主键,自动增长
rname 旅游线路名称非空,唯一,字符串 100
price 价格
rdate 上架时间,日期类型
cid 外键,所属分类
*/
CREATE TABLE tab_route(
rid INT PRIMARY KEY AUTO_INCREMENT,
rname VARCHAR(100) NOT NULL UNIQUE,
price DOUBLE,
rdate DATE,
);
-- 添加旅游线路数据:这里我们不给rid赋值,因为它会自动增长,所以会自动从1开始标号
-- 这里rid从1开始自动增加,而tab_category表不是(暂时不知道为啥)
INSERT INTO tab_route(rname , price , rdate ) VALUES
('【厦门+鼓浪屿+南普陀寺+曾厝垵 高铁 3 天 惠贵团】尝味友鸭面线 住 1 晚鼓浪屿', 1499,'2018-01-27'),
('【浪漫桂林 阳朔西街高铁 3 天纯玩 高级团】城徽象鼻山 兴坪漓江 西山公园', 699, '2018-02-22'),
('【爆款¥1699 秒杀】泰国 曼谷 芭堤雅 金沙岛 杜拉拉水上市场 双飞六天【含送签费 泰风情 广州往返 特价团】', 1699, '2018-01-27'),
('【经典•狮航 ¥2399 秒杀】巴厘岛双飞五天 抵玩【广州往返 特价团】', 2399, '2017-12-23'),
('香港迪士尼乐园自由行 2 天【永东跨境巴士广东至迪士尼去程交通+迪士尼一日门票+香港如心海景酒店暨会议中心标准房 1 晚住宿】', 799, '2018-04-10');
SELECT * FROM tab_route;
DROP TABLE tab_route;
-- ---------------------------------------
/*
旅游线路表 tab_route与用户表 tab_user已经创建,
因为一个用户可以收藏多条线路,而一条线路可以被多个用户收藏
接下来创建收藏表 tab_favorite,这张表(多)描述表tab_route与表 tab_user所对应的外键
*/
/*
创建收藏表 tab_favorite
rid 旅游线路 id,外键
date 收藏时间
uid 用户 id,外键
rid 和 uid 不能重复,设置复合主键,同一个用户不能收藏同一个线路两次
*/
CREATE TABLE tab_favorite(
rid INT, -- 旅游线路 id,外键
d DATE,
uid INT, -- 用户 id,外键
-- ------------------------------------------外键设置(不为外键命名)
-- 创建复合主键(主键部分不需要赋值):联合主键使得rid和uid不能重复
PRIMARY KEY(rid,uid),
FOREIGN KEY (rid) REFERENCES tab_route(rid),
FOREIGN KEY (uid) REFERENCES tab_user(uid)
);
-- 增加收藏表数据
INSERT INTO tab_favorite VALUES
(1, '2018-01-01', 1), -- 老王选择厦门
(2, '2018-02-11', 1), -- 老王选择桂林
(3, '2018-03-21', 1), -- 老王选择泰国
(2, '2018-04-21', 2), -- 小王选择桂林
(3, '2018-05-08', 2), -- 小王选择泰国
(5, '2018-06-02', 2); -- 小王选择迪士尼
SELECT * FROM tab_favorite;
DROP TABLE tab_favorite;
在测试的过程中,必须先创建表tab_user与表tab_route,否则表tab_favorite无法创建。
5、数据库的设计——范式
范式(Normal Form)概念:设计数据库时,需要遵循的一些规范。要遵循后边的范式要求,必须先遵循前边的所有范式要求。
设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。(一般只需要遵循前三种范式即可)
范式的分类如下:(这一部分主要见视频19——三大范式分析)
1. 第一范式(1NF):每一列都是不可分割的原子数据项
2. 第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于码。在1NF基础上消除非主属性对主码的部分函数依赖。 也就是说,如果我们想唯一确定一个非主属性的值,必须通过所有的主属性来确定,而不能单单通过某一个主属性来确定某一个非主属性。
* 几个概念:
1. 函数依赖:A ——> B,如果通过A属性(属性组)的值,可以确定唯一B属性的值。则称B依赖于A
例如:学号 ——> 姓名。 (学号,课程名称) ——> 分数
2. 完全函数依赖:A ——> B, 如果A是一个属性组,则B属性值的确定需要依赖于A属性组中所有的属性值。
例如:(学号,课程名称) ——> 分
3. 部分函数依赖:A ——> B, 如果A是一个属性组,则B属性值得确定只需要依赖于A属性组中某一些值即可。
例如:(学号,课程名称) ——> 姓名
4. 传递函数依赖:A ——> B, B ——> C . 如果通过A属性(属性组)的值,可以确定唯一B属性的值,在通过B属性(属性组)的值可以确定唯一C属性的值,则称 C 传递函数依赖于A
例如:学号 ——> 系名,系名 ——> 系主任
5. 码:如果在一张表中,一个属性或属性组,被其他所有属性所完全依赖,则称这个属性(属性组)为该表的码
例如:该表中码为:(学号,课程名称)
* 主属性:码属性组中的所有属性
* 非主属性:除过码属性组的属性
判断一个属性(属性组)是否是一张表中的码,我们只需要判断通过这个属性(属性组)能否唯一确定其他属性即可。
4. 第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)
如上图,“系”这一列还可以分割为“系名”和“系主任”2列,说明其不具备第一范式(每一列都是不可分割的原子数据项)原则
为了解决上面存在的问题,我们需要用到第二范式。使用2NF消除非主属性对主属性的部分函数依赖。在下图第二张表中,“学号”可以唯一确定其他属性,它可以作为这张表的码。当然,“姓名”也可以唯一确定其他属性,那么“姓名”也可以作为主属性。
使用2NF后,仍然存在问题,我们就需要用到第三范式。3NF使得任何非主属性不依赖于其他非主属性,消除传递依赖。我们发现上图中“学号”被“系名”所依赖,而“系名”被“系主任”所依赖,出现了传递依赖。既通过“学号”可以唯一确定“系名”,而“系名”可以唯一确定“系主任”,那么他们之间存在传递依赖,将他们消除。
如上图,视频里面说所有的传递依赖都消失了,其实并没有,我们看第二张“学生表”,发现“学号”可以唯一确定姓名,而“姓名”可以唯一确定系名,还是存在传递依赖。这里视频没有解释,我就自己总结一下。
我们看到“学生表”中,“学号”可以作为主属性,而“姓名”也可以作为主属性(但是“系名”不能作为主属性),那么我们在判断是否存在传递依赖的时候,如果传递依赖中出现的中间元素也可以作为主属性,我们不将这种情况视为传递依赖。
总结:如何使用范式:
1、先使用1NF,使得每一列都不可分割;
2、再使用2NF,使用的时候先确定表的码(主属性),将部分依赖于主属性的非主属性剔除出去,构造第二张表,使得表里面非主属性对主属性只有完全依赖。
3、最后使用3NF,将非主属性之间的依赖消除,既消除表中的传递依赖。最后表中可能有多个属性可以作为主属性,如果出现传递依赖,多个可以作为主属性的属性出现在传递依赖的中间(既主属性1——> 主属性2,主属性2——>非主属性),那么这种其实不算传递依赖。
6、数据库的备份和还原
命令行:
* 语法:
* 备份: mysqldump -u用户名 -p密码 数据库名称 > 保存的路径
如:mysqldump -uroot -proot db1> G://a.sql (在使用的时候,直接在命令行一开始使用即可,而不需要进入数据库的数据文件中使用)
注意,Windows中使用“\”作为路径分隔符,而cmd中使用“/”作为路径分隔符,且作为字符串表示,必须使用“//”
* 还原:
1. 登录数据库 : mysql -uroot -proot;
2. 创建数据库 : create database db1;
3. 使用数据库 : use db1;
4. 执行文件,将备份的数据库文件的数据还原到当前数据库中,格式:source 文件路径 : source G://b.sql;
如:“mysqldump -uroot -proot > G://a.sql ”。保存的a.sql文件里面存储的其实就是一些SQL语句。其实保存的是有问题的,因为我们没有指定要备份保存哪一个数据库。那么我们重新执行“mysqldump -uroot -proot db1> G://b.sql ” ,发现db1数据库的数据正常备份。
为了测试通过“b.sql”文件还原数据库,我们先登录数据库并删除这个数据库db1。(注意,在cmd中语句后面加上分号)随后我们登录数据库——创建一个新的数据库——使用这个新的数据库——将之前备份的“b.sql”文件加载到新的数据库里。
图形化工具SQLyog。(具体看视频怎么做)导出:右键——备份/导出——备份数据库,转储到SQL;导入:root@local右键——执行SQL脚本(注意不是选择导入外部数据)