MySQL(视图、触发器、事务、存储过程、索引)
python操作MySQL
# python中支持操作MySQL的模块很多 其中最常见的当属'pymysql'
为了使python连接上数据库,你需要一个驱动,这个驱动是用于与数据库交互的库
PyMySQL : 这是一个使Python连接到MySQL的库,它是一个纯Python库(文件夹)
PyMySQL 是一个纯 Python 实现的 MySQL 客户端操作库,支持事务、存取过程、批量执行,实现增删改查等
# 下载方式
1.命令行输入(下载模块 pymysql)
pip3 install pymysql 
2.切换下载的源(仓库)
pip3 install pymysql -i 源地址
 pymysql的基本使用
pymysql的基本使用
import pymysql
# 链接服务端
conn_obj = pymysql.connect(  # 赋值给 conn连接对象
    host='127.0.0.1',  # MySQL服务端的IP地址
    port=3306,  # MySQL默认PORT地址(端口号)
    user='root',  # 用户名
    password='123',  # 密码  也可以简写 passwd
    database='db_01',  # 连接数据库名称 也可以简写 db
    charset='utf8'  # 字符编码 不能写utf-8
)
# 生成一个获取命令的游标对象(相当于cmd打开mysql中的 mysql>)
cursor = conn.cursor(
    cursor=pymysql.cursors.DictCursor
)  # 括号内不写参数 数据是元组要元组 不够精确 添加参数则会将数据处理成字典
# 定义SQL语句
sql = 'select * from teacher'
# 执行SQL语句
 affect_rows = cursor.execute(sql)
 print(affect_rows)  # 执行SQL语句之后受影响的行数
# 获取返回结果
res = cursor.fetchall()
print(res)
excute返回值
1.execute返回值是执行SQL语句之后受影响的行数
2.fetchall()获取所有的结果
fetchone()获取结果集第一个结果
fetchmany()括号内可以指定获取几个结果集
# 获取返回结果
# res = cursor.fetchall()  # 列表套字典
# res = cursor.fetchone()  # 数据字典
# res = cursor.fetchmany(3)  # 列表套字典
cursor.scroll(1, 'relative')  # 相对于当前位置往后移动一个单位
cursor.scroll(1, 'absolute')  # 相对于起始位置往后移动一个单位


SQL注入问题
基本登录代码
import pymysql
# 创建链接
conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='123',
    database='db_01',
    charset='utf8'
)
# 生成一个游标对象
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)  # 让数据自动组织成字典
# 获取用户名和密码
username = input('username>>>:').strip()
password = input('password>>>:').strip()
# 构造SQL语句
sql = "select * from egg where name='%s' and password='%s'"%(username,password)
print(sql)
# 执行sql语句
cursor.execute(sql)
# 获取所有返回结果
res = cursor.fetchall()
# if判断
if res:
    print(res)
    print('登录成功')
else:
    print('用户名或密码错误')
注入问题发现
# 写正确的用户名错误的密码也可以登录
	用户名:jason' -- jhahsdjasdjasd
  密码:直接回车
# 用户名和密码都不需要也可以登录
	用户名:xxx' or 1=1 -- asdjasjdkajsd
  密码:直接回车
"""上述现象就是典型的SQL注入问题"""
	上述情况利用的是MySQL注释语法及逻辑运算符
	就是通过一线特殊符号的组合 达到某些特定的效果从而避免常规的逻辑

解决注入问题
#execute方法
自动将 用户名和密码放在对应的%s内,并且放之前会自动对用户名和密码做特殊符号的校验,确保安全性
'''execute方法自带校验SQL注入问题 自动处理特殊符号
设计到敏感数据的拼接 全部交给execute方法即可'''
# 代码修改:
sql = "select * from userinfo where name=%s and password=%s;"
    
cursor.execute(sql, (name, password))
# execute批量插入数据
sql = 'insert into userinfo(name,password) values(%s,%s)'
cursor.executemany(sql,[('tom',123),('lavin',321), ('pony',333)])

commit()二次确认
#数据的增删改查四个操作
查                           不会影响真正的数据 重要程度最低
增、改、删                    都会影响真正的数据 重要程度较高
'''pymysql针对增、改、删三个操作 都设置了二次确认 如果不确认则不会真正影响数据库'''
# 方式1:代码直接编写
	affect_row = cursor.execute(sql)
	conn_obj.commit()  # 手动二次确认
    
# 方式2:配置固定参数
conn_obj = pymysql.connect(
      host='127.0.0.1',  
      port=3306,  
      user='root',  
      password='jason123', 
      database='jp04_3',  
      charset='utf8',
      autocommit=True  # 自动二次确认
  )  
修改表SQL语句
# 1.修改表的名字  rename
	alter table t1 rename ttt;
    
# 2.添加字段		 add
	alter table ttt add pwd int;  '''默认是尾部追加字段'''
	alter table ttt add tid int after name;  '''指定追加位置'''
  alter table ttt add nid int first;  '''指定头部添加字段'''
# 3.修改字段	change(名字类型都可)/modify(只能改类型不能改名字)
	alter table ttt change pwd password tinyint;
    
# 4.删除字段			 drop
	alter table ttt drop nid;
视图
# 视图的概念
视图是虚拟的表
# 视图的创建
	create view 视图名 as sql语句
    
'''视图虽然看似很好用 但是会造成表的混乱 毕竟视图不是真正的数据源
视图只能用于数据的查询 不能做增、删、改的操作 可能会影响原始数据(视图里面的数据是直接来源于原始表 而不是拷贝一份)   '''

触发器
# 触发器概念
	在对表数据进行增、删、改的具体操作下,自动触发的功能
    
# 触发器作用
	专门针对表数据的操作 定制个性化配套功能
    
# 触发器种类
	表数据新增之前、新增之后
	表数据修改之前、修改之后
	表数据删除之前、删除之后  
    
# 触发器创建
	create trigger 触发器名字 before/after insert/update/delete
	on 表名 for each row
		begin
			SQL语句
		end
# 触发器的命名规范
    tri_after_insert_t1
    tri_before_update_t2
    tri_before_delete_t3
    '''更直观的看出触发器的作用'''
    
# 查看当前库下所有的触发器信息
    	show triggers\G;
    
# 删除当前库下指定的触发器信息
    	drop trigger 触发器名称;
触发器具体案例
创建表
'''
临时修改SQL语句的结束符
	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
    );
需求
cmd表插入数据的success如果值为no 则去errlog表中插入一条记录
    delimiter $$  # 将mysql默认的结束符由;换成$$
    create trigger tri_after_insert_cmd after insert on cmd for each row
    begin
        if NEW.success = 'no' then  # 新记录都会被MySQL封装成NEW对象
            insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);
        end if;
    end $$
    delimiter ;  # 结束之后记得再改回来,不然后面结束符就都是$$了
  3.仅仅往cmd表中插入数据
  	INSERT INTO cmd (
          USER,
          priv,
          cmd,
          sub_time,
          success
    )VALUES
        ('kevin','0755','ls -l /etc',NOW(),'yes'),
        ('kevin','0755','cat /etc/passwd',NOW(),'no'),
        ('kevin','0755','useradd xxx',NOW(),'no'),
        ('kevin','0755','ps aux',NOW(),'yes');

事务
# 事务的概念
数据库事务(Database Transaction),是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。
简单的说:事务就是将一堆的SQL语句(通常是增删改操作)绑定在一起执行,要么都执行成功,要么都执行失败,即都执行成功才算成功,否则就会恢复到这堆SQL语句执行之前的状态。
事务的四个特性ACID
# A:原子性(Atomicity)
一个事务是一个不可分割的整体 里面的操作要么都成立要么都不成立
# C:一致性(Consistency)
事务必须使数据库从一个一致性状态变到另外一个一致性状态
# I:隔离性(Isolation,又称独立性)
并发编程中 多个事务之间是相互隔离的 不会彼此干扰
# D:持久性(Durability)
事务一旦提交 产生的结果应该是永久的 不可逆的
隔离级别
# 读未提交(Read uncommitted) 
安全性最差,可能发生并发数据问题,性能最好
# 读提交(read committed) 
Oracle默认的隔离级别
# 可重复读(repeatable read)
MySQL默认的隔离级别,安全性较好,性能一般
# 串行化(Serializable) 
表级锁,读写都加锁,效率低下,安全性高,不能并发
事务具体案例
创建表录入数据
create table user(
      id int primary key auto_increment,
      name char(32),
      balance int
      );
    insert into user(name,balance)
      values
      ('jason',1000),
      ('kevin',1000),
      ('tank',1000);
事务的操作
# 开启一个事务的操作
    	start transaction;
    
# 编写SQL语句(同属于一个事务)
update user set balance=900 where name='jason';
update user set balance=1010 where name='kevin'; 
update user set balance=1090 where name='tank';
# 事务回滚(返回执行事务操作之前的数据库状态)
    	rollback;  # 执行完回滚之后 事务自动结束
    
# 事务确认(执行完事务的主动操作之后 确认无误之后 需要执行确认命令)
    	commit;  # 执行完确认提交之后 无法回滚 事务自动结束
    
'''
事务操作步骤:
开启事务:start transaction;
需要执行的SQL语句集
结束事务:commit(提交事务)或rollback(回滚事务)
'''

存储过程
# 存储过程的概念
本质上就是将一段SQL代码封装起来,用于完成特定的操作,在使用时只需通过储存过程的名称调用即可(如果存储过程需要参数的话还需要传递对应的参数)
# 类似于有参函数
  delimiter $$
  create procedure p1(
      in m int,  # in表示这个参数必须只能是传入不能被返回出去
      in n int,  
      out res int  # out表示这个参数可以被返回出去,还有一个inout表示即可以传入也可以被返回出去
  )
  begin
      select tname from userinfo where id > m and id < n;
      set res=0;  # 用来标志存储过程是否执行
  end $$
  delimiter ;
# 针对res需要先提前定义
  set @res=10;  定义
  select @res;  查看
  call p1(1,5,@res)  调用
  select @res  查看
查看存储过程具体信息
	show create procedure pro1;
查看所有存储过程
	show procedure status;
删除存储过程
	drop procedure pro1;
无参无返回值的存储过程
# 相当于定义函数
delimiter $$
create procedure p1()
begin
	select * from cmd;
end $$
delimiter ;
# 相当于调用函数
call p1()
带参数的存储过程
# 类似于有参函数
  delimiter $$
  create procedure p1(
      in m int,  # in表示这个参数必须只能是传入不能被返回出去
      in n int,  
      out res int  # out表示这个参数可以被返回出去,还有一个inout表示即可以传入也可以被返回出去
  )
  begin
      select tname from userinfo where id > m and id < n;
      set res=0;  # 用来标志存储过程是否执行
  end $$
  delimiter ;
# 针对res需要先提前定义
  set @res=10;  定义
  select @res;  查看
  call p1(1,5,@res)  调用
    
    
'''
查看存储过程具体信息
	show create procedure pro1;
查看所有存储过程
	show procedure status;
删除存储过程
	drop procedure pro1;
'''
函数
'''
mysql内置的函数只能在sql语句中使用
可以通过help 函数名   查看帮助信息
'''
# 1.移除指定字符
Trim、LTrim、RTrim
# 2.大小写转换
Lower、Upper
# 3.获取左右起始指定个数字符
Left、Right
# 4.返回读音相似值(对英文效果)
Soundex
"""
eg:客户表中有一个顾客登记的用户名为J.Lee
		但如果这是输入错误真名其实叫J.Lie,可以使用soundex匹配发音类似的
		where Soundex(name)=Soundex('J.Lie')
"""
# 5.日期格式:date_format
'''在MySQL中表示时间格式尽量采用2022-11-11形式'''
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');
1.where Date(sub_time) = '2015-03-01'
2.where Year(sub_time)=2016 AND Month(sub_time)=07;
# 更多日期处理相关函数 
	adddate	增加一个日期 
	addtime	增加一个时间
	datediff	计算两个日期差值
  ...
流程控制
# python if判断
	if 条件:
    子代码
  elif 条件:
    子代码
  else:
    子代码
# js if判断
	if(条件){
    子代码
  }else if(条件){
    子代码
  }else{
    子代码
  }
# MySQL if判断
	if 条件 then
        子代码
  elseif 条件 then
        子代码
  else
        子代码
  end if;
  
# MySQL while循环
	DECLARE num INT ;
  SET num = 0 ;
  WHILE num < 10 DO
    SELECT num ;
    SET num = num + 1 ;
  END WHILE ;
索引
# 索引的概念
索引就是一种数据结构
类似于书的目录 意味着以后再查数据应该先找目录再找数据,而不是用翻页的方式查询数据
# 索引的优缺点
优点:提高数据检索的效率,降低数据排序的成本。
缺点:会降低更新表的速度。
索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构
  primary    key 	主键
  unique	key		唯一键
  index 	key     索引键
上面三种key前两种除了有加速查询的效果之外还有额外的约束条件(primary key:非空且唯一,unique key:唯一),而index key没有任何约束功能只会帮你加速查询
'foreign key不是用来加速查询用的,不在我们研究范围之内'
# 索引的基本用法
id	name	pwd		post_comment  addr  age 
	基于id查找数据很快 但是基于addr查找数据就很慢 
  	解决的措施可以是给addr添加索引
'''索引虽然好用 但是不能无限制的创建!!!'''
索引的底层数据结构是b+树
	b树 红黑树 二叉树 b*树 b+树
  	上述结构都是为了更好的基于树查找到相应的数据
聚焦索引
聚集索引是将主键与行记录存储在一起,当根据主键进行查询时,可直接在表中获取到数据,不用回表查询。InnonDB的所有的表都是索引组织表,主键与数据存放在一起。InnoDB选择聚集索引遵循以下原则:
在创建表时,如果指定了主键,则将其作为聚集索引。
如果没有指定主键,则选择第一个NOT NULL(非空)的唯一索引作为聚集索引。
如果没有唯一索引,则内部会产生一个6字节的rowID(主键值)作为主键 
辅助索引
# 辅助索引
查询数据的时候不可能都是用id作为筛选条件,也可能会用name,password等字段信息,那么这个时候就无法利用到聚集索引的加速查询效果。就需要给其他字段建立索引
优势:显示指定的主键可以是普通的int类型,这样存储空间就是4字节,在二级索引的叶子结点中存储主键的所占用空间就会变小
'''
叶子结点存放的是辅助索引字段对应的那条记录的主键的值(比如:按照name字段创建索引,那么叶子节点存放的是:{name对应的值:name所在的那条记录的主键值})
数据查找 如果一开始使用的是辅助索引 那么还需要使用聚焦索引才可以获取到真实数据
'''
覆盖索引
#覆盖索引:
只在辅助索引的叶子节点中就已经找到了所有我们想要的数据
select name from user where name='jason';
#非覆盖索引:
虽然查询的时候命中了索引字段name,但是要查的是age字段,所以还需要利用主键才去查找
select age from user where name='jason';
