一. 数据库介绍
-
数据库分类:
- 层次数据库
- 关系数据库
- 网络数据库
-
按照找当前市面上根据实际数据模型分类:
- 关系型数据库:SQL
- 非关系型数据库:NoSQL
-
关系型数据库:建立在关系模型上的数据库
转化为二维表进行存储,存储介质是磁盘
产品代表:
- 大型:Oracle、DB2
- 中型:SQLServer、MYSQL
- 小型:Access、SQLite
解决的问题主要是:数据的有效管理和持久化存储
-
非关系型数据库:不采用关系模型的数据库
存储介质大部分是内存(一旦不使用可能消失)
解决的主要问题是:高并发和效率
二. 关系型数据库(系统)概念
- 关系型数据库系统(Database System,DBS)
- 由外到内:数据库管理系统、数据库、数据表、字段(数据列)
- 是C语言底层实现的结构化查询语言
- DBS->DBMS->DB->Table->Field(columns)
三.SQL(Structured Query Language)
- SQL是一种结构化的查询语言
- DQL:数据查询语言
- DML:数据操作语言
- DDL:数据定义语言
- DCL:数据控制语言(用户权限管理)
- TPL:事物处理语言,辅助DML(关系型数据库所特有)
四. MYSQL基本介绍
MYSQL存储引擎
- MyIsam:不安全,访问快
- InnoDB:默认存储引擎,安全,大量维护占用磁盘高
- Memory:效率高,不支持模糊查询,要收费,服务重启数据就会丢失
- 服务端与客户端
五. MYSQL服务器运行管理
- servers.msc打开服务管理
- net start打开MYSQL服务
六. MYSQL数据库基本操作
(一).SQL语法规则
- 语法环境:SQL执行必须是客户端与服务器端建立联系的情况下
- SQL语句按顺序执行
- 语句结束符
- 英文分号
- \g:与分号的效果一样
- \G:数据结果以行为单位纵向展示
- SQL指令关键字不区分大小写
- 注释:
- 行注释 :#和-- (空格)
- 块注释:/* */
(二).SQL库操作
-
查看数据库
- 查看所有数据库:show databases;
- 查看部分数据库:show databases like ‘pattern’;
- 占位符_(下划线):匹配固定位置单个字符, _a可以是Aa, aa, sa
- 占位符%(百分号):匹配多个字符, %a可以是任何以a结尾的字符
- 为了解决实际需求,_和%可以无限制使用
- 查看创建数据库时所用的指令:show create database …;
-
创建数据库
-
创建数据库:create database 数据库名字(没有引号);
-
如果数据库名字使用到了SQL内部关键字或者保留字,应该使用 反引号包裹(数字键1左边对应的英文状态)不建议
-
数据库创建时可以指定数据库字符集:create database 数据库名字 charset 字符集(不允许使用中划线)
create database test charset utf8;
-
数据库创建成功后都会在MYSQL文件夹下面创建一个对应的文件夹
-
-
修改数据库字符集
- 5版本址前是允许修改数据库名字的,但5版本后不允许
- 可以修改默认字符集
- alter database 数据库名字 charset 新字符集
- 这个修改是针对以后再数据库创建的结构的默认字符集,不会修改已经存在了的数据表对应的字符集
-
删除数据库
- 删除数据库:drop database 数据库名字
- 删除数据库会直接删除数据库中的所有内容,而且不可找回,操操作之前一定要备份
- 数据库的删除一次只能删除一个,不能删除多个
- 数据库的删除还会删除数据库对应的文件夹
(三).数据表(字段)操作
-
创建数据表
create table (数据库名.)表名(
字段名 字段类型 [字段属性],
字段名 字段类型 [字段属性],
…
字段名 字段类型 [字段属性],
)[表选项];
-
表名:如果使用关键字或者保留字作为表名应该使用反引号,表名在同一数据库下不能重复
-
字段名:二维表列名字,同一表内字段名不能重复
-
字段类型:每列数据都有规定的数据类型
-
表选项:表的额外属性,包括存储引擎,字符集,校对集
-
可以不先进入数据库,在创建表的时候在指定数据库
如:
create table child(
name varchar(20),
age int,
id varchar(10));
-
表选项1:存储引擎,数据的存储方式,默认是InnoDB。存储引擎的语法结构是:engine[=]InnoDB/MyIsam
如:create table my_engine1(
name varchar(10)
)engine InnoDB;
create table my_engine2(
name varchar(10)
)engine = MyIsam;
-
InnoDB与MyIsam的区别:
- InnoDB会创建一个结构和一个数据、索引文件
- MyIsam会创建三个文件:结构、数据、索引
-
表选项二:字符集,字符集的语法为:[default]charset[=]字符集
如:create table my_charset1(
name varchar(10)
)default charset = utf8;
create table my_charset2(
name varchar(10)
)charset utf8;
-
两个表选项可以同时存在,即写在同一条命令中
-
创建表的特殊方式:
-
使用SQL指令创建仿造已经存在的表创建新表:create table 表名 like [数据库名.]旧表名
如:create table my_table_new like my_table;
不会复制其中的数据 -
可以直接复制MyIsam结构表的三个文件到另外的数据库,MyIsam表是独立的空间,可以移动有效,而InnoDB是非独立空间,直接移动是没办法生效的
-
-
-
查看数据表:
-
查看全部已存在表:show tables;要求已进入数据库环境
-
查看部分匹配表名:show tables like ‘pattern’
匹配模式也是’_‘单个字符匹配和’%'多个字符匹配
-
查看表结构,就是现实表中所有字段的详细信息:
desc/describe/[show columns from] 表名
-
查看表创建语句,可以方便看出表的结构信息以及表选项控制:
show create table 表名
这种结构查看适合使用\G,会更清晰:show create table 表名\G
-
-
更新数据表
-
更新表名,表的名字可以修改:
rename table 旧表名 to 新表名
-
更新表选项,即更新表后期对应信息(不建议修改):
alter table 表名 表选项修改(charset = )
表选项通常再一开始确定后就不再修改,尤其是当表 内已经产生数据后更不要轻易修改(尤其是字符集修改)
-
修改表字段或者属性,这块操作比较复杂,涉及到字段新增、字段修改(名字+属性)、字段删除等操作,都是使用alter table表名来操作
-
增加表字段,本质是增加表的字段名以及字段类型(属性):alter table 表名 add [column] 字段名 字段类型 [属性];
-
修改表字段名,也是强制要求必须修改对应的字段类型:alter table 表名 change [column] 旧字段名 新字段名 字段类型 [属性]
改了名字也要加上字段类型
支持名字和类型同时修改
-
修改表字段类型(属性):alter table 表名 modify [column] 字段名 新字段类型 [属性];
change 可以修改字段类型和属性(不建议),只需要使用:alter table 表名 change 旧名字 旧名字 字段类型
-
删除表字段:alter table 表名 drop [column] 字段名
-
-
修改字段位置:字段默认的操作(新增)都是在所有字段最后,我们可以通过位置指令来实现字段放到任意位置(change 和 modify 指令也可以修改位置,add增加时也可以指定位置)
-
first:字段放到第一个位置
如:alter table student add name int first;
-
after 字段名:放到指定字段名的后面
如alter table student add name int after id;
alter table student modify name varchar(10) after sex;
通过change或者modify或者add来指定位置都是在语句末尾添加first或者after来实现
注意:drop 字段操作会将该字段对应的数据也一并删除,所有请确保安全(备份)
-
-
-
数据表删除
-
删除表语法结构:drop table 表名
-
删除表的时候MYSQL允许同时删除多张表
-
在项目前期或者备份还原数据的时候,有的时候会特意去删除已经存在的同名数据表,这个时候也会用到删除操作,不过在操作的时候会加上一个判断,保证删除表结构的指令不会被报错:
drop table if exists 表名
可以用show warnings查看警告
-
(四).数据操作
-
新增数据
-
新增一条完整数据:对表中对应的一条空白记录处所有字段数据:insert [into] 表名 values(字段1对应的值、字段2对应的值…)
- 值元素的数量和字段数量要一致
- 字段的顺序与值的顺序要对应上
- 字符串数据需要使用引号包裹,可单可双
-
新增指定字段数据:给表中对应的一条空白记录出指定字段数据:insert into 表名(字段1、字段2)values(值1、值2)
- 值元素的数量要与指定的字段数量一致
- 值元素的顺序要与指定字段顺序一致(字段顺序可以不和表字段的顺序一致)
- 没有被选中的其他表字段不能因为没有数据而报错
-
新增多条记录:可以是完整的或者指定字段的多条记录:insert into 表名[(字段列表)] values (值列表1)、(值列表2)
insert into child values ("fanglei","woshizhu");
-
-
查询数据
-
查看所有数据:select * from 表名;*属于通配符,表示匹配所有字段信息
-
查看指定字段数据:select 字段名1,字段名2… from 表名
-
匹配数据查看:查看数据的时候根据适当的条件筛选数据:
select 字段列表/* from 表名 where 条件表达式
-
-
更新数据
-
更新全部数据的某个字段信息:update 表名 set 字段名 = 新值
这个操作是针对某个字段的全部数据
-
根据更新条件实现部分记录更新:update 表名 set 字段名 = 新值 where 条件表达式
-
可以同时修改多个字段数据:update 表名 set 字段1 = 新值1, 字段2 = 新值2…[where 条件表达式]
-
-
删除数据
-
删除全部数据:delete from 表名;
数据删除通常操作都是逐个删除
-
删除部分条件匹配数据:delete from 表名 where 条件表达式
-
七.MYSQL数据库细节
(一).字符集
-
字符集概念
- ASCII:美国信息交换标准代码,单字节编码,一个字节最大值是256,足够表示所有拉丁字符,ASCII实际采用7位128哥字符
- latin1:拉丁文字符集,能够向下兼容ASCII,比ASCII更复杂
- GB2312:信息交换用汉字编码字符集
- GBK:汉字内码扩展规范,使用双字节编码方案(汉字很多超过5000)
- Unicode:万国码
- UTF-8:针对Unicode的可变长度字符编码,采用1-6个字节编码Unicode字符(目前通用编码规则),更节省空间
-
MYSQL字符集详解
-
查看MYSQL服务器所支持的字符集规范:
show character set;
-
服务器端支持大量字符集格式,客户端只需要告知服务器端对应的数据字符集即可。但是客户端的数据字符集是不确定的,服务器端怎么能够知道呢?在服务器端有一个规范,是系统去进行数据操作的规则,这些规范是内部变量控制。这种规范的默认值是在安装MYSQL的时候确定的,可以通过以下命令查看:
show variables like ‘character%’
-
与客户端打交道的有三个:
- character_set_client:客户端给服务器端的数据字符集
- character_set_connection:为底层字符集和校对集使用(非表操作)
- character_set_results:服务器提供给客户端的数据字符集
-
怎么修改这些数据,从而让服务器能够与客户端正确通信呢?
- set character_set_client = 客户端字符集:能够让服务器端正确接收客户端数据(告诉服务器端客户端到底是什么类型的数据,从而使服务器端能正常翻译)
- set character_set_results = 客户端字符集:能够让服务器端给客户端提供正确的数据(告诉服务器端客户端能接收翻译什么类型的数据)
- set character_set_connection = 客户端字符集:能够允许客户端发送其他非表操作指令时服务器端正确准备数据
-
一个指令对客户端的三个数据进行修改(第四点的三条):
set names 客户端字符集
-
(二).校对集
-
校对集的概念:某个指定字符集下进行比较的校对规则的集合
-
在MYSQL中,校对集分为三种格式
-
_ci:case insensitive 不区分大小写(针对字母)
默认校对集
-
_cs:case sensitive 区分大小写(针对字母)
-
_bin:binary:二进制比较,本质上也区分大小写
-
-
校对集是根据字符集设定,并非所有的字符集都支持三种校对集,事实上,字符集通常支持的是大小写不敏感的(_ci)和二进制比较(__bin)。在MYSQL中可以通过show collation命令来查看所有支持的校对集
-
-
校对集的设定
-
指定校对集,数据表的校对集可以在创建表的时候利用表选项指定:create table 表名字(表字段 字段类型)collate [=] 校对集规则(如果没有指定就沿用默认校对集)
-
查看数据表校对集:用查看数据表的创建语句来实现校对集查看
注意:校对集可以针对数据字段来设定,但一般让整表同一校对集(字符集也一样)
-
数据表创建的时候如果不指定校对集,则会自动使用字符集所对应的默认校对集(通常是_ci)
注意:校对集如果是默认的,那么将不会特别显示出来,当前utf8字符集默认的校对集就是utf8_collate_ci
-
如果校对集在表中不进行设定,那么可以就会采用数据库默认的校对集来实现。数据库设定校对集可以在创建数据库的时候指定对应的库选项即可:create database 数据库名字 [charset 字符集设定] [collate 校对集设定]
create database test charset utf8 collate utf8_bin;
数据库创建后,在数据库对应的文件夹下有一个db.opt文件里,里面就有字符集和校对集
-
-
校对集的应用:校对集的使用就是通过特定的进行数据比较的指令来触发校对集规则
-
为了让数据比较指令能够有效执行,先往两张校对集表中插入对应的数据
表中的数据使用普通查询后就是按照具体插入顺序显示数据(此时不涉及校对集)
-
触发校对集规则的SQL指令:order by 字段 ASC|DESC;对某个字段进行升序(ASC默认)或者降序(DESC)实现;
-
修改校对集:alter table 数据库名 collate = 校对集名
注意:校对集必须在表中数据插入前设定好才有效,如果是先有数据,然后再修改表对应的校对集,那么数据也不能按照新的校对集规则来进行比较
-
八. MYSQL数据库字段
(一).MYSQL数据库字段类型
-
MYSQL字段(列)类型
MYSQL中根据业务的需求,将数据类型分成了四大类
- 整数类型:整数数据
- 小数类型:浮点型数据
- 时间日期型:时间日期型数据
- 字符串型:字符数据
-
整数类型
-
迷你整型:tinyint,只用一个字节存储,存储范围是-128~127
-
短整型:smallint,用两个字节存储
-
中整型:mediumint,使用三个字节存储
-
标准整型:int,使用四个字节存储
-
大整型:bigint,使用八个字节存储
-
MYSQL中默认都是有符号类型,即支持负数,如果某些 数据不需要负数,那么可以使用unsigned标志无符号类型
create table my_unsigned(id tinyint unsigned) charset utf8
注意:如果数据超出了范围,数据就会按上限或者下限来存储
-
-
显示宽度:对应整型数据类型再表中最多能够显示的宽度,通常显示的是最大数所占领的宽度,即几个符号能够表示出来
-
MYSQL中显示宽电压是根据整型所能表示的最大值的数字个数以及是否有符号
-
数字的显示宽度可以在实际创建表的时候来根据需求设定
-
显示宽度不会改变数据类型所能表示的大小:即数据长度超过显示宽度,数据依然有效
-
显示宽度不是用来限定数据,而是用来配合zerofill实现数据不足宽度时用前导0补充至指定宽度:
filed 字段类型(显示宽度)zerofill
如:
create table my_wide(
id1 tinyint(2) zerofill,
id2 tinyint(2) unsigned zerofill
)charset utf8;
注意:zerofill要求数据必须为unsigned,如果不是也会自动转化为unsigned;
-
-
小数类型
-
浮点型:即一种不能确保精确度的小数类型,在MYSQL中有float 和 double 两种
- float:单精度类型,4个字节存储数据,有效精度7-8位
- double:双精度类型,8个字节,有效精度15-16位
-
浮点型会虽然能表示很大的数据,但是由丢失精度的可能:超过精度的部分进行四舍五入
-
浮点型可以指定整数部分和小数部分的位数:
float/double(总长度,小数部分的长度)
注意:这个时候数据不能超过指定数据的大小,超过的部分会四舍五入(有效精度内),而整数部分则不允许超过(会直接报错)
注意:如果是因为仅为超过整数部分长度,系统默认允许
在存数据的时候也可以用科学计数法
-
定点型:decimal,是一种可以确保精确度不丢失的小数类型,本质是因为decimal不是使用固定的字节存储,而是根据数据的大小来自适应字节长度,大致是每九个数字使用四个字节存储
-
定点型数据也可以限制整数部分和小数部分的长度,但小数部分超过的会自动截取,截取方式是四舍五入,整数部分超过都会操作失败
注意:定点型即便是因为进位导致整数部分超过指定长度,也不允许数据插入
-
-
时间日期类型
-
年year:使用一个字节存储年份,可以使用year和year(4)来实现,表示的范围是1901-2155
year只能表示1901到2155年,而且MYSQL的year支持两种方式插入数据:2位年和4位年
注意:MYSQL中year字段类型使用2位数字插入时,在69以前是追加2000,70之后是追加1900
-
时间戳timestamp:从格林威治时间开始的时间
不同于PHP中的时间戳是秒数,MYSQL中的timestamp是
YYYY-MM-DD HH:II:SS年月日时分秒格式,数据插入可以使用
YYYY-MM-DD HH:II:SS或者YYYYMMDDHHIISS方式
注意:MYSQL中timestamp字段是有默认当前时间的,而且只要所在记录被修改,那么该字段就会自动更新(可以用作记录记录最后更新时间 )
-
时间段time:表示一个时间点或者一个时间段
create table my_time(
t1 time,
t2 time
)charset utf8;
MYSQL中的time类型数据,
可以直接插入时间格式(HH:II:SS),
也可以是非时间格式(日期HH:II:SS/HHH:II:SS),
而且还可以使用负号"-"(HHH表示的范围是-850~850)
insert into my_time values(‘12:12:12’, ‘123:12:12’), (‘4 12:12:12’, ‘-3 12:12:12’);
前面的4和-3表示天数,但是在实际存储的时候会自动全部转化为小时数
-
日期date:具体日期,格式为YYYY-MM-DD,表示范围为1000-01-01到9999-12-32
-
时间日期datetime:表示具体时间日期
-
-
字符串类型
-
定长字符串char:语法char(L),即实现确认数据的存储长度L字符数,然后存储的实际数据不能超过指定长度,L字符数不超过255
定长字符串应用场景是确定某项数据的长度都是某个固定的字符数,如身份证号码
-
变长字符串varchar:语法varchar(L),即实现确认数据可能出现的最大长度L字符数,存储的实际数据不能超多指定长度,L字符数理论值为65535
-
char与varchar的对比
-
都是指定长度存储字符串数据
-
char存储是数据长度一样的字段,varchar存储长度有差距的字段
-
char占据的实际存储空间是由L和字符集共同决定,
即L * 字符集对应字节数(数据不够时会使用空格填充,有空间浪费);varchar是根据实际存储的数据计算存储空间,即实际存储字符 * 字符集对应字节数 -
varchar因为根据数据长度来确定实际占用空间 ,因此需要一个额外的空间存储数据长度,规则是256个字符内需要一个字节,超过256个需要两个字节
-
GBK用2个字节存储一个汉字,UTF-8用3个
-
char的查询数据较高,但是varchar更灵活,节省空间
-
-
文本字符串test/blob:语法test/blob,是指存储较大的数据(通常是超过varchar的范围),其中test表示字符文本,而blob表示二进制文本。test和blob都有四种方式存储,而且都是自适应(不需要判定长度去选择哪种)
-
varchar、test和blob对比
- 三者的都是变长存储数据
- varchar和test是存储字符,而blob存储的是字节
- varchar存储受MYSQL记录长度限制(达不到理论值),test和blob不受限制
- varchar需要事先指定长度且数据不能超出长度,test和blob会自动根据数据调整长度
-
枚举enum:语法enum(数据1、数据2…数据N),使用1~2个字节存储,最多可以在enum中设定65535个数据,是一种提前规范可能出现的数据的字符串。允许设计人员将字段可能出现的数据罗列出来,然后在数据录入时只能选择列表数据中的某一个具体数据
create table my_enum(
gender enum(‘男’, ‘女’, ‘保密’)
)charset utf8;
枚举字段类似于单选框,设定的值在实际的选择时只能选择其中任意一个
insert into my_enum values(“女”);
注意:枚举是使用12个字节存储数据,但实际上可以看出数据本身是字符串,那么12个字节根本不够存储。enum的原理是建立枚举数据与数值之间的映射表。所以,在实际存储的时候,字段中本质存储的是映射数字,这也就能解释为什么能够使用2个字节达到65535个数据列表了。可以使用select 枚举字段 + 0 from 枚举字段表 来检测字段是否是数值(+运算符会自动类型转换)
select gender,gender + 0 from my_enum;
正是因为enum存储的本身实际是数据映射,因此在进行数据插入的时候,可以直接使用对应的映射关系的数值来进行数据插入
-
集合set:语法set(数据1、数据2…数据N),使用1~8个字节存储,最多可以在set中设置64个数据,也是一种 提前规范可能出现的数据的字符串。允许设计人员将字段可能出现的数据罗列出来,然后在数据录入时可以选择列表数据中的某一个或者多个具体数据
create table my_set(
ball_hobby set(‘篮球’, ‘足球’, ‘羽毛球’, ‘乒乓球’, ‘保龄球’)
)charset utf8;
集合字段类似于多选框,设定的值在实际选择的时候可以任选多个:使用一个字符串,中间用逗号分隔,中间不能加空格
insert into my_set values(‘篮球,足球’);
注意:集合set是使用1~8个字节存储数据,具体使用空间是系统自动通过计算set中元素的个数来设定,没满8个元素使用一个位存储。set中数据与字节的映射关系是:具体位上的数据对应一个字节中的比特位
所以在实际存储的时候,字段中存储的本质是选中的数据组成对应的字节数据转换的十进制数值。倒置后转化为十进制
数据插入的时候同样可以插入数值
-
(二).MYSQL字段属性
-
MYSQL字段属性
- NULL/NOT NULL:数据为空或者不能为空
- default:默认值设定
- primary key:主键唯一性
- auto_increment:自动增长
- unique key:数据唯一性
- comment:字段描述
-
NULL/NOT NULL属性
-
允许为空:默认情况下,不对字段类型进行任何属性控制的话,基本都是允许为空的
-
不允许为空:绝大部分情况下,数据为空没有意义,因此我们在进行字段设置的时候,都需要通过not null来限制数据不能为空
create table my_null(
id int not null,
name varchar(10) not null,
price decimal(8,2) not null,
deal_date date not null
)charset utf8;
-
NOT NULL不允许为空的字段在进行数据插入的时候,就不能插入NULL数据,或者在进行数据插入时跳过相关字段(不考虑设定默认值的情况)
-
-
Default属性
-
每个字段本身都有默认值,在不指定的情况下默认通常是NULL
虽然有的表设定所有字段不能为空,但其默认值依然是NULL
-
默认值的设定就是在某个可能出现某个固定数据的字段后使用 default来实现
create table my_default(
id int not null,
gender enum(‘男’, ‘女’, ‘保密’) default ‘保密’
)charset utf8;
-
一旦设置默认值之后,那么改字段进行数据插入的时候,就可以不用特别指定数据,系统会自动填充。但是当字段有数据插入的时候,默认值不再生效
-
数据在有了默认值的时候,有的时候一条SQL指令可能会因为享用某个默认值而不得不指定字段列表,这种操作将会非常麻烦而且容易出错,因此也可以在进行数据插入的值部分时,使用default关键字强制使用默认值
insert into my_default values(1, default);
-
-
primary key属性
-
主键可以通过在具体字段后使用primary key关键字实现
id char(18) primary key;
-
主键字段的数据不允许重复
-
主键字段的数据默认不允许为空
-
主键的创建也可以通过在所有字段后使用primary key(主键字段名)来设定
create table my_primary(
id char(18),
name varchar(10) not null,
age tinyint unsigned not null,
primary key(id)
)charset utf8;
-
主键可以是复合主键,即由多个字段共同组成一个主键,复合主键是通过在表所有字段后使用primary key(字段1、字段2…)组成,通常主键最多由两个字段组成
-
主键删除:如果一张表中已经设定主键,且不再需要主键了,那么可以通过修改表实现:alter table 表名称 drop primary key
-
追加主键:alter table 表名 add primary key (主键字段列表);(可以是复合主键)
注意:追加主键的字段要求对应字段的数据必须是唯一的且不能由NULL数据
-
逻辑主键:通常在进行表设计的时候,主键为了保障数据的唯一性,而且主键的更重要的使用方式是为了方便数据的高效查询。所以在创建主键的时候,通常不是针对具体的业务数据来设定主键,而是设定一个对应的无实际业务含义的字段(数字),这种主键称之为逻辑主键
-
主键不能修改,只能先删除后再追加
-
-
auto_increment属性
-
自增长必须搭配整数类型且对应字段必须存在索引(就是Key字段必须有值),所以通常自增长是配逻辑主键
create table my_auto(
id int primary key auto_increment,
name varchar(10) not null,
age tinyint unsigned not null
)charset utf8;
-
当字段有自增长属性后,可以进行数据插入的时候,使用null代替相应字段(或者不指定相应字段)
insert into my_auto values (null, ‘zhangsan’, 18), (null, ‘lisi’,20);
insert into my_auto (name, age) values (‘zhangsan’, 18), (‘lisi’,20);
-
自增长设定后,默认是从1开始自增的
-
一张表只能拥有一个自增长字段,自增长字段绑定后会出现再表选项中,可以通过show create table 表名 来查看
-
自增长也可以被手动数据填充:即插入数据明确指定自增长字段数据
注意:自增长是根据表中已有的最大值自动加1操作,因此当手动插入的数据偏大时,系统下次自动操作的值也是从目前的最大值+1
-
自增长可以通过后期表字段进行维护:增删改
-
alter table 表名 modify 字段名 字段类型 [字段属性] auto_increment #新增操作
-
alter table 表名 modify 字段名 字段类型 [字段属性]
#清除自增长
-
alter table 表名 auto_increment = 新值
#修改自增长下个值(只能大于当前最大的)
#可以通过这个来判断当前表的字段数量
-
-
MYSQL中自增长的控制:起始值和步长(每次变化多少),都是在MYSQL系统中通过变量控制的,可以通过show variables like 'auto_increment%'查看
注意:该值可以通过set变量名 = 新值 改变,知识通常不会去改
-
-
unique key属性:唯一键,保障数据的唯一性
-
在字段后使用unique key关键字描述字段
create table my_unique(
id int primary key auto_increment,
username varchar(20) unique [key],
email varchar(50) not null
)charset utf8;
-
unique唯一键的效果就是当数据插入重复的时候报错
-
unique唯一键不会像主键那样强势,默认允许字段为空,且NULL数据不存在重复问题
-
unique唯一键允许一张表中多个字段使用
-
unique也可以在所有字段后使用unique key(字段名列表)一样设置唯一键,多个字段也可以组合成复合唯一键
可以在设置唯一键的时候设置唯一键的名字:
unique key username_index (username)
unique key username_email (username, email)
-
唯一键删除:unique唯一键不像primary key那样可以直接删除,因为unique key一张表可以有多个,系统不知道删除谁。unique key在表中被当作一个普通索引,因此删除方式是:
alter table 表名 drop index 唯一键对应的索引名字(默认为数据名,也可能是设置的唯一键的名字)
-
唯一键不能修改,只能删除添加
-
唯一键增加:alter table 表名 add unique [key](字段列表)
-
-
comment属性
- 在字段后使用comment ‘描述说明’
- 字段说明不会在其他任何地方看到,只能通过创建语句来查看
九. MYSQL数据库高级操作
(一). 新增数据
-
蠕虫复制:从已有表中复制数据直接插入到另一张表,而且还可以快速将数据实现成倍增加
-
语法结构:insert into 表名 [(字段列表)] select * / [字段列表] from 已有数据表
需求:在my_database1中创建表my_clone_table,然后冲my_database数据库中的my_table表中取出数据存入my_clone_table
create database my_database1;
use my_database1;
create table my_clone_table like my_database.my_table;
insert into my_clone_table select * from my_database.my_table;
后面两句话可以直接用这一句代码实现:
create table my_clone_table select * from my_database.my_table;
-
蠕虫复制通常用在数据表的效率测试
-
-
replace替换:目标不存在就执行插入,目标存在执行替换
-
语法:replace into 表名 [(字段列表)] values [(值列表)]
-
原理
-
replace值列表中如果对应主键字段为NULL,系统执行插入操作
-
replace值列表中如果有主键字段值,系统就会先去匹配表中是否有该主键,如果匹配不到就执行插入操作,匹配到了就先产出原有数据,然后插入数据
-
replace每次需要检索数据,所以没有insert效率高
注意:replace在主键冲突的时候先删除数据然后再新增数据,所以replace值列表中的数据要与字段本身的需求相满足,必须给所有非空又没有默认值的字段提供数据
-
-
(二). 查询数据
-
查询选项:select 查询的时候,实际上是存储指令可以控制数据本身的效果的。即主要对于出现的重复数据进行管理
-
select选项使用指令:select select选项 */字段列表 from 表名;
-
all:默认的select选项值,表示保留所有查到的结果
-
distinct:去重的,表示重复的数据只显示一条记录
select all * from my_database1.my_clone_table;
select distinct * from my_database1.my_clone_table;
-
-
distinct去重针对的是所有查出来的字段数据(结果),是记录相同则去重,而不是某个字段重复。
select distinct * from student;
select distinct age from student;
此处由于只查了age,所以查重针对的是age
-
-
别名:即对某些较长名字或者冲突名字(重复)指定一个新名字,在最后查看数据的时候就可以通过新的名字来进行查看
-
别名分为两种:字段别名和表别名,使用方法基本一致:
字段名/表名 [as] 别名
select id as index_id, name '姓名', age '年龄' from my_auto as a;
-
别名只能在查询的时候使用
-
-
数据源:凡是跟在from关键字之后,用来提供数据的部分都称之为数据源。MYSQL中绝大部分的数据源都是一张实际表,但是根据实际数据的操作,也可以来源是多张表或者一个数据查询得到的表。
-
单表数据源:所有数据的需求就是在一张表
select * from my_year;
-
多表数据源:数据需要从多张表获取 ,这是可以直接在from后跟多张表,使用逗号分隔即可
select * from my_year, my_wide;
注意:此时的查询结果行和列的组成方式是这样的:行数 = 第一张表行数 * 第二张表行数;列数 = 第一张表列数 + 第二张表列数
多表数据源要考虑查询出来的字段名字重复问题(即多张表中有同名字段),这个时候可以使用字段别名和表别名,在选择字段输出的时候使用表名/表别名,字段名
select a.*, s.id s_id, s.sid s_sid, s.name s_name, s.sex s_sex from my_auto a, student s;
-
查询数据源:有的数据来源并非是一张表或者多张表,而是一条查询结果。MYSQL规定使用select查询的结果可以作为数据源,但是必须给查询的结果一个别名(假装表名)
select * from (select * from my_auto) as a;
注意:查询数据源不会像上述SQL指令这么简单,查询数据源的本质是子查询(后续会学)
-
-
where子句:条件判定,实现数据的筛选
-
语法:where 条件表达式
select * from student where id = 1;
-
where表达式分类:where后可以跟几类运算符表达式
-
比较运算符 :>、<、=、>=、<=、<> | !=(不等于)、in(字段在某些指定值内存在返回true)、
between and(判断字段区间)
select * from my_auto where age between 20 and 22;
select * from my_auto where id in (1, 2, 3);
-
逻辑运算符:
- &&/and:逻辑与
- ||/or:逻辑或
- !/not:逻辑非
-
空运算符
- is null
- is not null
-
匹配运算符
like:字段like ‘模式’,模式匹配成功返回true
select * from student where name like ‘Li%’;
-
-
-
group by子句:分组,根据某个具体的字段,相同的值分到一组
先创建一张表,并且插入数据
create table my_student( id int primary key auto_increment, name varchar(10) not null, gender enum('男','女','保密'), age tinyint unsigned not null, class_name varchar(10) not null comment '班级名称' )charset utf8; insert into my_student values (null, '鸣人','男',18,'木叶1班'), (null,'佐助','男',18,'木叶1班'), (null,'佐井','男',19,'木叶2班'), (null,'大蛇丸','男',28,'木叶0班'), (null,'卡卡西','男',29,'木叶0班'), (null,'小樱','女',18,'木叶1班'), (null,'雏田','女',18,'木叶1班'), (null,'我爱罗','男',19,'木叶1班'), (null,'博人','男',8,'木叶10班'), (null,'向日葵','女',6,'木叶10班'), (null,'鼬','男',28,'木叶0班');
-
group by语法:where 子句后 group by 字段名
-
group by分组原理
- 按照分组字段,将获取到的记录分成几块
- 保留每块的第一条记录
-
group by目的:实现分组统计,分组统计主要用到以下聚合函数
-
count(*/字段名):统计分组字段对应的记录数量
-
max(字段名):统计分组后某个字段的最大值
-
mix(字段名):统计分组后某个字段的最小值
-
avg(字段名):统计分组后某个字段的平均值
-
sum(字段名):统计分组后某个字段的和
-
group_concat(字段名):统计分组后,某个分组字段的字符串拼接
select max(age),min(age),avg(age),sum(age),group_concat(name) names,count(class_name) counts,class_name from my_student group by class_name;
-
-
注意事项:
-
MYSQL5.7.5版本后,凡是使用到group by分组统计后,select后的字段信息 必须只能是分组字段本身或者使用聚合函数统计;5.7.5以前允许查看其他非分组字段(每组第一条记录对应字段值)
-
count函数在进行字段统计时,不会统计字段值为NULL的字段
-
group by子句必须在where子句之后使用(可以没有where子句):where是从磁盘中读取数据,group by是针对已经进入内存的数据进行分组
-
如果想访问其他非分组统计字段,那么可以使用系统函数any_value(想要查看的字段)来实现(也可以修改MYSQL配置文件中的严格模式,网上有很多方案)
(我的系统好像没有这个函数= =)
-
-
多分组:group by可以针对多个字段进行分组,即根据某个字段分组后,再对分组进行其他字段分组。多分组语法为:
group by 字段1,字段2…字段N
select class_name, gender, count(*) from my_student group by class_name, gender;
-
分组排序:group by在进行字段分组的同时,也会对分组字段进行排序,默认排序为升序ASC,可以使用降序DESC,排序语法为group by 字段 [ASC|DESC];
-
回溯统计:回溯统计是指当数据被分组后,系统会在最后额外按照分组字段进行一次统计,此时分组字段会被置空。回溯通国际的语法为:group by 字段1,字段2…字段N with rollup;
select gender, count(*) from my_student group by gender with rollup
-
-
having子句:where是从磁盘中读取数据时进行 判定,而在数据进入到内存之后where就不能生效,having是完全针对进入内存后的数据进行判断
-
having语法:having几乎能做where所能做的所有事情:having 条件判断
因为having要把 数据全部加载到内存后再进行判定,所以它的效率要比where要低一些
-
having主要针对group by后的统计结果进行判定
select count(*) number, class_name,group_concat(name) from my_student group by class_name having number > 1;
//就是为了解决由于where要在group by前面使用而导致的不便
-
-
order by子句:通过对某个字段,使用表对应的校对集实现排序
-
语法:order by 字段 [ASC|DESC];其中ASC升序,DESC降序select * from my_student order by age;
-
中文排序:绝大部分字符集的校对集默认是ci(即不区分大小写),如果要对中文进行排序,那么必须指定排序方式(可以事先指定表的校对集),order by的中文方式:
order by convert(字段 using 字符集)[collate 校对集]
select * from my_student order by convert(name using gbk);
#此处默认ci
utf8是比不出中文的,gkb_chinese_ci可以按拼音来比较中文
-
多字段排序:即对某个字段排序后,再将排序字段内的其他字段进行排序,排序语法为:
order by 字段1[ASC|DESC],字段2[ASC|DESC];
-
-
limit子句:在获取记录的时候,可以按照设定的记录数来获取对应的数据
-
简易的limit:限制获取的记录数,语法:limit 具体数量
select * from my_student limit 5;
-
limit在设定的数量大于记录数量时,就是获取全部数据
-
综合版limit:限制获取记录数的同时,还可以设定获取数量的起始位置:limit 起始数量,获取数量
select * from my_student limit 2, 5;(从索引2开始,数5个)
-
limit分页功能实现:分页就是按照起始根据页码不断的计算起始位置,去获取不同的数据
页码:外部传入
数据长度:明确规范数据
起始位置:(页码 - 1)* 数据长度
分页SQL:limit 起始位置,数据长度;
-
-
查询总结
-
MYSQL的高级查询操作在实际开发中应用非常频繁
-
SQL查询的完整语句为:select 查询选项 字段列表 [别名]/聚合函数 from 数据源 where 子句 group by 子句 having 子句 order by子句 limit 子句
-
字段别名、聚合函数、where条件筛选、group by分组统计、
order by排序和limit数量控制都是非常常用的
-
(三). 更新数据
-
限制更新:在更新的时候,可以限制更新的记录数
-
更新时使用简易limit限制更新记录数:
update 表名 set 字段 = 新值 where 条件判定 limit 更新数量
-
(四). 限制删除
-
限制删除:设定了删除条件之后,可以控制满足条件的删除数量
-
删除的时候使用建议limit限制删除数量:
delete from 表名 where 条件 limit 限制数量
-
-
清除数据:清除数据与delete删除数据不同,清除数据是将表结构和数据删除,然后重建一张信标,因此可以实现表结构的auto_increment重置为初始值
- 语法:truncate 表名
十. 数据库设计规范
- 范式:是按照离散数学的数理逻辑规范数据表的设计,解决的问题是数据的存储优化,凡是通过关系能够查到的数据,坚决不重复存储,最终解决数据的冗余问题。设计范式的层级有6层,每一层都是递进关系,在数据库设计的时候借鉴了前三种:
- 第一范式(1NF)
- 第二范式(2NF)
- 第三范式(3NF)
- 表关系:是指在设计表与表之间关系来规范数据的时候应该遵循的方式,一共分为三种关系:
- 一对一
- 一对多
- 多对多
(一). 关系型数据库设计范式
-
第一范式:在设计表存储数据的时候,如果表中设计的字段存储的数据,在取出来使用之前还需要额外的处理(拆分)就不符合1NF,第一范式就是处理数据颗粒度大的问题
-
第二范式:在数据表设计的过程中,如果有复合主键(多字段主键),且表中有字段并不是由整个主键来确定,而是依赖主键中的某个字段(主键的部分):存在字段依赖主键的部分的问题,称之为部分依赖:第二范式就是要解决表设计不允许出现部分依赖
解决方案:就是让字段不会存储依赖部分主键的问题。因此需要做的就是增加一个逻辑主键字段。
虽然解决了依赖问题,但学生和课程又不具备唯一性了,所以应该增加复合唯一键:unique(学生,课程)
在实际开发中几乎不用复合主键,因此可以完美避免违背第二范式
-
第三范式:理论上讲,应该一张表中的所有字段都应该直接依赖主键(逻辑主键:代表的是业务主键),如果表设计中存在一个字段,并不直接依赖主键,而是通过某个非主键字段依赖,最终实现依赖主键:把这种不是直接依赖主键,而是依赖非主键字段的依赖关系称之为传递依赖。第三范式就是要解决传递依赖的问题
第三范式的解决方案:如果某个表中由字段依赖非主键字段 ,而被依赖字段依赖主键,我们就应该将这种非主键依赖关系进行分离,单独形成一张表
-
逆规范化:在考虑查询效率和数据冗余的时候,为了提升查询效率而选择牺牲磁盘空间,适当的增加数据冗余
(二).表关系
- 一对一关系
- 定义一张表,包含多字段,有常用字段和不常用字段
- 把常用字段放一张表,不常用字段放一张表,共用常用字段表的主键即可
- 两表就具有一对一的关系
- 一对多关系:意味着有一张表中,要保留与另外一张表中数据的关系。关系细节就是一张表中的一条记录对应另外一张表中的多条记录
- 多对多关系
十一. MYSQL数据库多表操作
(一).联合查询
准备工作:增加my_student1表(与my_student表一致),存入学生数据
insert into my_student1 values
(null, '张三','男',16,'现代1班'),
(null,'波兰','女',38,'现代2班'),
(null,'香蕉','男',49,'现代4班'),
(null,'苹果','男',37,'现代3班'),
(null,'西瓜','男',25,'现代1班'),
(null,'晕蛋','女',11,'现代6班'),
(null,'二流子','女',44,'现代5班'),
(null,'宝批龙','男',23,'现代4班'),
(null,'傻蛋','男',45,'现代5班'),
(null,'白痴','女',26,'现代3班'),
(null,'小黄','男',14,'现代2班');
-
联合基本查询语法
select 查询语句 union [union选项] select 查询语句
select * from my_student union select * from my_student1
-
union选项:union选项和select选项一样分两种:
- distinct:去重,默认
- all:全部(主要使用)
-
union联合查询特点:
-
union联合查询都是对结果的拼接,要切前后查询的字段数一致
-
union只是拼接结果,不考虑数据类型,因此union前后字段类型可以不同(即不限制数据类型)
select name, age, class_name from my_student union select class_name, name, age from my_student1;
-
-
union联合查询中使用where条件:union是针对结果合并,因此条件的限定属于union前后的select语句对应的条件限定
select * from my_student union select * from my_student1 where gender = '男'; #此时针对select * from my_student1进行条件限定 select * from my_student where gender = '男' union select * from my_student1 where gender = '男';
-
union不限定联合次数,可以多次union
select * from my_student union select * from my_student1 union select * from my_student1;
-
union中使用order by
-
select中使用order by:即对部分select查询结果进行排序
- order by的使用必须将对应的select语句使用括号包裹
- order by必须使用limit辅助才会生效
(select * from my_student order by gender limit 99999) union all (select * from my_student1 order by gender limit 99999)
-
union使用order by:对整个union得到的结果进行排序
select * from my_student union all select * from my_student1 order by gender;
注意:gender作为枚举字段,实际存储是数值,在select中使用order by的时候,gender排序是按enum列表顺序,而在union后使用order by,gender对应的值是实际中文,所以排序结果不一样
-
(二).连接查询
-
交叉连接:没有任何条件的连接
-
内连接:条件完全匹配的连接
-
外连接:主表匹配连接
-
自然连接:自动匹配条件的连接
create table my_class( id int primary key auto_increment, name varchar(10) not null unique, room varchar(10) )charset utf8; insert into my_class values(null, '木叶1班', '101'), (null, '木叶2班', '102'), (null, '木叶0班', '100'), (null, '木叶10班', '201'); create table my_student2( id int primary key auto_increment, name varchar(10) not null, gender enum('男', '女', '保密') default '保密', age tinyint unsigned not null, class_id int not null )charset utf8; insert into my_student2 values (null, '鸣人','男',18,1), (null,'佐助','男',18,1), (null,'佐井','男',19,2), (null,'大蛇丸','男',28,2), (null,'卡卡西','男',29,1), (null,'小樱','女',18,1), (null,'雏田','女',18,1), (null,'我爱罗','男',19,3), (null,'博人','男',8,3), (null,'向日葵','女',6,4), (null,'鼬','男',28,3);
-
交叉连接
-
隐示交叉连接:就是在数据源部分直接使用两张表
select * from my_student2, my_class;
-
显示交叉连接:早表查询的时候使用表 cross join 另一张表
select * from my_student2 cross join my_class
-
交叉连接的结果也称之为笛卡尔积,笛卡尔积是数学上的一种交叉结果
-
-
内连接:关键字inner join,表示的是一种需要两张表连接,而且连接关系是一张表中的数据都与另一张表中的数据一定能对应上的连接方式。连接成功的保留,连接失败的不保留
-
内连接基础语法:表1[inner] join 表2 on 条件匹配
insert into my_class values (null, '神妖1班', '1001'), (null, '法师1班', '2001'), (null, '宠物1班', '3001'); select * from my_class inner join my_student2 on my_student2.class_id = my_class.id; select * from my_class join my_student2 on my_student2.class_id = my_class.id;
-
表别名的使用:使用连接查询的时候多使用表别名
select c.* from my_class c inner join my_student2 s on s.class_id = c.id;
-
字段别名的使用:查询出来多表的数据可能出现字段名重复,那么可以使用别名来区分
select c.id, c.name class_name, s.id s_id, s.name student_name, s.gender, s.age from my_class c inner join my_student2 s on s.class_id = c.id;
-
内连接还可以使用where代替on关键字作为匹配条件
注意:虽然where可以代替on实现效果,但是on是在数据库拿数据时生效,而where是在数据拿出来的过程中生效,即on决定是否拿数据,where是拿来之后判定数据
-
内连接也可以不指定条件,最终结果与交叉连接一样,所以在使用内连接的时候不要忘记条件
-
-
外连接:是指以一张表为主表,主表的每一条记录去匹配另一张表(从表)的所有记录,匹配成功不成功都会保留,只要匹配成功就会有匹配的数据,没有成功只有主表的数据,从表的数据就会置空null
-
外连接分为两种:左外连接和右外连接
-
左外连接:left [outer] join,以左边的表为主表
-
右外连接:right [outer] join,以右边的表为主表
#左外连接 select * from my_class c left outer join my_student2 s on c.id = s.class_id; #右外连接 select * from my_class c right outer join my_student2 s on c.id = s.class_id;
-
-
外连接必须使用on关键字限定条件,不能使用where代替
-
如果要使用条件限定,可以采用on多条件,或者在on进行匹配后使用where条件
select * from my_class c left outer join my_student2 s on c.id = s.class_id and s.age > 20;
注意:on后使用and附加条件,理论上还是属于外连接部分,因此一定会保留主表数据(从表置空);如果使用where是针对外连接结果再进行筛选
-
全外连接:full outer join,即没有主表和从表的关系,匹配不上都会保留,只是如果对应表的数据不存在则置空,但是MYSQL不支持,可以使用左连接和有连接联合起来,效果相同
-
-
自然连接:自然连接是一种系统设定匹配条件的连接方式,匹配的原理是两张表的同名字段
-
自然连接不是特定的连接方式,而是自动匹配条件的连接方式,自然连接支持内连接和外连接
-
自然内连接:natural join
-
自然外连接:natural left/right join
select * from my_class natural join my_student2;
-
-
自然连接是自动匹配,匹配逻辑是所有同名字段,因此如果要使用自然连接,必须让表非常规范
注意:自然连接会将同名字段自动保留至一个
-
其实外连接和内连接也可以达到同样的自然连接效果(保留一个列),这个时候需要使用using关键字
select * from class inner join student using(c_id); select * from class left join student using(c_id); select * from class right join student using(c_id);
-
(三). 子查询
-
标量子查询:子查询的结果返回的只有一个数据
-
列子查询:子查询的结果返回是一列数据
-
行子查询:子查询的结果返回是一行数据
-
表子查询:子查询的结果返回是一张表
还可以将子查询出现的位置进行分类:
- where子查询
- from子查询(from后where前)
总结:就是以一个select的查询结果来帮助另一个查询实现
-
标量子查询
-
查询‘木叶1班’的所有学生
-
查询的内容是学生,所以外部查询源是my_student2
-
学生表中没有班级名称条件,只有班级ID,所以必须找出班级名称对应的ID
select * from my_student2 where class_id = ?; select id from my_class where name = '木叶1班'; #组合查询(注意打括号) select * from my_student2 where class_id = (select id from my_class where name = '木叶1班');
-
-
-
列子查询
-
查询有学生的班级
-
查询的内容是班级,所以外部查询数据源是my_class
-
查询的条件是学生表中已使用的班级ID
select * from my_class where id in(select class_id from my_student2);
-
-
在列子查询中,除了使用一般的运算符作为条件匹配以外,还有额外的匹配方式
-
any:与大于、小于等比较运算符共同使用,表示满足部分条件
-
some:与any一样
-
all:表示满足全部要求
#查询my_student1表中只要比my_student表中任意一个学生年龄大的学生 select * from my_student1 where age > any(select age from my_student); #查询my_student1表中比my_student表中全部学生年龄大的学生 select * from my_student1 where age > all(select age from my_student); #等价于 select * from my_student1 where age > (select max(age) from my_student);
-
-
-
行子查询
-
获取my_student2表中和my_student2表中鸣人性别年龄都相同的学生信息分析
-
数据获取目标是my_student
-
匹配的条件是两张表中的gender和age字段
#依据需求尝试设计 select * from my_student where gender = ? and age = ?; select gender, age from my_student2 where name = '鸣人'; #好像一个子查询无法满足,貌似要用两个子查询结果,再次尝试 select * from my_student where gender = (select gender from my_student2 where name = '鸣人')and age = (select age from my_student2 where name = '鸣人');
-
-
以上方式使用了两个子查询,即同一张表查询了两次:可以想办法将两个字段合并成一个字段,即构造一个行元素(拥有多个字段的一体元素),从而只需要查询一次
select * from my_student where (gender, age) = (select gender, age from my_student2 where name = '鸣人');
-
-
表子查询
-
使用分组统计尝试解决问题
-
获取字段数据的内容为max(age), class_name 和 name(非分组统计字段,使用any_value函数)
-
按照班级分组,class_name
-
按照查出来的年龄降序排序
select max(age), class_name, name from my_student1 group by class_name order by max(age) desc;
-
-
以上貌似解决了问题,但最大年龄对应的人是错误的
- group by在分组的时候取的name是第一条记录的值
- 对应最大年龄不是该分组的第一条记录
-
要解决以上问题其实也不难:先按照年龄降序排序,保证所有分组的第一条记录本身就是年龄最大的一条记录
-
但是此时矛盾会再次产生:order by字段是在group by字段之后执行,所以无法先order by再进行group by。因此就需要想办法让order by先执行。这个时候就可以使用子查询先order by数据,然后将order by后的数据当作数据源再次group by
#1.明确目标:要从已经排序好的表中进行group by操作 select max(age), class_name, name from ? group by class_name; #2.制作满足要求的order by数据源 select age, class_name, name from my_student1 order by age desc; #3.将查询语句组装成子查询语句 select max(age), class_name, name from (select age, class_name, name from my_student1 order by age desc) group by class_name; #错误:from后必须跟表名 #4.将查询结果制作成一张表:增加表名 select max(age), class_name, name from (select age, class_name, name from my_student1 order by age desc) s group by class_name; #5.版本5.7之后需要使用limit来使order by生效 select max(age), class_name, name from (select age, class_name, name from my_student1 order by age desc limit 99999) s group by class_name;
-
十二. MYSQL安全管理
(一).数据备份与还原
- 文件的备份与还原
- 整库的数据备份与还原
- 单表数据备份与还原
- 增量备份与还原
-
文件的备份与还原
- 文件备份:将数据库表对应文件复制到安全位置即可,但是InnoDB与MyIsam存储引擎有区别
- 文件还原:直接将备份文件放到对应数据库文件夹下即可
-
整库备份与还原
-
mysqldump.exe备份:mysqldump是一种客户端,因此也需要像mysql.exe一样先进行连接认证;只是mysqldump备份的指令是再认证之后的
mysqldump.exe (-h 主机地址 -P端口) -u用户名 -p密码 数据库名字 [表名列表] > SQL文件路径
-
mysqldump可以实现多种备份方式
-
单表备份:数据库名字 表名 > SQL文件路径
-
多表备份:数据库名字 表名1 表名2 … > SQL文件路径
-
整库备份:数据库名字 > SQL文件路径
mysqldump -uroot -proot test my_class > D:/a.sql mysqldump -uroot -proot test my_class my_student > D:/as.sql mysqldump -uroot -proot test > D:/ad.sql
-
-
还原:还原就是用备份的SQL指令执行一次,从而可以覆盖掉当前数据库对应的表和数据
-
mysql客户端还原,即登录时直接还原:
mysql.exe -hPup 数据库名字 < SQL文件路径
-
指令还原,即登录MYSQL之后还原:
source SQL文件路径
-
-
(二).外键约束
-
外键
-
创建表时增加外键:在所有字段后使用 constraint ’ 外键名字’ foreign key (外键字段) references 表名(主键字段)
create table my_foreign( id int primary key auto_increment, name varchar(10) not null, age tinyint unsigned not null, c_id int comment '指向my_class的主键', constraint `my_class_id` foreign key (c_id) references my_class(id) )charset utf8;
-
外键要求:字段本身必须是一个普通索引,如果不是,外键会自动加上,因此对应的外键字段的key为mul
-
删除外键:如果一张表明确不需要使用对应的外键,一一使用修改表结构删除:alter table 表名 drop foreign key `外键名字``;
注意:删除外键对应的表字段索引依然是存在的
-
追加外键:即在创建表后增加外键字段:alter table 表名 add constraint `外键名字`` foreign key (外键字段) references 外键表(主键)
-
-
外键约束
-
外键增加之后默认子表插入的数据对应的外键字段必须在父表存在,否则会报错
insert into my_foreign values(null, '犬夜叉', 200, 5); #外键值5在主表中存在,插入成功 insert into my_foreign values(null, '神武', 3, 100); #外键值不存在,报错
-
外键增加后默认父表主键如果有外键值在使用,那么不能更新值,也不能删除主键所在记录
-
因此,外键的作用就明确了
- 限定子表(外键所在表)不能插入主表中不存在的外键值(不能更新)
- 限定父表(主键被引用)不能删除或者更新子表有外键引用的主键信息
-
可以在创建外键之后指定外键的约束效果:即控制父表的操作对子表的影响
-
控制情况
- on update:父表更新与子表有关联的主键时
- on delete:父表删除与子表有关联的主键时
-
控制效果
- cascade:级联操作,即父表怎么样,子表相有对应的记录就怎么样
- set null:置空操作,即父表变化,子表关联的记录对应的外键字段置空(注意:能够使用的前提是外键对应的字段允许为空)
- restrict/no action:严格模式(默认),即不允许父表操作
-
通常搭配情况
-
on update cascade:父表更新,子表级联更新
-
on delete set null:父表删除,子表对应外键字段置空
constraint `my_class_id` foreign key (c_id) references my_class(id) on update cascade on delete set null
注意:表中外键名字是整个数据库唯一的,不能重复,因此可以在创建表的时候自己不指定,即直接使用foreign key及以后的内容
-
-
外键只有innoDB支持,MyIsam不支持
-
-
(三). 事务安全
-
默认的MYSQL是关闭事务的,即用户一提交操作,系统就自动将数据写到数据表
-
如果想要开启事务可以使用两种方式
-
自动开启:修改系统事务控制变量autocommit
set commit = ?;
注意:当前的修改只是当前的客户端这次连接有效,关闭退出就无效了
-
手动事务:使用事务命令来控制要执行的事务操作
-
开启事务:start transaction
-
执行事务:各类SQL写操作
-
关闭事务
- commit:操作成功全部提交
- rollback:操作失败全部回退
-
事务回退:指事务操作过程中,某个节点成功了,但后续未必会成功,那么可以设置节点,以后返回该位置
-
设置节点:savepoint 名字
-
回退到节点:rollback to 节点名字
savepoint sp1; rollback to sp1;
-
-
-
(四). 视图
-
视图
-
定义视图:create view 视图名字 as select 查询语句
create view student_class as select s.*, c.name class_name, c.room class_room from my_student2 s left join my_class c on s.class_id = c.id;
-
视图创建成功后可以使用表查看的所有方式进行查看
show tables; desc student_class; show create table/view student_class;
-
修改视图:即修改视图的创建语句,修改有两种方式
-
修改结构:alter view修改
-
修改替换:create or replace view
alter view 视图名字 as 新select语句 #创建替换,没有就新增,有就替换 create or replace view student_view as select * from my_student2;
-
-
删除视图:drop view视图名字
-
视图使用:视图有select语句组成结果,是提供数据的,可以像查询表一样查询数据
-
-
视图数据操作:其实是通过视图对基表进行操作,所以只能对单基表进行操作,不能对多基表进行操作。视图本是用来查看数据的,一般不修改其数据
-
视图算法:优化视图的运算逻辑
(五). 用户管理权限
-
root用户是超级管理员账户,拥有所有权限,也最不安全:用户在MYSQL中都包含两个部分,即用户名和主机地址(host):可以通过
show grants for root@localhost来查看root用户权限
所有权限都在MYSQL数据库下的user表中
-
一般我们项目中都不大会直接都使用root用户访问操作数据库(生产环境),这个时候会创建不同的账号给不同的团队,创建账号的语法:
create user 用户名@主机地址 identified by password ‘明文密码’;
#允许任何人访问的user1账户 create user 'user1'@'%' identified by'123456'; #仅允许本机访问 create user 'user2'@'localhost' identified by'123456'
-
赋予权限:默认的用户没有任何权限
赋予权限的语法:grant 权限列表 on 数据库.表名 to user
#给他查看视图的权限 grant select on test.student_view to 'user1'@'%'; #给他全部权限 grant all privileges on *.* to 'user2'@'localhost';
-
取消权限:revoke 权限列表 on 数据库.表名 from user;
-
如果用户密码忘记了,可以帮助修改用户密码:
set password for ‘用户名’@‘主机地址’ = ‘新密码’;
-
回收账户即删除账户:drop user 用户名;
十三. PHP操作MYSQL数据库
-
常用的MYSQL方法有以下几个
- mysqli_connect:连接认证
- mysqli_connect_error:连接失败的错误信息
- mysqli_close:关闭连接
- mysqli_errno:出现错误的编号
- mysqli_error:出现错误的信息
- mysqli_query:执行查询SQL指令
- mysqli_affected_rows:当前MYSQL操作受影响的行数
- mysqli_num_rows:当前查询结果集中的行数
- mysqli_free_result:释放查询的结果集
- mysqli_fetch_row:从查询结果集中取出一条记录,返回一维数组(索引数组)
- mysqli_fetch_assoc:从查询结果集中取出一条记录,返回一维数组(关联数组:字段名作为下标)
- mysqli_fetch_all:从查询结果集中取出所有记录,返回二维数组
-
PHP操作数据库
-
连接认证、设定字符集和选择数据库
<?php //使用MYSQLi操作数据库 $conn = @mysqli_connect("localhost", "root", "root"); //设置字符集 $sql = "set names gbk"; $res = mysqli_query($conn, $sql); //选择数据库 $sql = "use test"; $res = mysqli_query($conn, $sql);
-
新增数据
<?php //连接认证 $sql = "insert into my_student1 values (null, '黑崎一护', '男', 200, 5)"; $res = mysqli_query($conn, $sql); //查看受影响的行数 echo mysqli_affected_rows($conn);
-
修改数据
<?php //连接认证 $sql = "update my_student1 set age = 22 where id = 1"; $res = mysqli_query($conn, $sql);
-
删除数据
$sql = "delete from my_student2 where id = 11"; $res = mysqli_query($conn, $sql);
-
查询数据
$sql = "select * from my_student2"; $res = mysqli_query($conn, $sql);
-
十四. HTTP协议
(一). HTTP协议了解
- HTTP协议的作用
- HTTP协议的特点
(二). HTTP请求
- HTTP请求分为四个部分
- 请求行:请求方式 资源路径(URI) 协议版本
- 请求头:各种协议信息
- host:主机名字
- Accept:告知服务器能够接收的数据方式
- Accept-language:能够接收的语言
- User-Agent:游览器内核信息
- cookie:游览器存储的该网站的cookie数据
- 空行:用来分隔请求头和请求体
- 请求体:POST方式提交的数据,数据格式是表单名字 = 值&多个数据
(三). HTTP响应
-
HTTP也分为四个部分
- 响应行:协议版本 状态码 状态提示 如HTTP/1.1 200 ok
- 响应头:具体的协议项,常见响应体如下
- content-type:内容类型
- Date:服务器时间
- server:服务器类型
- set-cookie:服务器想保留在浏览器上的cookie数据
- content-length:数据长度(字节)
- cache-control:缓存控制
- 空行:分割
- 响应体:服务器给游览器的数据
-
状态码是浏览器解析服务器对本次请求的服务状态
- 200:表示服务器响应完毕
- 403:本次请求没有权限
- 404:本次请求的内容找不到
- 500:服务器出现了问题
-
PHP可以通过header设置一些响应规则
-
header(location:url):让浏览器重回定向请求,立即发起(浏览器不会解析剩下的响应头和响应体)
-
header(refresh:时间;url):让浏览器指定时间后重定向,浏览器会继续执行后面所有内容(时间内)
-
header(content-type):让浏览器按照明确的格式解析内容
-
header(content-disposition):让浏览器如何处理内容(选择应用程序)
<?php #立即重定向 header('Location:http://www.baidu.com'); #延时重定向(提示跳转) header('Refresh:3;url = http://www.baidu.com'); echo '三秒后自动跳 转到百度...'; #解析图片:png图片 header('content-type:image/png'); //告诉游览器读到的内容当作图片处理 echo file_get_contents('databases.PNG'); #文件下载 header('content-type:application/octet-stream'); #告知浏览器当前内容为八进制 header('content-disposition:attachment;filename = "美女.png"'); echo file_get_contents('databases.PNG');
-
十五. 会话技术
(一). cookie技术
-
cookie技术认识:cookie是绘画技术的一种,是利用HTTP协议中增加一组协议,来实现互相传递数据,从而让服务器额能够识别游览器
- 浏览器第一次访问服务器的时候,是什么都没有的。因此cookie是由服务器来发起的。即服务器在浏览器访问后响应的时候,在响应头总增加一个协议:set-cookie让服务器把数据传递给浏览器
- 浏览器在第一次拿到服务器响应的时候,会解析到cookie协议:然后将数据保存到浏览器中
- 浏览器在以后访问的时候,会先寻找浏览器缓存里是不是有对应网站的cookie数据,如果有就使用请求头中的cookie选项把数据携带过去
- 服务器在接受浏览器请求的时候,可以通过请求头中的cookie获取数据,从而判定浏览器有没有访问过,进而达到识别浏览器的特点
-
cookie技术实现:cookie技术实现是PHP利用了HTTP协议规范,在PHP输出数据之前设定相关协议实现的
-
PHP设定cookie:PHP提供了一个函数setcookie(‘名字’, ‘值’)
<?php #设置cookie setcookie('first', 'hello');
-
浏览器保存cookie
浏览器请求该PHP文件时HTTP响应
第一次请求时,没有cookie,第一次响应时,响应头里有cookie
可以在开发者工具中查看网站cookie
-
PHP读取cookie:PHP已经设定了系统变量$_COOKIE,直接在里面即可
<?php #读取cookie var_dump($_COOKIE);
-
cookie复杂数据存储:cookie只能(建议)存储简单数据,但是如果想要存储多一些或者数组的数据的话,可以使用[]
<?php #设置cookie setcookie('user[name]', 'Jim'); setcookie('user[age]', '30');
-
-
cookie高级应用:控制cookie是生命周期和访问有效性
-
设定cookie生命周期:cookie的生命周期默认是会话结束,但是可以通过setcookie第三个参数设定时间
<?php #设置cookie生命周期 setcookie('second', 'world'); setcookie('name', 'Tom', 0);#0表示会话结束(浏览器关闭) setcookie('age', 30, time());#表示设定time()对应的时间戳秒数 #cookie的生命周期设定是从格林威治时间到现在的描述限定的,如果要设定长周期就需要大过当前时间戳算 setcookie('long', 'long', time()+7*24*3600) #7天过期
有了生命周期之后我们就可以删除cookie了:就是让cookie的生命周期提前到期(会话结束前)
<?php #设置cookie生命周期 setcookie('die', 'die', 1); setcookie('heaven', 'heaven', time()-1);
-
cookie有路径访问问题:cookie特性是指定目录下的统计目录或者子目录可以访问,而上级目录则不能访问。通常一个网站的cookie是整个网站都可以访问,所以要设定cookie的访问路径为网站根目录"/"
<?php #设置cookie访问路径 setcookie('anywhere', 'anywhere', 0, '/') #当前网站整站可访问
-
cookie跨域问题:cookie默认只能当前网站访问(主机名限定),如果不想限定cookie的访问网站的话,可以通过setcookie的第五个参数限定
<?php #设置cookie访问限定 setcookie('hole', 'hole', 0, '/', '.com'); //所有.com都可以访问 setcookie('home', 'home', 0, '/', 'taobao.com');//淘宝下所有网站都可以访问
-
总结
- 第三个参数设置生命周期,默认是会话结束
- 第四个参数设置访问路径,默认是同级目录及其子目录可以访问
- 第五个参数设置访问网站,默认是当前网站
-
(二). session技术
-
session技术认识:是一种存储在服务器端,实现数据跨脚本共享的会话技术,session技术的实现是依赖cookie技术的
- 浏览器第一次访问服务器时,服务器会设定一个cookie给浏览器,同时服务器会自己创建一个文件把核心数据保存在文件中
- 浏览器依然还是保存cookie数据,但是以后传递给服务器时,服务器会根据cookie数据来打开对应的服务器核心文件,从而取出重要内容
-
session技术实现
-
session的使用必须开启session:利用PHP函数session_start(),session_start()完成了几件事情
- sessionID获取,sessionID就是session文件名字的核心部分
- 读取cookie:固定cookie名字,PHPSESSID,读取到了就直接使用
- 自动生成:没有读到cookie,就自动生成一个sessionID,同时设置cookie,把该ID拼凑到响应头中
- 寻找sessionID对应的session文件
- 没有:创建对应文件
- 有:打开文件,读取里面的内容
- 初始化$_SESSION变量
- 没有数据:数组为空
- 有数据:存放到$_SESSION中
- sessionID获取,sessionID就是session文件名字的核心部分
-
session操作继续,不管 S E S S I O N 中 是 否 已 经 有 数 据 了 , 我 们 都 可 以 往 _SESSION中是否已经有数据了,我们都可以往 SESSION中是否已经有数据了,我们都可以往__SESSION中读写数据
<?php #启用session session_start(); #访问session数据 var_dump($_SESSION); #写入session数据 $_SESSION['userinfo'] = array( 'username' => 'Tom', 'password' => '123456', 'age' => '18' );
-
session_start开启后,一直在等待:等待脚本执行结束,session系统自动将$_SESSION中的数据取出来,然后写入到session文件
-
销毁session:即删除当前session文件,同时关闭session系统。session文件默认的是不会自动删除的,除非程序主动删除:可以使用session_desroy()函数来删除session文件
-
-
session配置
- session在php.ini中有很多配置项来控制session的效果
- session.name,session存储cookie的名字,默认是PHPSESSID
- session.auto_start,session是否自动开启,默认是关闭
- session.cookie_lifetime,生命周期,默认会话结束
- session.cookie_path,访问限定,默认是整站访问/
- session.cookie_domain,跨域设置,默认不允许
- session.gc_maxlifetime = 1440,session文件的生命周期,默认是24分钟
- session.gc_probability = 1,session垃圾回收机制的概率分子
- session.gc_divisor = 1000,垃圾回收机制的概率分母
- 垃圾回收:指session在开启session之后,有一点几率触发系统回收机制,然后会一次性清理所有过期的session文件
- session在php.ini中有很多配置项来控制session的效果