官网地址:https://www.mysql.com/
MySQL安装
Ubuntu安装MySQL服务
终端执行: sudo apt install mysql-server
配置文件:/etc/mysql
数据库存储目录 :/var/lib/mysql
Windows安装MySQL
下载MySQL安装包(windows) https://dev.mysql.com/downloads/windows/installer/8.0.html
直接运行安装文件安装
启动和连接MySQL服务
服务端启动
查看MySQL状态 : sudo service mysql status
启动/停止/重启服务:sudo service mysql start/stop/restart#(开启、关闭、重启)
连接数据库
mysql -h 主机地址 -u 用户名 -p
注意:
回车后输入数据库密码 (我们设置的是123456)
如果链接自己主机数据库可省略 -h 选项
关闭连接
exit
快捷键:ctrl+D
SQL语言
基础数据类型
数字类型:
整数类型:int,smallint,tinyint,mediumint,bigint
浮点类型:float,double,decimal
比特值类型:bit
类型 | 大小 | 范围(无符号) | 范围(有符号) | 用途 |
---|---|---|---|---|
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 | 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 372036 854 775 807) | (0,18 446 744 073 709 551 615) | 极大整数值 |
float | 4字节 | (-3.402 823 466 E+38,-1.175 494 351E-38),0,(1.175 494 351 E-38,3.402823 466 351 E+38) | 0,(1.175 494 351 E-38,3.402 823 466E+38) | 单精度浮点数值 |
double | 8字节 | (-1.797 693 134 862 315 7E+308,-2.225 073 858 507 201 4 E-308),0.(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) | 0,(2.225 073 858 507 201 4 E-3081.797 693 134 862 315 7 E+308) | 双精度浮点数值 |
decimal | 对decimal(m,d) | 依赖于m和d的值 | 依赖于m和d的值 | 小数值 |
注意:
对于准确性要求比较高的东西,比如money,用decimal类型减少存储误差。声明语法是decimal(m,d)。M是数字的最大数字位数,D是小数点右侧数字的位数。比如 decimal(6,2)最多存6位数字,小数点后占2位,取值范围-9999.99到9999.99。
比特值类型指0,1值表达2种情况,如真,假
字符串类型:
普通字符串:char,varchar
存储文本:text
存储二进制数据: blob
存储选项型数据:enum,set
类型 | 大小 | 用途 |
---|---|---|
char | 0-255字节 | 定长字符串 |
varchar | 0-65535 字节 | 变长字符串 |
tinyblob | 0-255字节 | 不超过 255 个字符的二进制字符串 |
tinytext | 0-255字节 | 短文本字符串 |
blob | 0-65 535字节 | 二进制形式的长文本数据 |
text | 0-65 535字节 | 长文本数据 |
mediumblob | 0-16 777 215字节 | 二进制形式的中等长度文本数据 |
mediumtext | 0-16 777 215字节 | 中等长度文本数据 |
longblob | 0-4 294 967 295字节 | 二进制形式的极大文本数据 |
longtext | 0-4 294 967 295字节 | 极大文本数据 |
注意:
char:定长,即指定存储字节数后,无论实际存储了多少字节数据,最终都占指定的字节大小。默认只能存1字节数据。存取效率高。
varchar:不定长,效率偏低 ,但是节省空间,实际占用空间根据实际存储数据大小而定。必须要指定存储大小 varchar(50)
enum用来存储给出的多个值中的一个值,即单选,enum(‘A’,‘B’,‘C’)
set用来存储给出的多个值中一个或多个值,即多选,set(‘A’,‘B’,‘C’),插入值的写法是:‘多选值1 ,多选值2’
DDL建库建表,看库看表的结构
对库的操作
SQL语言特点
SQL语言基本上独立于数据库本身
各种不同的数据库对SQL语言的支持与标准存在着细微的不同
每条命令以 ; 结尾
SQL命令(除了数据库名和表名)关键字和字符串可以不区分字母大小写
1,查看已有库
show databases;
2,创建库
create database 库名 [character set utf8];
e.g. 创建stu数据库,编码为utf8
create database stu character set utf8;
create database stu charset=utf8;#这个是老写法,也能用
3,切换库
use 库名;
4,查看当前所在库
select database();
5,删除库
drop database 库名;
对表的操作
1,创建表
create table 表名(字段名 数据类型 约束,字段名 数据类型 约束,...字段名 数据类型 约束);
字段约束(约束可以写多个)
unsigned,设置数字为无符号则加上(比如不能为负数)
not null,字段不能为空。
default,设置一个默认值。(比如default 7)
comment,增加字段说明。(比如comment '说明')
primary key,关键字用于定义列为主键。主键的值不能重复,且不能为空。
auto_increment,一般用于主键,数值会自动加1。(比如:字段n 类型 primary key auto_increment)
2,查看数据表
show tables;
3,删除表
drop table 表名;
4,查看表结构
desc 表名;
5,查看数据表创建信息
show create table 表名;
#我理解就是看 当初建表的命令
DML表的操作
1,插入(insert)
insert into 表名 values(值1,值2...),(值1,值2...),...;
insert into 表名 (字段1,字段2,...) values (值1,值2...),(值1,值2...)...;
2,更新表记录(update)
update 表名 set 字段1=新值1,字段2=新值2,... where 条件;
注意:update语句后如果不加where条件,所有记录全部更新
3,删除表记录(delete)
delete from 表名 where 条件;
注意:delete语句后如果不加where条件,所有记录全部清空
4,表字段的操作(alter)
语法 :alter table 表名 执行动作;
* 添加字段(add)
alter table 表名 add 字段名n 数据类型;(在字段后面添加字段n)
alter table 表名 add 字段名n 数据类型 first;(在字段前面添加字段n)
alter table 表名 add 字段名 数据类型 after 字段n;(在字段n的后一列插入字段)
* 删除字段(drop)
alter table 表名 drop 字段名;
* 修改数据类型(modify)
alter table 表名 modify 字段名 新数据类型;
* 修改字段名(change)
alter table 表名 change 旧字段名 新字段名 新数据类型;
DQL查询
1,查询(select)
select * from 表名 [where 条件];
select 字段1,字段2 from 表名 [where 条件];
where子句
where子句在sql语句中扮演了重要角色,主要通过一定的运算条件进行数据的筛选,在查询,删除,修改中都有使用
聚合函数
select 字段名只能用
使用聚合函数的字段。如果要加其他的字段,除非这个字段的所有值都是一样的。(比如:整个字段的值都是男或女或国籍等等)
函数 | 说明与实例 |
---|---|
avg | 平均值,select avg(字段) from 表; |
max | 最大值,select max(字段) from 表; |
min | 最小值,select min(字段) from 表; |
sum | 求和,select sum(字段) as a from 表; |
count | 统计,select count(*) from 表 where 字段>7; |
我:count(字段名) 不会统计值为null的值。(只会统计有实际值的数量)
建议两个方法统计: count(主键字段或某个不为空字段) 或者 count(*)
高级查询
1,排序
order by 子句来设定你想按哪个字段哪种方式来进行排序,再返回搜索结果。加不加where都可以,看查询条件
select 字段名 from 表 order by 字段n;(按字段n进行排序,默认从小到大)
select 字段名 from 表 order by 字段n desc; (加上desc是降序)
select 字段名 from 表 where条件 order by 字段n;(根据条件排序)
复合排序:对多个字段排序,就是当字段n排序时数字一样,那么按照字段n+1数据排序
select 字段名 from 表 order by 字段n,字段n+1 desc;也可以不加desc,字段n相等,按照n+1字段排序
2,模糊查询
like用于在where子句中进行模糊查询
模糊匹配,% 代表匹配任意字符=,_(一个下划线)代表1个字符,匹配任意
select 字段名 from 表 where 字段名 like 张% (查询张...的数据)
select 字段名 from 表 where 字段名 like %张% (查询...张...的数据)
select 字段名 from 表 where 字段名 like ___ (查询3个字符的数据,我这里写的是3个下划线,显示的不清楚)
3,as 用法
在sql语句中as用于给字段或者表重命名。其实就是起个别名
4,限制
limit 用于限制select 返回的数据数量(永远放在最后)
如果limit3,5则表示,跳过前3行(从第3行的下一行开始),显示五行内容
select 字段名 from 表 limit 2; (只查看两条记录)
select 字段名 from 表 where条件 limit2; (根据筛选后的结果只查看两条记录)
我:也可以用在update和delete语句的操作数量(永远放在最后)
5,联合查询(也是上下连接表)
union 操作符用于连接两个以上的 select 语句的结果组合到一个结果集合中。多个 select 语句会删除重复的数据。
selet 字段a,字段b from 表A union select 字段c,字段d from 表A; (查询的4个字段abcd都会出来,只不过查询出来的字段名显示的是字段a,字段b。因为是上下连接表,所以只能有两个字段,希望后续自己能看懂)
select 字段1,字段2,字段3 from 表A where条件 union select 字段6,字段5,字段9 from 表A where条件; (这个例子是同一个表的联合查询,也可以不加where条件)
select 字段1,字段2,字段3 from 表A where条件 union select 字段a,字段b,字段c from 表B; (也可以用于不同的表一起查询,但是查询的字段数量要保持一致。)
我:如果要排序,只能在最后增加一个order by,这是对联合查询出来的结果进行排序。不要理解错了,不是单独对一个表排序后再联合查询的。
6,子查询(其实就是嵌套)
定义 : 当一个语句中包含另一个select 查询语句,则称之为有子查询的语句
select * from (select * from 表 where条件) as s where s.字段=值; #注意:放在from后面必须要起个别名。
select * from 表 where 字段= (select * from 表 where条件); #放在where中,等于是提供了一个值,不用起别名,放在这里必须是个明确的值。我:也可以用在增删改上
select * from 表 where 字段 in (select * from 表); #其实吧也可以用于多个值,但是多个值 必须用于集合
777777777777777777,多表关联:
① select 字段名 from 表1,表2 where 表1.字段=表2.字段;(多表关联,表1的字段与表2的字段必须是相同字段)
② select 字段名 from 表1 left join 表2 on 表1.字段=表2.字段;(左连接,以左表为主,左表有的内容全部显示出来)
③ select 字段名 from 表1 right join 表2 on 表1.字段=表2.字段;(右连接,以右表为主,右表有的内容全部显示出来)
④ select 字段名 from 表1 inner join 表2 on 表1.字段=表2.字段;(内连接,左右两表相同的部分显示出来)
备注:union 表的字段数量一样,可以相互关联,是上下连接。
查询程序的执行顺序
(1)from <left_table> <join_type> join <right_table> on <on_predicate> #先执行:from 表
(2)where <where_predicate> #再执行:where条件
(3)group by <group_by_specification> #再执行:分组
(4)having <having_predicate> #再执行:having
(5)select distinct <select_list> #再执行:select 字段
(6)order by <order_by_list> #再执行:排序
(7)limit <limit_number> #再执行:限制看几行,怎么看
聚合查询,聚合操作
1,聚合分组
group by 给查询的结果进行分组。加不加where都可,加就是按照条件分组,
select 字段n,avg(字段n+1) from 表 group by 字段n; #我写select avg(字段n+1)是为了说,select查询不能写其他普通字段,只能写对应的分组字段,或者写所有值都是一样的字段,或者是 加跟聚合函数的字段
select 字段n,字段n+1 from 表 group by 字段n,字段n+1;#多字段段统计
2,聚合筛选
having 对分组后的结果进一步筛选,having语句必须与group by联合使用.
having语句解决了where不能与聚合函数联合使用的缺点
select 字段n from 表 group by 字段n having条件;
select 字段n,avg(字段n+1) from 表 group by 字段n having avg(字段n+1)>7;#半个实例
3,去重语句
distinct
select distinct 字段n from 表; #不显字段n重复值
上一行解释:如果这个字段n的值有 5个男 7个女,那么去重之后只会显示两个,就是男和女各一个
算数运算符
比较运算符
符号 | 描述 | 备注 |
---|---|---|
= | 等于 | |
!= | 不等于 | |
> | 大于 | |
< | 小于 | |
<= | 小于等于 | |
>= | 大于等于 | |
between | 在两值之间 | 比如:between 80 and 90 (包含80和90) |
not between | 不在两值之间 | |
in | 在集合中 | |
not in | 不在集合中 | |
is null | 为空 | |
is not null | 不is not null |
between and,匹配一个区间,select * from student where number between 20190304 and 20190306;
匹配number在20190304到20190306之间的所有内容
Limit,查看前3行内容,select * from 表名 limit 3;
查看前3行内容(如果limit 3,5则表示,从第3行的下一行开始,显示五行内容)
逻辑运算符
优先级 | |
---|---|
1 | 算术运算符 |
2 | 链接符 |
3 | 比较符 |
4 | is [not] null,like,[inot] in |
5 | [inot] between |
6 | not |
7 | and |
8 | or |
时间类型数据
- 日期 : date
- 日期时间: datetime,timestamp
- 时间: time
- 年份 :year
类型 | 范围(字节) | 范围 | 格式 | 用途 |
---|---|---|---|---|
date | 3 | 1000-01-01/9999-12-31 | YYYY-MM-DD | 日期值 |
time | 3 | ‘838 59 597838 59 59’ | HH:MM:SS | 时间值或持续时间 |
year | 1 | 1901/2155 | YYYY | 年份值 |
datetime | 8 | 1000-01-01 00:00:00/9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
timestamp | 4 | 1970-01-01 00:00:00/2038,结束时间是第 2147483647 秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07 | YYYYMMDD HHMMSS | 混合日期和时间值,时间戳 |
#时间格式
date :"YYYY-MM-DD"
time :"HH:MM:SS"
datetime :"YYYY-MM-DD HH:MM:SS"
timestamp :"YYYY-MM-DD HH:MM:SS"
- 日期时间函数
now() 返回服务器当前日期时间,格式对应datetime类型
我:也可以设置表的字段的默认值,那么每次创建数据如果不填写这条数据的话,就会自动生成当前时间
select now();查询当前时间
- 时间操作
- 时间类型可以比较和排序等操作,在写时间字符串时尽量按照标准格式书写。
select * from 表 where date类型字段>='2000-01-01';
select * from 表 where date类型字段>="2000-07-01" and time类型字段<="2:30:00";
索引操作
可快速访问数据库表中的特定信息。
优缺点
- 优点 : 查找很快,提高查找效率
- 缺点 :占用数据库物理存储空间,当对表中数据更新时,索引需要动态维护,降低写入效率
注意 :
一般只在经常进行查询的字段上创建索引
对于数据量很少的表或者经常进行写操作而不是查询操作的表不适合创建索引
索引分类
- 普通(MUL)
普通索引 :字段值无约束,key标志为 mul - 唯一索引(UNI)
唯一索引(unique) :字段值不允许重复,但可为 null,key标志为 uni - 主键索引(PRI)
一个表中只能有一个主键字段, 主键字段不允许重复,且不能为NULL,KEY标志为PRI。通常设置记录编号字段id,能唯一锁定一条记录
索引创建
create table 表名(
字段名 数据类型,
字段名 数据类型,
index 索引名(字段名),
index 索引名(字段名), #这是普通索引
unique 索引名(字段名));#这是唯一索引
create table 表(id int id int primary key auto_increment,字段n varchar(30),index (字段n));#没索引名,(id是主键,不用管)
我理解加没加索引名都一样,没自己加索引名,那索引名默认就设置成了字段名。加个索引名就是自己再起一个名字而已
create table 表(字段n varchar(30),index 索引名(字段n));#加了索引名
在已有表中创建索引:
我:这个时候必须要写一个索引名,不然会报错的。
create index 索引名 on 表名(字段名);#添加普通索引
create unique index 索引名 on 表名(字段名);#添加唯一索引
主键索引添加
alter table 表名 add primary key(id);#给id字段加一个主键,可以理解为字段n
查看索引
1、desc 表名; # KEY标志为:MUL 、UNI。
2、show index from 表名; #查看的另一种方式
删除索引名
dropindex 索引名 on 表名;
扩展: 借助性能查看选项去查看索引性能
*自己了解一下(不懂也没事) *
set profiling = 1; 打开功能 (项目上线一般不打开)
自己操作需要查询的语句
show profiles 查看语句执行信息。我理解就是看查询的时间是多少
外键约束 和 关联表的原理
外键约束
- 约束 : 约束是一种限制,它通过对表的行或列的数据做出限制,来确保表的数据的完整性、关联性
- foreign key 功能 : 建立表与表之间的某种约束的关系,由于这种关系的存在,能够让表与表之间的数据,更加的完整,关连性更强,为了具体说明创建如下部门表和人员表。
主表和从表(次表):若同一个数据库中,B表的外键与A表的主键相对应,则A表为主表,B表为从表。
foreign key 外键的定义语法:
- 主表和从表:若同一个数据库中,B表的外键与A表的主键相对应,则A表为主表,B表为从表。
1,foreign key 外键的定义语法:
constraint 外键名 foreign key (外键字段) #外键名称可以不写,不写的话系统自动生成(建议不写这个)
references 主表(主表字段)#一般都是用主表主键
用法:
创表的时候直接加外键约束,取名字(不建议用这个)
create table 次表名(字段n 类型,.., 字段n+1 类型,constraint 外键名 foreign key(字段n) references 主表(主表字段));
#建议取的外键名后面加_fk
#但是建议不取名字,直接让系统帮我取
创表的时候直接加外键约束,不取名字(建议用这个)
create table 次表名(字段n 类型,.., 字段n+1 类型,foreign key(字段n) references 主表(主表字段));
有表后,增加外键约束
alter table 次表名 add constraint 外键名 foreign key(关联字段) references 主表(主表字段);
2,查看外键名称
show create table 表名;#最好是次表名
#注意:删除外键后发现desc查看索引标志还在,其实外键也是一种索引,需要将外键名称的索引删除之后才可以。
3,删除外键,通过外键名称解除外键约束
alter table 表名 drop foreign key 外键名;#最好是次表名
注意:对应的字段类型要一致
设计习惯尽量要关联主键
创建好外键后,系统会自动创建索引
级联动作(主表和次表通过外键 设置值是否同步等设置)
- restrict(默认) : on delete restrict on update restrict
- 当主表删除记录时,如果从表中有相关联记录则不允许主表删除
- 当主表更改主键字段值时,如果从表有相关记录则不允许更改 - cascade :数据级联更新 on delete cascade on update cascade
- 当主表删除记录或更改被参照字段的值时,从表会级联更新
写法是 在创建外键约束的最后加上 on delete cascade on update cascade
alter table 次表名 add constraint 外键名 foreign key(关联字段) references 主表(主表字段) on delete cascade on update cascade;
- set null : on delete set null on update set null
- 当主表删除记录时,从表外键字段值变为null
- 当主表更改主键字段值时,从表外键字段值变为null
写法是 在创建外键约束的最后加上 on delete set null on update set null
alter table 次表名 add constraint 外键名 foreign key(关联字段) references 主表(主表字段) on delete set null on update set null;
表关联关系
- 一对多关系
一张表中有一条记录可以对应另外一张表中的多条记录;但是反过来,另外一张表的一条记录 只能对应第一张表的一条记录,这种关系就是一对多或多对一
举例: 一个人可以拥有多辆汽车,每辆车登记的车主只有一人。
不要在意字段名是啥,我直接复制的。
create table person(
id varchar(32) primary key,
name varchar(30),
age int
);
create table car(
id varchar(32) primary key,
brand varchar(30),
price decimal(10,2),
pid varchar(32),
foreign key(pid) references person(id)
);
- 多对多关系
一对表中(A)的一条记录能够对应另外一张表(B)中的多条记录;同时B表中的一条记录 也能对应A表中的多条记录
举例:一个运动员可以报多个项目,每个项目也会有多个运动员参加,这时为了表达多对多关系需要单独创建关系表。
我理解就是再造一个表来搞多对多
foreign key (运动员id) references athlete (关系表id) #运动员表和关系表要做外键一对多
foreign key (项目id) references item (关系表id) #项目表和关系表要做外键一对多
下面代码是个实例
#运动员表
CREATE TABLE athlete (
id int primary key AUTO_INCREMENT,
name varchar(30),
age tinyint NOT NULL,
country varchar(30) NOT NULL
);
#项目表
CREATE TABLE item (
id int primary key AUTO_INCREMENT,
rname varchar(30) NOT NULL
);
#自己创造的关系表
CREATE TABLE athlete_item (
id int primary key auto_increment,
aid int NOT NULL,
tid int NOT NULL,
FOREIGN KEY (aid) REFERENCES athlete (id), #运动员表和关系表要做外键一对多
FOREIGN KEY (tid) REFERENCES item (id) #项目表和关系表要做外键一对多
);
E-R模型图
E-R模型(Entry-Relationship)即 实体-关系 数据模型,用于数据库设计
用简单的图(E-R图)反映了现实世界中存在的事物或数据以及他们之间的关系
矩形框代表实体,菱形框代表关系,椭圆形代表属性
我:会看就行了。能看懂别人画的E-R模型图就行,
关联表的原理
我:建议数据量大的表放前面。这样更为优化
内连接
注意,交集是两个字段下的相同值。
最后筛选出的内容是这一行数据。不是单个字段
select 字段列表 from 表1 inner join 表2 on 表1.字段 = 表2.字段;
#表1.字段 = 表2.字段,可以理解 比如 表1有很多学号=表2有少量学号;
#总而言之这两个字段的值是相对应的
左连接 : 左表全部显示,显示右表中与左表匹配的项
注意,交集是两个字段下的相同值。
最后筛选出的内容是这一行数据。不是单个字段
select * from 表1 left join 表2 on 表1.字段 = 表2.字段;
#表1.字段 = 表2.字段,可以理解 比如 表1有很多学号=表2有少量学号;
#总而言之这两个字段的值是相对应的
注意:我们尽量使用数据量大的表作为基准表,放在前面。
右连接 :右表全部显示,显示左表中与右表匹配的项
注意,交集是两个字段下的相同值。
最后筛选出的内容是这一行数据。不是单个字段
select * from 表1 right join 表2 on 表1.字段 = 表2.字段;
#表1.字段 = 表2.字段,可以理解 比如 表1有很多学号=表2有少量学号;
#总而言之这两个字段的值是相对应的
注意:我们尽量使用数据量大的表作为基准表,放在前面。
多张表关联
以左连接为例
内连接与右连接同理
select 字段
from 表1 left join 表2
on 表1.字段 = 表2.字段 或者再加where条件
left join 表3 #可以理解把刚才的连表当成一个表再次跟表3连接
on 表1或者表2.字段 = 表3.字段
...再多以此类推
视图
视图概念
视图是存储的查询语句,当调用的时候,产生结果集,视图充当的是虚拟表的角色。其实视图可以理解为一个表或多个表中导出来的表,作用和真实表一样,包含一系列带有行和列的数据 视图中,用户可以使用SELECT语句查询数据,也可以使用INSERT,UPDATE,DELETE修改记录,视图可以使用户操作方便,并保障数据库系统安全,如果原表改名或者删除则视图也失效
我理解就是在原有的表上新搞了一个表,创建的快捷方式。只不过新表能看什么内容,需要自己设置。
语法
创建视图
create view 视图名称_view as select语句;
create [or replace] view 视图名称_view as select语句;
释义:
create view: 创建视图
or replace : 可选,如果添加原来有同名视图的情况下会覆盖掉原有视图。(修改已有视图的方法之一)
视图名称: 后面建议带上_view,不成文规定
select语句:需要什么样的,就写什么样的语句
视图表的增删改查操作
视图的增删改查操作与一般表的操作相同,使用insert update delete select即可,但是1对视图增删改也会同步到原数据表,2原数据表的约束条件仍然对视图产生作用。
我:也可以创建多表关联的视图,但是不建议用视图修改数据。
查看现有视图
show full tables in 库名 where table_type like 'VIEW';
删除视图
drop view 视图名;#写法1
drop view if exists 视图名;#写法2
if exists表示如果存在,这样即使没有指定的视图也不会报错。
修改视图
alter view 视图名 as select新的查询语句;
参考创建视图,将create关键字改为alter
视图作用
作用
- 是对数据的一种重构,不影响原数据表的使用。
- 简化高频复杂操作的过程,就像一种对复杂操作的封装。
- 提高安全性,可以给不同用户提供不同的视图。
- 让数据更加清晰。
缺点
视图的性能相对较差,从数据库视图查询数据可能会很慢。
函数和存储过程
函数创建
注意我的每一个分号存在的位置,函数体都要用;分号
delimiter 自定义符号(一般写$$)
create function 函数名(形参列表) returns 返回类型 #注意是retruns
begin
函数体; #可以写很多增删改sql语句,但是不要直接写查询
return 返回值; #返回指定类型返回值
end 自定义符号(一般写$$)
delimiter ; #必须用分号;回到原来的设置
释义:
delimiter 自定义符号 是为了在函数内些语句方便,制定除了;之外的符号作为函数书写结束标志,一般用$$或者//
形参列表 : 形参名 类型 类型为mysql支持类型
返回类型: 函数返回的数据类型,mysql支持类型即可
举例
无参数的函数调用
#无参数的函数调用
delimiter $$
create function 函数1() returns int #注意是retruns
begin
函数体;#sql语句结束一定要写;分号
return 返回值;#返回一定写int。注意,返回值可以写(select...)
end $$
delimiter ;
下面是调用
select 函数1();
也可以用select 字段 from 表 where 字段><=函数1();
有参数的函数调用
#无参数的函数调用
delimiter $$
#注意是变量名 数据类型,不一定是varchar(30)
create function 函数1(变量名1 varchar(30)) returns int
begin
函数体;#变量名1也可以在这里赋值定义。不知道怎么赋值,往下看变量知识
return return (select...where 字段=变量名1);;#用法之一。
end $$
delimiter ;
下面是调用
select 函数1(传参);
也可以用select 字段 from 表 where 字段><=函数1(传参);
设置变量
局部变量
#局部变量
delimiter $$
create function 函数1() returns int #注意是retruns
begin
declare a int;#定义一个变量a
declare b int;#定义一个变量b
set a=(select...);#赋值方法之一
select... into b;#赋值方法之二
return a+b;#返回一定写int。注意,返回值可以写(select...)
end $$
delimiter ;
存储过程
delimiter $$
create procedure 存储过程名(形参列表)
begin
存储过程; #sql语句增删改查随便写
end $$
delimiter ;
调用方式
call 存储过程名(形参列表);
实例
delimiter $$
create procedure st()
begin
update...;
select...;
end $$
delimiter ;
调用
call st();
有参数的存储过程
这里实在看不懂,就看下一个标题讲解。我写的比较详细
delimiter 自定义符号
create procedure 存储过程名(形参列表)
begin
存储过程 -- sql语句构成存储过程语句集
end 自定义符号
delimiter ;
释义:
delimiter 自定义符号 是为了在函数内些语句方便,制定除了;之外的符号作为函数书写结束标志
形参列表 :[ in | out | inout ] 形参名 类型
in 输入,out 输出,inout 可以输入也可以输出
存储过程: 若干sql语句组成,如果只有一条语句也可以不写delimiter和begin,end
存储过程三个参数的区别
-
in 类型参数可以接收变量也可以接收常量,传入的参数在存储过程内部使用即可,但是在存储过程内部的修改无法传递到外部。
-
out 类型参数只能接收一个变量,接收的变量不能够在存储过程内部使用(内部为NULL),但是可以在存储过程内对这个变量进行修改。因为定义的变量是全局的,所以外部可以获取这个修改后的值。
-
inout类型参数同样只能接收一个变量,但是这个变量可以在存储过程内部使用。在存储过程内部的修改也会传递到外部。
有参数的存储过程实例讲解
e.g. : 分别将参数类型改为in out inout 看一下结果区别
delimiter $$
create procedure 函数n(in 参数名 参数类型) #用法1
create procedure 函数n(out 参数名 参数类型) #用法2
create procedure 函数n(inout 参数名 参数类型) #用法3
begin #下面是存储过程随便写的语句,不是固定
select 参数名;#select 这里的作用是查看,类似于python的打印
set 参数名=100;#
select 参数名;#
end $$
delimiter ;
用法1调用:
第一种传参方法:直接:call 函数n(实参);
第二种传参方法:看下面
set @num=10;#@num是定义的实参变量
call 函数n(实参);#这里的实参传的是@num。分开写是容易让自己以后看的懂
#in 类型类似于python的形参和实参的用法,传参,接收参数,函数内用参数。但是函数内修改参数的值,外层是不会变得。
用法2调用:
set @num=10;
call 函数n(@num)#只能传变量,out 类型参数只能接收一个变量
#out 类型,接收的变量不能在函数内直接用,因为接收变量后默认为NULL,需要重新赋值才能用。重新赋值后外部的值也变了。
用法3调用:和用法2调用方法一样
#inout类型参数同样只能接收一个变量,但是这个变量可以直接用。也可以重新赋值,重新赋值后外部的值也变了。
存储过程和存储函数操作
1调用存储过程
call 存储过程名(参数);
#无参数则不写
2调用存储函数
select 函数名(参数);
#无参数则不写
3使用show create语句查看存储过程和函数的定义
show create procedure 存储过程名; #查看存储过程
show create function 存储函数名; #查看存储函数
#我理解就是看当初创建的语句
4查看所有函数或者存储过程
select name,type from mysql.proc where db='库名';
5删除存储过程或存储函数
删除存储过程
drop procedure 名字; #写法1删除存储过程
drop procedure if exists 名字; #写法2删除存储过程,区别是这个如果需要删除的存储过程没有,也不报错
删存储函数
drop function 名字; #写法1删除存储函数
drop function if exists 名字; #写法2删除存储函数,区别是这个如果需要删除的存储函数没有,也不报错
函数和存储过程区别
- 函数有且只有一个返回值,而存储过程不能有返回值。
- 函数只能有普通参数,而存储过程可以有in,out,inout多个类型参数。
- 存储过程中的语句功能更丰富,实现更复杂的业务逻辑,可以理解为一个按照预定步骤调用的执行过程,而函数中不能展示查询结果集语句,只是完成查询的工作后返回一个结果,功能针对性比较强。
- 存储过程一般是作为一个独立的部分来执行(call调用)。而函数可以作为查询语句的一个部分来调用。
事务控制
事务概述
MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,既需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,如果操作就必须同时操作成功,如果有一个不成功则所有数据都不动。这时候数据库操作语句就构成一个事务。事务主要处理数据的增删改操作。
- 定义
一件事从开始发生到结束的过程 - 作用
确保数据操作过程中的数据完整和使用安全。
事务操作
- 开启事务
begin;
- 开始执行事务中的若干条SQL命令(增删改)
- 终止事务,若begin之后使用commit提交事务或者使用rollback进行事务回滚。
commit; # 事务中SQL命令都执行成功,提交到数据库,结束!
rollback; # 有SQL命令执行失败,回滚到初始状态,结束!
注意:事务操作只针对数据操作。rollback不能对数据库,数据表结构操作恢复。
事务四大特性
- 原子性(atomicity)
一个事务必须视为一个不可分割的最小工作单元,对于一个事务来说,不可能只执行其中的一部分操作,整个事务中的所有操作要么全部提交成功,要么全部失败回滚
- 一致性(consistency)
事务完成时,数据必须处于一致状态,数据的完整性约束没有被破坏。
- 隔离性(isolation)
数据库允许多个并发事务同时对其数据进行读写和修改的能力,而多个事务相互独立。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
- 持久性(durability)
一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。
事务隔离级别
事务四大特性中的隔离性是在使用事务时最为需要注意的特性,因为隔离级别不同带来的操作现象也有区别
隔离级别
- 读未提交:read uncommitted
事务A和事务B,事物A未提交的数据,事物B可以读取到 这里读取到的数据叫做“脏数据” 这种隔离级别最低
我:脏数据可以理解为:A事务操作了一堆,但是还未提交,B全看到了。A最后没有提交事务,B刚才看的那一堆A操作的数据就是脏数据。
- 读已提交:read committed
事物A提交后的数据,事物B才能读取到。这种隔离级别高于上面的读未提交。解决了脏数据,缺点:这种隔离级别会导致“不可重复读取”
我:不可重复读取可以理解为:假设B第1次读数据是cc,A也读了这个数据同样也是cc,但是A现在把cc改成dd并且提交事务。B这时再次读这个数据就变成了dd。导致第1次和第2次读出来的记录数不一样。
- 可重复读:repeatable read
事务A提交之后的数据,事务B读取不到 事务B是可重复读取数据 这种隔离级别高于上面的读已提交 MySQL默认级别 虽然可以达到可重复读取,但是会导致“幻像读”
我:幻象读可以理解为:A开始事务,修改了一个表中第7行的c字段,A提交事务。接着,B读取事务的时候是看不到修改的第7行c字段,但是如果B update修改了第7行的c字段同一行的其他字段值,然后再select读这个数据 就可以看到刚才A改的第7行的c字段新内容。
MySql两个事务容易堵塞,其实是被锁住了。事务A修改了一个数据,事务b第二时间也修改这个数据,就会堵塞住。其实是这行数据上锁了,需要事务A提交之后才能修改这个数据。(简单理解一下)
- 串行化:serializable
事务A和事务B,事务A在操作数据库时,事务B只能排队等待 这种隔离级别很少使用,吞吐量太低,用户体验差 这种级别可以避免“幻像读”,每一次读取的都是数据库中真实存在数据,事务A与事务B串行,而不并发
数据库优化
1,数据库设计范式
设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式。
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。
通常数据库设计遵循第一第二第三范式,以避免数据操作异常,又不至于表关系过于复杂。
范式简介:
- 第一范式: 数据是不可分割的原子数据,不能是集合,数组等类型。简单来说要求数据元素不可再分。
例如: 在国内的话通常理解都是姓名是一个不可再拆分的单位,这时候就符合第一范式;但是在国外的话还要分为first name和last name,这时候姓名这个字段还可以拆分为姓和名2个字段,就不符合第一范式了。 - 第二范式: 要求数据库表中的每个记录必须可以被唯一的区分,所有属性依赖于主属性。其实简单来说就是设置主键就好了。
- 第三范式: 在第二范式的基础上属性不传递依赖,合理使用外键,使不同的表中不要有重复的字段就好了。简单来说,就是外键最好设置的都是对应的主键,不要把普通字段设置外键
2,MySQL存储引擎
- 定义: mysql数据库管理系统中用来处理表的处理器
基本语法
1、查看所有存储引擎
show engines;
2、查看已有表的存储引擎
show create table 表名;
3、创建表指定
create table 表名(...)engine=MyISAM; #MyISAM是MySQL存储引擎之一
4、已有表指定
alter table 表名 engine=InnoDB; #InnoDB是MySQL存储引擎之一
- 常用存储引擎特点
InnoDB
1. 支持行级锁,仅对指定的记录进行加锁,这样其它进程还是可以对同一个表中的其它记录进行操作。
2. 支持外键、事务、事务回滚
3. 表字段和索引同存储在一个文件中
1. 表名.frm :表结构
2. 表名.ibd : 表记录及索引文件
MyISAM
1. 支持表级锁,在锁定期间,其它进程无法对该表进行写操作。如果你是写锁,则其它进程则读也不允许
2. 表字段和索引分开存储
1. 表名.frm :表结构
2. 表名.MYI : 索引文件(my index)
3. 表名.MYD : 表记录(my data)
- 如何选择存储引擎
1. 执行查操作多的表用 MyISAM(使用InnoDB浪费资源)
2. 执行写操作多的表用 InnoDB
3,字段数据类型和键的选择
- 数据类型优先程度 数字类型 --> 时间日期类型 --> 字符串类型
- 同一类型 占用空间小的 --> 占用空间大的
字符串在查询比较排序时数据处理慢
占用空间少,数据库占磁盘页少,读写处理就更快
- Innodb如果不设置主键也会自己设置隐含的主键,所以最好自己设置
- 尽量设置占用空间小的字段为主键
- 建立外键会自动建立索引,在表关联查询时建议使用外键子段作为关联条件
- 外键虽然可以保持数据完整性,但是会降低数据导入和操作效率,增加维护成本
4,explain语句
语法:
explain sql语句;
其实就是正常的sql语句前,写上explain再执行
使用 EXPLAIN 关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。这可以帮你分析你的查询语句或是表结构的性能瓶颈。通过explain命令可以得到:
- 表的读取顺序
- 数据读取操作的操作类型
- 哪些索引可以使用
- 哪些索引被实际使用
- 表之间的引用
- 每张表有多少行被优化器查询
explain主要字段解析:(这个要知道概念)
- table:显示这一行数据是属于哪张表
- type:这是最重要的字段之一,显示查询使用了何种类型。从最好到最差的连接类型为system、const、eq_reg、ref、range、index和ALL,一般来说,得保证查询至少达到range级别,最好能达到ref。
type中包含的值:
- system、const: 可以将查询的变量转为常量. 如id=1; id为 主键或唯一键.
- eq_ref: 访问索引,返回某单一行的数据.(通常在联接时出现,查询使用的索引为主键或唯一键)
- ref: 访问索引,返回某个值的数据.(可以返回多行) 通常使用=时发生
- range: 这个连接类型使用索引返回一个范围中的行,比如使用>或<查找东西,并且该字段上建有索引时发生的情况
- index: 以索引的顺序进行全表扫描,优点是不用排序,缺点是还要全表扫描
- ALL: 全表扫描,应该尽量避免
- possible_keys:显示可能应用在这张表中的索引。如果为空,表示没有可能应用的索引。
- key:实际使用的索引。如果为NULL,则没有使用索引。
- key_len:使用的索引的长度。在不损失精确性的情况下,长度越短越好
- rows:MySQL认为必须检索的用来返回请求数据的行数
5 SQL优化
- 尽量选择数据类型占空间少,在where ,group by,order by中出现的频率高的字段建立索引
- 尽量避免使用 select * …;用具体字段代替 * ,不要返回用不到的任何字段
- 尽量控制使用自定义函数
- 查询最后添加 LIMIT 会停止全表扫描
- 尽量避免 NULL 值判断,否则会进行全表扫描,默认值为空时可以用默认0代替
优化前:select number from t1 where number is null;
优化后:select number from t1 where number=0; - 尽量避免 or 连接条件,否则会放弃索引进行全表扫描,可以用union代替
优化前:select id from t1 where id=10 or id=20;
优化后: select id from t1 where id=10 union all select id from t1 where id=20; - 尽量避免使用 in 和 not in,否则会全表扫描
优化前:select id from t1 where id in (1,2,3,4);
优化后:select id from t1 where id between 1 and 4;
6 表的拆分
垂直拆分 : 表中列太多,分为多个表,每个表是其中的几个列。将常查询的放到一起,blob或者text类型字段放到另一个表
水平拆分 : 减少每个表的数据量,通过关键字进行划分然后拆成多个表
数据库安全和管理
表的复制
- 表能根据实际需求复制数据
- 复制表时不会把KEY属性复制过来
语法
create table 表名 select 查询命令;
#查询什么样的表,就复制什么样的表了
数据库备份。(这个是在终端下写,不是在mysql环境中写)
- 备份命令格式,就是导出命令.sql文件(这个是在终端下写,不是在mysql环境中写)
mysqldump -u 用户名 -p 源库名 > /路径/备份库名.sql
mysqldump -u 用户名 -p 源库名 > 备份库名.sql #不写路径直接就备份在当前文件夹下
- 恢复命令格式,就是导入命令.sql文件(这个是在终端下写,不是在mysql环境中写)
mysql -u root -p 目标库名 < 备份的库名.sql
#备份的库名.sql 就是1导出的文件
MySQL远程连接
连接数据库语法
mysql -h 主机地址 -u 用户名 -p
主机地址是数据库服务器地址,比如:127.0.0.1 的形式
服务端如果想要被远程连接,就要数据库更改配置文件,重启服务!
更改配置文件,重启服务!
1.cd /etc/mysql/mysql.conf.d
2.sudo vi mysqld.cnf 找到43行左右,加 # 把第43行注释了,变成:# bind-address = 127.0.0.1,下行是注释
找到43行,按i进入编辑模式,把第43行注释了,按esc返回,wq保存退出
3.保存退出
4.sudo service mysql restart 这一步是重启服务
5.进入mysql环境,进入mysql库,修改用户user表的host值 下面是步骤
进入mysql环境
use mysql;
update user set host='%' where user='root'; 这一步的详细解释看下面
#user='root' 值是root用户,就是让root用户可以远程登录,也可以是其他用户名
#host='%' 值的意思是决定登录范围。如果是%,就可以随便用刚才绑定的root用户登录不同的电脑。。如果值是ip地址,host=ip地址,那就只能用绑定的ip电脑才能登陆root账号
#也可以用这个命令看看,用户都有哪些 select Host,User from user;
6.刷新权限 (mysql环境下)
flush privileges;
添加用户和授权
1. 用root用户登录mysql
mysql -u root -p
2. 添加用户 % 表示自动选择可用IP
CREATE USER 'username'@'host' IDENTIFIED BY 'password'; 下面看解释
#'username' 是设置的用户名
#'host' 是ip地址,如果设置成% 就随便登录电脑
#'password' 密码
3. 权限管理
增加权限(权限列表不知道可以看下一个代码块)
grant 权限列表 on 库.表 to "用户名"@"%" identified by "密码" with grant option;
#库.表 : *.* 代表所有库的所有表。
#with grant option 加不加都行,加上就等于这个用户可以创建这个用户的下线用户。不加就是这个用户没有创建自己下线用户的权限
#如果后续想继续给这个用户加权限,直接写这个语句就行,意思是追加权限,不是覆盖
删除权限
revoke insert,update,select(要删的权限) on 库.表 from 'user'@'%';
4. 刷新权限
flush privileges;
5. 删除用户
drop user "用户名"@"%"
权限列表
all privileges #全部权限
select #查询
insert #插入
update #更新表
delete #删除表数据
alter #表结构
create #创建
drop等。#删除
库.表 : *.* 代表所有库的所有表
pymysql模块
pymysql是一个第三方库,如果自己的计算机上没有可以在终端使用命令进行安装。
安装
sudo pip3 install pymysql
流程
- 建立数据库连接:db = pymysql.connect(…)
- 创建游标对象:cur = db.cursor()
- 游标方法: cur.execute(“insert …”)
- 提交到数据库 / 查询数据 : db.commit() / cur.fetchall()
- 关闭游标对象 :cur.close()
- 断开数据库连接 :db.close()
import pymysql
age = {
'host':'192.168.137.129', #主机地址,本地 localhost
'port':3306, #端口号,默认3306,可以不写,报错要写
'user':'root', #用户名
'password':'123456', #密码
'database':'stu', #stu是指定控制的数据库,可以不写,不写就是控制全部
'charset':'utf8' #编码方式,推荐使用 utf8,可以不写,报错要写
}
#第1步:连接主机创建连接对象
db = pymysql.connect(**age) #传入变量
#第2步:创建游标对象。(游标和事务差不多)
cur = db.cursor()
这里面写增删改 或者 查询
#第5步:关闭
cur.close()#关闭游标对象
db.close()#断开数据库连接
pymysql各种模板,建议都在第2个模板上改。123是写,4是查
1,引擎是MyISAM,增删改(写入操作)模板
import pymysql
age = {
'host':'192.168.137.129', #主机地址,本地 localhost
'port':3306, #端口号,默认3306,可以不写,报错要写
'user':'root', #用户名
'password':'123456', #密码
'database':'stu', #stu是指定控制的数据库,可以不写,不写就是控制全部
'charset':'utf8' #编码方式,推荐使用 utf8,可以不写,报错要写
}
#第1步:连接主机创建连接对象
db = pymysql.connect(**age) #传入变量
#第2步:创建游标对象。(游标和事务差不多)
cur = db.cursor()
#第3步:游标方法,也是执行sgl语句。这里展示的是增删改操作(写入操作)
sql = 'update hobby set price = 9800 where id = 3;' #别忘记写分号
cur.execute(sql) #传入变量,执行语句
#不一定是第4步
db.commit() #如果数据表引擎是InnoDB,会默认开启事务。所以最后要关闭事务
#如果是MyISAM引擎,就不用写第4步。但是写上也没事
#第5步:关闭
cur.close()#关闭游标对象
db.close()#断开数据库连接
2,引擎是InnoDB,增删改(写入操作)模板。建议都在这个模板改
因为这里引用了try,except Exception捕获异常了
import pymysql
age = {
'host':'192.168.137.129', #主机地址,本地 localhost
'port':3306, #端口号,默认3306,可以不写,报错要写
'user':'root', #用户名
'password':'123456', #密码
'database':'stu', #stu是指定控制的数据库,可以不写,不写就是控制全部
'charset':'utf8' #编码方式,推荐使用 utf8,可以不写,报错要写
}
#第1步:连接主机创建连接对象
db = pymysql.connect(**age) #传入变量
#第2步:创建游标对象。(游标和事务差不多)
cur = db.cursor()
#第4步
'''
db.commit()
#如果数据表引擎是InnoDB,会默认开启事务。所以最后要关闭事务
这里用了个异常,很棒
'''
try:
# 第3步:游标方法,也是执行sgl语句。这里展示的是增删改操作(写入操作)
sql = 'update hobby set price = 9800 where id = 3;' # sql语句,别忘记写分号
cur.execute(sql) # 传入变量,执行语句
db.commit() #提交事务
except Exception as e:#捕获异常
print(e)
db.rollback() #回滚
#第5步:关闭
cur.close()#关闭游标对象
db.close()#断开数据库连接
3,execute(sql, args_list) 游标方法第二个值的用法和解释。引用多个参数写入 模板
import pymysql
age = {'host':'192.168.137.129','port':3306,'user':'root','password':'123456','database':'stu','charset':'utf8'
}
db = pymysql.connect(**age)
cur = db.cursor()
#第二个参数解释
a = input('名字')#1,不用定义类型,这是输入的变量
b = input('金额')#什么变量都行
try:
sql = 'update hobby set price = %s where name =%s;' # 2,用变量的地方直接用%s
cur.execute(sql, [b,a]) # 3,以数组传入参数,执行的时候会自动把变量按顺序传入%s中。记住是按顺序传。
#可以看我画的图。
db.commit()
except Exception as e:
print(e)
db.rollback()
cur.close()
db.close()
4,executemany(sql命令,args_list) 用法,大量数据增删改模板
如果不想用,可以用上面的execute(),然后用for循环就行了。
import pymysql
age = {
'host':'192.168.137.129','port':3306,'user':'root','password':'123456','database':'stu','charset':'utf8'
}
db = pymysql.connect(**age)
cur = db.cursor()
# 1,这里是数组里面有元组
变量n= [
("Lucy",18,'w',76),
("Joy",17,'m',69),
("Levi",18,'m',81)
]
sql = "insert into 表(字段1,字段2,字段3,字段4) values (%s,%s,%s,%s);"#几个字段就写几个%s
cur.executemany(sql,变量n) #2,data变量传进去。。执行的时候,每个元组的数据都会安装顺序传入,分别执行一次语句。
#图不好画,就没画图,应该很好理解
db.commit()
# 关闭数据库连接
cur.close()
db.close()
我:用这个方法的好处是,开启一个事务,然后操作大量的数据,最后关闭事务。效率比较高
如果是用execute(),然后用for循环。如果要操作10次,就要开启10次事务,关闭10次事务,效率比较低。
4,查询,读,模板
cur.fetchone() 获取查询结果集的第一条数据,查找到返回一个元组否则返回None
cur.fetchmany(n) 获取前n条查找到的记录,返回结果为元组嵌套元组, ((记录1),(记录2)),查询不到内容返回空元组。
cur.fetchall() 获取所有查找到的记录,返回结果形式同上。
import pymysql
kwargs = {"host":"ip地址","port": 3306,"user": "名字", "passwd": "密码","database":"库","charset": "utf8"}
db = pymysql.connect(**kwargs)
cur = db.cursor()
# 给sql语句传值 使用第二个参数按照位置给sql语句%s传值
sql = "select 字段 from 表 where 字段1>%s and 字段2=%s;"
cur.execute(sql,[80,'m'])#第二个值不传入也行,我只是演示一下。这个方法依然可行
# 第1种查询方式:查询结果集的第一条数据,查找到返回一个元组否则返回None
a = cur.fetchone()#是结果集的第一条数据,是结果集的,是结果集的。
print(a)
# 第2种查询方式:多个查询结果
a= cur.fetchmany(7)#只会查询到数据表的前7条数据
print(a)
# 第3种查询方式:所有查询结果
a = cur.fetchall()
print(a)
# 第4种查询方式:迭代获取结果
for row in cur:#直接便利游标对象,也可以查询到全部的。
print(row)
我:注意,第1 2 3种方式可以混合使用。但是混合使用会接着查询
意思就是比如我用第1种方式查询后,接着又用第二种 参数给7 那么就会从第二条接着查询7条数据,因为第一条数据刚才查过了。
这就是接着查询,悟了没有
cur.close()
db.close()
文件存储
- 存储文件路径
- 优点:节省数据库空间,提取方便
- 缺点:文件或者数据库发生迁移会导致文件丢失
- 存储文件本身
- 优点:安全可靠,数据库在文件就在
- 缺点:占用数据库空间大,文件存取效率低
静态文件存储示例:存取二进制数据(例如图片)
import pymysql
kwargs = {
"host": "localhost",
"port": 3306,
"user": "root",
"password": "123456",
"database": "stu",
"charset": "utf8"
}
# 连接数据库
db = pymysql.connect(**kwargs)
# 创建游标 使用sql操作数据得到结果的对象
cur = db.cursor()
# 存储图片
# with open("left.png",'rb') as f:
# data = f.read()
# sql = "update class set image=%s where id=4;"
# cur.execute(sql,[data])
# db.commit()
# 取出图片
sql = "select image from class where id=4;"
cur.execute(sql)
data = cur.fetchone()#这里返回的是个元组
with open("xxx.png",'wb') as f:
f.write(data[0])#是个元组,所以要用索引
# 关闭
cur.close()
db.close()