学习python,从入门到放弃(48)


学习python,从入门到放弃(48)

python操作MySQL

python中支持操作MySQL的模块很多,其中最常见的当属pymysql

因为其属于第三方模块,所以需要导入。

pip3 install pymysql

import pymysql

使用方法:

  • 1.链接服务端

    conn_obj = pymysql.connect(
        host='127.0.0.1',  # MySQL服务端的IP地址
        port=3306,  # MySQL默认PORT地址(端口号)
        user='root',  # 用户名
        password='jason123',  # 密码  也可以简写 passwd
        database='jp04_3',  # 库名称  也可以简写 db
        charset='utf8'  # 字符编码 千万不要加杠utf-8
    )  # 要善于查看源码获取信息
    
  • 2.产生获取命令的游标对象

    cursor = conn_obj.cursor(
        cursor=pymysql.cursors.DictCursor
    )  # 括号内不写参数 数据是元组要元组 不够精确 添加参数则会将数据处理成字典
    
  • 3.编写SQL语句

    sql1 = 'select * from teacher;'  # SQL语句会被高亮显示
    
  • 4.执行SQL语句

    affect_rows = cursor.execute(sql1)
    print(affect_rows)  # 执行SQL语句之后受影响的行数
    
  • 5.获取结果

    res = cursor.fetchall()
    print(res)
    

获取SQL语句执行的结果 跟读取文件内容的read方法几乎一致(光标)

fetchone()	
fetchmany()
fetchall()
cursor.scroll(1, 'relative')  # 相对于当前位置往后移动一个单位
cursor.scroll(1, 'absolute')  # 相对于起始位置往后移动一个单位

SQL注入问题

有时候我们会遇见一些奇怪的情况。

  • 写正确的用户名错误的密码也可以登录
  • 用户名和密码都不需要也可以登录

上述现象就是典型的SQL注入问题,利用的是MySQL注释语法及逻辑运算符

解决SQL注入的问题其实也很简单 就是想办法过滤掉特殊符号。execute方法自带校验SQL注入问题,自动处理特殊符号。设计到敏感数据的拼接,全部交给execute方法即可。

sql = "select * from userinfo where name=%s and password=%s;"
cursor.execute(sql, (name, password))

二次确认

数据的增删改查四个操作是有轻重之分的,

查:不会影响真正的数据,重要程度最低。

增、改、删:都会影响真正的数据,重要程度较高。

pymysql针对增、改、删三个操作,都设置了二次确认,如果不确认则不会真正影响数据库。

  • 代码直接编写

    affect_row = cursor.execute(sql)
    conn_obj.commit()  # 手动二次确认
    
  • 配置固定参数

    conn_obj = pymysql.connect(
    	autocommit=True  # 自动二次确认
    )
    

修改表SQL语句补充

  • 修改表的名字

    rename

    alter table t1 rename ttt;
    
  • 添加字段

    add

    alter table ttt add pwd int; # 默认是尾部追加字段
    alter table ttt add tid int after name; # 指定追加位置
    alter table ttt add nid int first; # 指定头部添加字段
    
  • 修改字段

    change(名字类型都可)/modify(只能改类型不能改名字)

    alter table ttt change pwd password tinyint;
    
  • 删除字段

    drop

    alter table ttt drop nid;
    

视图

通过SQL语句的执行得到的一张虚拟表,保存下来之后就称之为'视图'

如果需要频繁的使用一张虚拟表,可以考虑制作成视图,降低操作难度。

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

临时修改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 ;  # 结束之后记得再改回来,不然后面结束符就都是$$了
    
  • 仅仅往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');
    

查看当前库下所有的触发器信息

show triggers\G;

删除当前库下指定的触发器信息

drop trigger 触发器名称;

事务

事务可以包含诸多SQL语句并且这些SQL语句,要么同时执行成功,要么同时执行失败,这是事务的原子性特点。

事务有 ACID 四大特性

  • A:原子性

    一个事务是一个不可分割的整体,里面的操作要么都成立要么都不成立。

  • C:一致性

    事务必须使数据库从一个一致性状态变到另外一个一致性状态。

  • I:隔离性

    并发编程中,多个事务之间是相互隔离的,不会彼此干扰。

  • D:持久性

    事务一旦提交,产生的结果应该是永久的,不可逆的。

具体使用

  • 建表及录入数据

    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;  # 执行完确认提交之后 无法回滚 事务自动结束
    

存储过程

类似于python中的自定义函数

  • 定义函数

    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)  调用
  select @res  查看

查看存储过程具体信息

show create procedure pro1;

查看所有存储过程

show procedure status;

删除存储过程

drop procedure pro1;

函数

注意与存储过程的区别,mysql内置的函数只能在sql语句中使用

  • 移除指定字符

    Trim、LTrim、RTrim

  • 大小写转换

    Lower、Upper

  • 获取左右起始指定个数字符

    Left、Right

  • 返回读音相似值(对英文效果)

    Soundex

    客户表中有一个顾客登记的用户名为J.Lee,但如果这是输入错误真名其实叫J.Lie,可以使用soundex匹配发音类似的。

    where Soundex(name)=Soundex('J.Lie')
    
  • 日期格式

    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');
    
    where Date(sub_time) = '2015-03-01'
    
    where Year(sub_time)=2016 AND Month(sub_time)=07;
    
    • 更多日期处理相关函数

      adddate 增加一个日期
      addtime 增加一个时间
      datediff 计算两个日期差值

流程控制

  • if判断

    if 条件 then
            子代码
    elseif 条件 then
            子代码
    else
            子代码
    end if;
    
  • 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没有任何约束功能只会帮你加速查询

  • 索引的基本用法

    表中有一下几个属性

    id name pwd post_comment addr age

    其中id是主键

    基于id查找数据很快 但是基于addr查找数据就很慢

    解决的措施可以是给addr添加索引

索引虽然好用 但是不能无限制的创建

在表中有大量数据的前提下,创建索引速度会很慢。
在索引创建完毕后,对表的查询性能会大幅度提升,但是写的性能会降低

索引的底层数据结构是b+树
数据结构有b树、红黑树、二叉树、b*树、b+树
上述结构都是为了更好的基于树查找到相应的数据

只有叶子结点存放真实数据,根和树枝节点存的仅仅是虚拟数据
查询次数由树的层级决定,层级越低次数越少
一个磁盘块儿的大小是一定的,那也就意味着能存的数据量是一定的。如何保证树的层级最低呢?一个磁盘块儿存放占用空间比较小的数据项
我们应该给我们一张表里面的什么字段字段建立索引能够降低树的层级高度>>> 主键id字段

聚集索引(primary key)
辅助索引(unique key,index key)
查询数据的时候不可能都是用id作为筛选条件,也可能会用name,password等字段信息,那么这个时候就无法利用到聚集索引的加速查询效果。就需要给其他字段建立索引,这些索引就叫辅助索引

叶子结点存放的是辅助索引字段对应的那条记录的主键的值(比如:按照name字段创建索引,那么叶子节点存放的是:{name对应的值:name所在的那条记录的主键值})
数据查找 如果一开始使用的是辅助索引 那么还需要使用聚焦索引才可以获取到真实数据

覆盖索引:只在辅助索引的叶子节点中就已经找到了所有我们想要的数据

select name from user where name='jason';

非覆盖索引:虽然查询的时候命中了索引字段name,但是要查的是age字段,所以还需要利用主键才去查找

select age from user where name='jason';

相关