六天带你玩转Mysql笔记--第三天
1. 字段属性
1.1 主键(primary key)
1.1.1增加主键
1.1.2 主键约束
1.1.3 主键更新&删除主键
1.1.3 主键分类
1.2 自动增长
1.2.1 新增自增长
1.2.2 自增长的使用
1.2.3 修改自增长
1.2.4 删除自增长
1.2.5 唯一键(unique key)
1.3 索引
2关系
2.1 一对一
2.2 一对多
2.3 多对多
3. 范式
3.1 INF
3.2 2NF
3.3 3NF
3.4 逆规范化:
4. 数据高级操作
4.1 主键冲突
4.2 蠕虫复制
4.3 更新数据
4.4 删除数据
4.5 查询数据
4.6 数据源
4.7 where子句
4.8 group by
4.9 having子句
4.10 Order by子句
4.11 limit子句
主键,唯一键和自增长。
一张表中只有一个字段可以使用主键,用来唯一约束该字段里面的数据,使其不能重复。
方案1:在创建表的时候,直接在字段之后跟primary key关键字(主键本身不允许为空)
代码:
-- 增加主键
create table my_pril(
name varchar(20) not null comment '学生姓名',
number char(10) primary key comment '学生学号,itcast+0000'
)charset utf8;
效果:
方案2:在创建表的时候,在所有字段之后,使用primary key(主键列表)来创建主键(如果有多个字段作为主键,则为复合主键)
代码:
-- 复合主键
create table my_pri2(
number char(10) comment'学号:itcast+0000',
course char(8) comment'课程代码:3901+0000',
score tinyint unsigned default 60 comment'成绩',
-- 增加主键
primary key(number,course)
)charset utf8;
效果:
方案3:当表已经创建好了之后,再次额外追加主键。可以通过修改字段属性,也可以直接追加:alter table 表名 add primary key(字段列表);
代码:
-- 追加主键测试
-- 创建没有主键的表
create table my_pri3(
course char(8) not null comment'课程编号:3901+0000',
name varchar(10) not null comment'课程名字'
)charset utf8;
-- 查看表
desc my_pri3;
-- 追加主键
alter table my_pri3 add primary key(course);
-- 查看表
desc my_pri3;
效果:
主键对应的字段中的数据不允许重复。一旦重复,则数据插入失败。
代码:
-- 查看表字段
desc my_pri2;
-- 向my_pri2表插入数据
insert into my_pri2 values
('itcast0001','68010001'80),
('itcast0001','68010002',90),
('itcast0001','68010002',80);
效果:
没有办法更新主键,主键必须先删除,才能增加。
Alter table 表名 drop primary key;
在实际创建表的过程中,很少使用真实的业务数据作为主键字段(业务主键,如学号,课程号)。大部分的时候使用逻辑性的字段(字段没有业务含义,值是什么都没有关系),将这种字段主键称为逻辑主键。
代码:
-- 使用逻辑型字段作为主键
create table my_student(
Id int primary key auto_increment comment'逻辑主键:自增长',
Number char(10) not null comment'学号',
name varchar(10) not null
)charset utf8;
-- 查看表
desc my_student;
效果:
(1)当相应字段为默认值或Null,自增长会自动被系统触发。系统会从当前字段中已有的最大值再进行+1操作,得到一个新的不同的字段。
(2)自增长通常是跟主键搭配.
(3)自增长特点:auto_increment
1. 任何一个字段要做自增长,本身必须是一个索引(key)。
-- 自增长
create table my_auto(
id int auto_increment comment'自动增长',
name varchar(10) not null
)charset utf8;
2.自增长字段必须是数字(整形)。
-- 自增长
create table my_auto(
id varchar(1) auto_increment comment'自动增长',
name varchar(10) not null
)charset utf8;
3.一张表最多只能有一个自增长。
(1)自增长的第一个元素是1
(2)自增长每次自增1
-- 三种方式触发自增长
insert into my_auto (name) values('邓丽君');
insert into my_auto values(null,'baby');
insert into my_auto values(default,'汪涵');
-- 查看表数据
select * from my_auto;
(1)自增长涉及到字段改变,必须先删除自增长,后增加(一张表只能有一个自增长)。修改当前自增长已经存在的值(修改的值必须比当前自增长最大值还要大)。Alter table 表名 auto_increment = 值;
(2)思考:为什么自增长是从1开始且每次自增1?
所有系统的实现(如字符集,校对集)都是有系统内部的变量进行控制的。
查看自增长对应的变量:show variables like ‘auto_increment%’;
自增长是字段的一个属性:可以通过modify来进行修改(保证字段没有auto_increment即可)Alter table 表名 modify 字段 类型;
alter table my_auto modify id int primary key; -- 错误:理论上主键是单次存在的。
alter table my_auto modify id int; -- 有主键的时候,不能再添加主键。
-- 查看表
desc my_auto;
(1)一张表往往需要多个字段具有唯一性(数据不能重复),但是一张表中只能有一个主键。唯一键可解决表中多个字段需要唯一性约束的问题。
(2)唯一键的本质和主键差不多,唯一键默认允许自动为空,而且多个为空(空字段不参与唯一性比较)。
(3)增加唯一键
方案1:在创建表的时候,字段后直接跟unique/unique key。
-- 唯一键
create table my_unique1(
number char(10) unique comment'学号:唯一允许为空',
name varchar(20) not null
)charset utf8;
方案2:在所有的字段后增加unique key(字段列表); 复合唯一键
-- 复合唯一键
create table my_unique2(
number char(10) not null comment'学号:唯一允许为空',
name varchar(20) not null,
unique key(number)
)charset utf8;
方案3:在创建表后增加唯一键。
create table my_unique3(
id int primary key auto_increment,
number char(10) not null,
name varchar(20) not null
)charset utf8;
alter table my_unique3 add unique key(number);
(4)唯一键的约束
唯一键与主键本质相同,唯一区别是唯一键默认允许为空,而且是多个空。
(5)更新唯一键&删除唯一键
更新唯一键:先删除后新增(唯一键可以有多个,可以不删除)
删除唯一键:
Alter table 表名 drop index 索引名字; -- 唯一键默认使用字段名作为索引名字。
(1)几乎所有的索引值都是建立在字段之上
(2)索引:系统根据某种算法,将已有的数据(未来可能新增的数据),单独建立一个文件,文件能实现快速的数据匹配数据,并且能快速的找到对应表中的记录。
(3)索引的意义
1)提升查询数据的效率
2)约束数据的有效性
(4)增加索引的前提条件:索引本身会产生索引文件(有时候可能比数据文件还大),会非常耗费磁盘空间。
(5)如果某个字段需要作为查询的条件经常使用,那么可以使用索引(一定会想办法增加);
如果某个字段需要进行数据的有效性约束,也可以使用索引(主键,唯一键)。
(6)mysql中提供了多种索引
1)主键索引:primary key
2)唯一索引:unique key
3)全文索引:fulltext index
4)普通索引:index6
全文索引:针对文字内部的关键字进行索引
全文索引的最大问题在于如何确定关键字
英文很容易:英文单词与单词之间有空格
中文很难:没有空格,而且中文可以随意组合
将实体与实体的关系(反应到最终数据库表的设计上来):将关系分成三种:一对一,一对多和多对多。
(1)一张表的一条记录只能和另外一张表的记录进行对应,反之亦然。
(2)学生表:姓名,性别,年龄,身高,体重,婚姻状况,籍贯,家庭住址,紧急联系人
Id(P) |
姓名 |
性别 |
年龄 |
体重 |
身高 |
婚姻 |
籍贯 |
住址 |
联系人 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(3)表设计成以上这种形式是符合要求的。
(4)但是姓名,年龄,身高,体重属于常用数据;婚姻,籍贯,住址和联系人属于不常用数据。如果每次查询都是查询所有数据,不常用的数据就会影响效率。
(5)解决方案:将常用和不常用的信息分离存储,分成两张表。
常用信息表
Id(P) |
姓名 |
性别 |
年龄 |
体重 |
身高 |
|
|
|
|
|
|
|
|
|
|
|
|
不常用信息表
Id(P ) |
婚姻 |
籍贯 |
住址 |
联系人 |
|
|
|
|
|
|
|
|
|
|
(6)不常用的信息表需保证不常用信息与常用信息一定能够对手,找到一个具有唯一性的字段来共同连接两张表
(7)一个常用表中的一条记录,永远只能在一张不常用表中匹配一条记录;反过来,一张不常用表中的一条记录在常用表中只能匹配一天记录也只能匹配一条记录,即一对一关系。
表A中有一条记录可以对应表B中的多条记录;但是表B中的一条记录只能对应表A中的一条记录。
母亲与孩子的关系:母亲,孩子两个实体
妈妈表
ID(P) |
姓名 |
年龄 |
性别 |
|
|
|
|
|
|
|
|
孩子表
ID(P) |
姓名 |
年龄 |
性别 |
|
|
|
|
|
|
|
|
一个妈妈可在孩子表中找到多条记录;但是一个孩子只能找到一个妈妈的记录(典型一对多的关系)。
以上设计解决了实体设计表问题,但是没有解决关系问题。孩子找不出妈,妈也找不到孩子。
解决方案:在某一张表中增加一个字段,使其能找到另外一张表的记录。应该在孩子表中增加一个字段指向妈妈表。因为孩子表的记录只能匹配到一条妈妈表的记录。
孩子表
ID(P) |
姓名 |
年龄 |
性别 |
妈妈id |
|
|
|
|
|
|
|
|
|
|
表A一条记录对应表B中的多条记录;同样,表B中的一条记录对应表A中的多条记录。
教师教学:老师和学生
老师表
T_ID(P) |
姓名 |
性别 |
1 |
A |
男 |
2 |
B |
女 |
学生表
S_ID(P) |
姓名 |
性别 |
1 |
张三 |
男 |
2 |
小芳 |
女 |
以上设计方案:实现了实体的设计,但是没有维护实体的关系。
一个老师教过多个学生;一个学生有多个老师。
解决方案:增加一张新表
中间关系表:老师月学生的关系
ID |
T_ID |
S_ID |
1 |
1 |
1 |
2 |
1 |
2 |
3 |
2 |
1 |
4 |
|
|
中间表与老师表形成了一对多的关系。而中间表示多表,维护了能够唯一找到一表的关系。
学生照老师:找出学生id -> 中间表寻找匹配记录(多条)-> 老师表匹配(一条)
老师找学生:找出老师id -> 中间表寻找匹配记录(多条)-> 学生表匹配(一条)
范式是一种离散化的数学知识,是为了解决一种数据存储与优化的问题。保存数据的存储之后,凡是能够通过关系寻找出来的数据,坚决不再重复存储。终极目标是减少数据冗余。
凡是是一种分层结构的规范,分为六层。每一层都比上一层更加严格,若要满足下一层范式,前提是满足上一层范式。
六层范式:1NF,2NF,3NF.....4NF。1NF是最低层,要求最低。6NF是最高层,最严格。
Mysql属于关系型数据库,有空间浪费。故气致力于节省空间,与范式不谋而合。在设计数据库的时候,会利用范式指导设计。
但是数据库不但是解决空间问题,还要保持效率问题。范式值解决空间问题,所有数据库的设计不能完全按照范式要求实现。一般情况下,只有前三种范式需要满足。
范式在数据库的设计中有指导意义,但是不是强制规范。
第一范式:在设计储存数据的时候,如果存储的表中在取出来使用前还需要额外的处理(字段拆分),则该表的设计不满足第一范式。第一范式要求字段的数据具有原子性(不可再分)。
如果需求是将数据查出来后,要求显示一个老师从什么时候开始上课,到什么结课。需要将代课时间进行拆分,故不符合1NF,数据不具有原子性,可以再拆分。
解决方案:将代课时间拆分成两个字段就可解决问题。
第二范式:在数据表设计过程中,如果有复合主键(多字段主键),且表中有字段并不是由整个主键来确定,而是依赖主键中的某个字段(主键部分)。存在字段依赖主键的部分问题
以上表中:因为讲师没有办法作为主键,需要结合班级才可以作为主键(复合主键)。代课时间,开始和结束都依赖主键(讲师和班级);性别不依赖班级,只依赖讲师;教师只依赖班级,依赖讲师。故不符合第二范式。
解决方案1:可以将性别和讲师单独成表,班级与教师单独成表。
解决方案2:取消复合主键,使用逻辑主键。
ID = 讲师 + 班级
第三范式:理论上讲,一张表中的所有字段都应该直接依赖主键(逻辑主键除外)。人员工艺表设计中存在一个字段,并不直接依赖主键,而是通过某个非主键字段依赖,从而实现依赖主键。这种依赖非主键字段的依赖关系称之为传递依赖,第三范式就是要解决传递依赖。
讲师代课表
以上设计方案中,性别依赖讲师存在,讲师依赖主键。教师依赖班级,班级依赖主键。性别和班级都存在传递依赖。
解决方案:将存在传递依赖的字段,以及依赖字段本身单独取出来形成一个单独的表。
讲师代课表
讲师表 班级表
讲师表:ID等价于讲师 班级表:ID等价于班级
讲师表和班级表的真正意义上的主键分别是讲师和班级,id只是逻辑主键,为的是解决教师和班级可能出现同名的问题,故不存在传递依赖。
磁盘利用率与效率的对抗。
基本语法
Insert into 表名 [(字段列表)] values(值列表);
当主键冲突时,可以选择性进行处理:更新和替换
(1)主键冲突:更新操作
Insert into 表名 [(字段列表)] values(值列表) on duplicate key update 字段=新值;
(2)主键冲突:替换
Replace intto 表名 values(值列表);
(1)从已有的数据中获取数据,然后将数据进行新增操作(数据成倍增加)
表创建高级操作:从已有表创建新表(复制表结构)。
create table my_copy like my_student;
(2)蠕虫复制:先查出数据,然后将查出的数据新增一遍。
Insert into 表名 [(字段列表)] select 字段列表/* from 数据表名;
(3)蠕虫复制意义:
1.从已有的表拷贝数据到新表
2.可以迅速让表中的数据膨胀到一定的数量级:测试表的压力以及效率
基本语法:update 表名 set 字段 = 值[where 条件];
高级新增语法:
update 表名 set 字段 = 值[where 条件] [limit 更新数量];
(1)Delete from table 表名 [where 条件] [limit 删除数量];
(2)数据的删除时不会改变表的结构,只能删除表后重建表。
Truncate 表名; -- 先删除表,后新增表
(1)基本语法:
Select */字段列表 from 表名[where 条件];
(2)完整语法:
Select[select选项] 字段列表[字段别名]/* from 数据源 [where 条件子句][group by子句][having 子句][order by子句][limit 子句];
(3)select选项:对查出的结果的处理方式
All:默认的,保留所有的结果
Distinct:去重,将查出结果重复部分去掉(所有字段相同,才叫重复)
(4)字段别名: 字段 [as] 别名
select Id,
Number as 学号,
name as 姓名
from my_student;
数据来源,关系型数据库的来源都是数据表。本质上只要保证数据类似二维表,最终都可以作为数据源。
数据源分为多种:单表数据源,多表数据源,查询语句
(1)单表数据源:select * from 表名;
(2)多表数据源:select * from 表名1,表名2.....;
笛卡尔积没有意义,应尽量避免。
(3)子查询:数据的来源是一条查询语句(查询语句的结果是二维表)
Select * from(select 语句)as 表名;
(1)用来判断数据,筛选数据
(2)Where子句返回结果:0或者1,0代表false,1代表true
(3)判断条件:
比较运算符:>,<,>=,<=,!=,=,<>,like,between and,in/not in
逻辑运算符:&&(and),||(or),!(not)
(4)where原理:where是唯一一个直接从磁盘获取数据的时候就开始判断的条件。从磁盘取出一条记录,开始进行where判断,判断的结果如果成立就保存到内存;如果失败就直接放弃。
(5) Between本身是闭区间:左边的值必须小于或者等于右边的值。
(1)Group by是分组的意思,根据某个字段进行分组(相同的放在一组,不同的分到不同的组)。
(2)基本语法:group by 字段名;
(3)分组的意义是为了统计数据(按组统计:按分组字段进行数据统计)
(4)Sql提供了一系列统计函数
1)count():统计分组后的记录数(每组分别有多少记录)
2)Max():统计每组中的最大值
3)Min():统计最小值
4)Avg():统计平均值
5)Sum():统计和
(5)Count函数两张参数:(1)*代表统计记录(2)字段名代码统计对应的字段(不统计Null)
(6)分组会自动排序:根据分组字段,默认升序
Group by 字段 [asc|desc]; -- 对分组的结果进行排序
(7)多字段分组:先根据一个字段分组,然后对分组后的结果再次按照其他字段进行分组。
(8) Group_concat(字段):对分组的结果中的某个字段进行字符串连接(保留该组所有的某个字段)。
(1)Where是针对磁盘数据进行判断,有效数据进入内存后,进行分组操作,分组的结果需要having来处理。
(2)Having能做where能做的所有事情,但是where却不能坐having能做的很多事情。
(3)分组统计的结果或者说统计函数都只能在having中使用
(4)Having能够使用字段别名,而where不能。 Where是从磁盘取数据,而别名是在字段进入内存后才会产生的。
(1)Orderby:排序,根据某个字段进行升序或降序排序,依赖校对集。
(2)使用基本语法:order by 字段名[asc/desc]
(3)排序可以进行多字段排序:先根据某个字段进行排序,然后按照排序号的内部,再根据某个数据进行排序。
(1)Limit子句是一种限制结果的语句:限制数量
(2)Limit两种使用方式:
1)方案1:只用来限制长度(数据量):limit数据量
Select * from my_student limit 2;
2)限制起始位置,限制数量:limit起始位置,长度
Select * from my_student 0,2 -- 从0开始,寻找两个
limit方案2主要是用来实现数据的分页,为用户节省时间,提高服务器的响应效率,减少资源的浪费。
对于用户来讲,可以点击分页按钮:1,2,3,4
对服务器来讲:根据用户选择的页码来获取不同的数据:
limit offset,length
Length:每页显示的数据量,基本不变
Offset = (页码-1)*每页显示量