5.8
昨日回顾
-
navicat
-
涉及多表查询的时候,肯定用到联表操作和子查询,可以联很多次
-
把昨天上课的题目理解自己写出来
-
pymysql模块
import pymysql conn = pymysql.connect( host = '127.0.0.1', port = 3306, user = root, password = '6008', database = 'db1', # 可以简写成db charset = 'utf8' ) # cursor = conn.cursor() # 括号内不急啊参数,查询得到的是元组形式,数据不够明确 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) sql = 'select * from user' res = cursor.execute(sql) # 返回值是sql语句执行受影响的行数 cursor.fetchone() # 拿到一条数据,数据本身 cursor.fetchall() # 拿到所有的结果,列表 # 这个cursor对象有类似文件指针的东西 cursor.scroll(1,'absolute')
-
sql注入
有很多种方式,我们用最明显的方式
日常应用应该在获取用户输入的内容时候,限制一些特殊符号的输入
在pymysql中,execute嫩帮你过滤,在sql中用%s占位,在execute里拼接
-
pymysql补充
# 增删改查 sql = 'insert into user(name,password) values(%s,%s)' rows = cursor.execute(sql) # 增删改查都有结果,但是无法直接执行这个命令 # 要sql语句起作用,要进行二次确认 conn.commit() # 确认操作,才能在sql中产生影响 # 也可以在conn中设置autocmmit为True,不用commit也能增删改 # 同时进行多次增删改:executemany # 执行多个sql语句:列表套元组 executemany(sql,[(123,123),(123,123)])
今日内容
今日内容基本都是了解知识点,基本用不到
- 视图
- 触发器
- 事务(掌握)
- 存储过程
- 内置函数
- 流程控制
- 索引理论
视图
-
什么是视图
视图就是通过查询一张虚拟表,保存下来,下次可以直接使用
# 创建视图
create view 表名 as 表查询的sql语句
# 创建的视图在navicat的视图里可以查看
# 生成的视图在硬盘上只存一个frm文件:表结构,数据还在原来的表中
# 视图一般只用来查询,不要改,改了会报错
# 删除视图
DROP VIEW teacher_view;
创建过多视图维护会有麻烦,所以不要创建过多的视图
触发器
在满足对表数据进行增,删,改的情况下,自动触发的功能,使用触发器可以帮助我们实现监控,日志,自动处理异常等等
触发器可以在六种情况下自动触发:增前,增后,删前删后,改前改后
# 触发器语法
create trigger 触发器的名字 before/after insert/update/delete on 表名 for each row
begin
sql语句
end
# 对触发器的名字,应该做到见名知意
# 增;
create trigger tri_before_insert_t1 before insert on t1 for each row
begin
sql语句
end
# 在t1表增加数据之前触发
# 针对删除和修改的书写一致
为了让触发器 begin和end之间的sql语句能正常执行,不因为使用分号就结束,修改结束sql语句的符号:delimiter
# 修改mysql语句的结束符:将;分号改成别的
delimiter $$
# 将默认的结束符号由分号改为 $$
使用案例
#准备表
CREATE TABLE cmd (
id INT PRIMARY KEY auto_increment,
USER CHAR (32),
priv CHAR (10),
cmd CHAR (64),
sub_time datetime, #提交时间
success enum ('yes', 'no') #0代表执行失败
);
CREATE TABLE errlog (
id INT PRIMARY KEY auto_increment,
err_cmd CHAR (64),
err_time datetime
);
# 创建触发器,把sql语句结束符号变为 //
# 因为sql语句是在触发后运行的,所以还是用分号
delimiter //
CREATE TRIGGER tri_after_insert_cmd AFTER INSERT ON cmd FOR EACH ROW
BEGIN
IF NEW.success = 'no' THEN #等值判断只有一个等号
INSERT INTO errlog(err_cmd, err_time) VALUES(NEW.cmd, NEW.sub_time) ; #必须加分号
END IF ; #必须加分号
END//
delimiter ;
#往表cmd中插入记录,触发触发器,根据IF的条件决定是否插入错误日志
INSERT INTO cmd (
USER,
priv,
cmd,
sub_time,
success
)
VALUES
('egon','0755','ls -l /etc',NOW(),'yes'),
('egon','0755','cat /etc/passwd',NOW(),'no'),
('egon','0755','useradd xxx',NOW(),'no'),
('egon','0755','ps aux',NOW(),'yes');
# 可以通过
select * from errlog # 看到错误记录
# 删除触发器
drop trigger tri_after_insert_cmd;
事务
-
什么是事务
开启一个可以包含多条sql语句,这些sql语句要么同时成功,要么一个都不成功,称之为事物的原子性
-
事务的作用
保证对数据操作的安全性:操作多条数据的时候,可能出现一条不成功的情况
-
事物的四大特性
ACID A:原子性:一个事物是一个不可分割的单位,事务中包含的诸多操作要么同时成功要么同时失败 C:一致性:事务必须是数据库 I:隔离性 D:持久性
-
使用方法
# 事务相关的关键字 # 1 开启事务 start transaction; # 2 回滚(回到事务执行之前的状态) rollback; # 3 确认(确认之后就无法回滚了) commit; """模拟转账功能""" create table user( id int primary key auto_increment, name char(16), balance int ); insert into user(name,balance) values ('jason',1000), ('egon',1000), ('tank',1000); # 1 先开启事务 start transaction; # 2 多条sql语句 update user set balance=900 where name='jason'; update user set balance=1010 where name='egon'; update user set balance=1090 where name='tank'; # 出现异常,回滚到初始状态 start transaction; update user set balance=900 where name='wsb'; # 买支付100元 update user set balance=1010 where name='egon'; # 中介拿走10元 uppdate user set balance=1090 where name='ysb'; # 卖家拿到90元,出现异常没有拿到 rollback; commit;
-
总结
- 当你想让多条sql语句保持一致性,使用事务
存储过程
类似于python中的自定义函数,存储过程存放于mysql服务端中,直接调用存储过程触发内部sql语句的执行
演示
create procedure 存储过程的名字(形参1,形参2,...)
begin
sql代码
end
# 调用
call 存储过程的名字();
三种开发模式
-
第一种
应用程序:程序员写代码开发,MySQL提前写好存储过程,给应用程序调用
优点:不需要写sql语句了,提升开发效率
缺点:考虑到人为因素,跨部门沟通的问题,后续的存储过程扩展性差
-
第二种
程序员写代码开发之外,涉及到数据库的操作也自己写
优点:扩展性高
缺点:开发效率低,编写sql太繁琐,后续还要考虑优化问题
-
第三种
应用程序开发只写程序代码,不写sql语句,基于别人写好的mysql框架,直接调用操作即可
ORM框架
优点:效率最高
缺点:扩展性差,效率低
第一种基本不用,一般都是第三种,出现问题再手动写sql
存储过程具体
delimiter $$
create procedure p1(
in m int, # 只进不出 m不能返回出去
in n int,
out res int # 该形参可以返回出去
)
begin
select tname from teacher where tid>m and tid<n;
set res=666; # 将res变量修改 用来标识当前的存储过程代码确实执行了
end $$
delimiter ;
# 针对形参res 不能直接传数据 应该传一个变量名
# 定义变量
set @ret = 10;
# 查看变量对应的值
select @ret;delimiter $$
create procedure p1(
in m int,
in n int,
out res int,
)
begin
select tname from teacher where tid>m and tid<n;
set res=0 # 修改res,标识当前存储过程确实执行了
end $$
delimiter ;
# 针对形参res,不能直接传值,应该传一个变量名
# 定义变量
set @ret = 10;
# 查看变量对应的值
select @ret;
在python中调用存储过程
import pymysql
conn = pymysql.connect(
host = '127.0.0.1',
port = 3306,
user = 'root',
passwd = '123456',
db = 'day48',
charset = 'utf8',
autocommit = True
)
cursor = conn.cursor(pymysql.cursors.DictCursor)
# 调用存储过程
cursor.callproc('p1',(1,5,10))
"""
@_p1_0=1
@_p1_1=5
@_p1_2=10
"""
# print(cursor.fetchall())
cursor.execute('select @_p1_2;')
print(cursor.fetchall())
函数
跟存储过程是有区别的,存储过程是自定义函数,函数就类似于是内置函数
('jason','0755','ls -l /etc',NOW(),'yes')
CREATE TABLE blog (
id INT PRIMARY KEY auto_increment,
NAME CHAR (32),
sub_time datetime
);
INSERT INTO blog (NAME, sub_time)
VALUES
('第1篇','2015-03-01 11:31:21'),
('第2篇','2015-03-11 16:31:21'),
('第3篇','2016-07-01 10:21:31'),
('第4篇','2016-07-22 09:23:21'),
('第5篇','2016-07-23 10:11:11'),
('第6篇','2016-07-25 11:21:31'),
('第7篇','2017-03-01 15:33:21'),
('第8篇','2017-03-01 17:32:21'),
('第9篇','2017-03-01 18:31:21');
select date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m');
流程控制
# if 判断
# while 循环
索引
一种数据结构,类似于书的目录,以后在查询数据应该先找目录再找数据,而不是一页一页的翻书,提升查询速度降低IO
索引再MySQL中也叫键,是存储引擎用于快速查找记录的一种数据结构
- primary key
- unique key
- index key
上面三种key,前面两种除了可以增加查询速度意外还各自有约束条件,而最后一种index key 没有任何约束,只是用来帮助快速查询
本质
通过不断地缩小想要的数据范围,筛选出最终的结果,同时将随机事件一页一页地翻,变成顺序事件(先找目录,再找数据)
有了索引机制,我们可以总是用一种固定的方式查找数据
一张表中可以有多个索引(多个目录),但是索引太多会导致索引很慢
b+树
只有叶子节点存放的是真实的数据,其他节点存放的是虚拟数据
树的层级越高,经历的步骤就越多
聚集索引
聚集索引指的就是主键
innodb只有两个文件,直接将主键存放在idb表中
辅助索引
主键之外的都叫辅助索引,查询索引不会一致用到主键,也可能用到username,password一类字段,那么这个时候没有办法利用聚集索引,这个时候你就可以根据情况给其他字段设置辅助索引,也是一个b+树
覆盖索引
在辅助索引的叶子节点就已经拿到了需要的数据