PyMySQL其它操作与数据库知识补充

PyMySQL其它操作与数据库知识补充

概要

  • pymysql其他操作

  • SQL注入

  • 事务

  • 用户管理

  • 索引

  • 辅助知识补充


详细

  • pymysql其他操作

pymysql.connect()中
    参数password支持简写passwd
    参数database支持简写db
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
1.游标对象默认可以执行查询操作
sql = 'select * from teacher'
2.游标对象执行涉及到操作数据的SQL语句不直接生效
sql = 'insert into teacher(tname) values("Leo")'
可以看到在数据库中数据并未增加

# 这是MySQL的一种安全机制,操作(即增删改)数据需要进行二次确认,在执行完后可利用代码提交
方法1:每次执行完操作数据后提交
    conn.commit()
方法2:pymysql.connect中添加自动确认参数
    autocommit=True
可以看到数据成功增加

 

游标对象有多种方法用来获取数据,以"select * from teacher"为例。

3.cursor.fetchall() #从结果中获取所有数据

4.cursor.fetchone() # 从结果中获取第一条数据

5.cursor.fetchmany(3) # 从结果中获取指定条数的数据

6.cursor.scroll(2,'relative') 
# 游标相对当前位置左右移动,第一个参数为正则向右,为负则向左
  cursor.scroll(1,'absolute') 
# 游标相对于起始位置移动,第一个参数为正则向右,为负则向左


  • SQL注入

# SQL语句的拼接(一次插入多条记录)
cursor.executemany(sql, [(记录1的参数),(记录2的参数)]
# 直接简单地拼接SQL语句可能会导致SQL注入问题
sql = "select * from userinfo where name=%s and password=%s"
1.只需要用户名正确即可通过验证
    select * from userinfo where name='jasonNB' -- ajsdkjaskldjklasd' and password=''
2.无论用户名密码是什么均可通过验证
    select * from userinfo where name='xxx' or 1=1 -- jkasdjaksldklsajd' and password=''
"""
    SQL注入的本质:利用一些特殊符号或特殊语法形式拼接处违背常理的语句
    应对:
        涉及到关键性的数据(用户名/密码等)不要手动拼接,使用能够过滤特殊符号的固定方法
"""

  • 事务

事物的四大特性:ACID
1.原子性---A
原子性是指事务包含的所有操作只能全部执行成功,只能全部执行失败并回滚,即成功指的是该事务涉及到的所有记录均更改成功,失败指的是该事务对数据库的数据无影响。
2.一致性---C
一致性是指一个事务执行之前和执行之后都必须处于一致性状态。例如转账,转账前后双方的金额和必须是相同的。
3.独立性---I
独立性是指当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰。例如转账,当一个用户需要转账给另一个用户的事务未结束时,第三方无法和这两个用户之间产生交易。
4.持久性---D
持久性是指一个事务被提交=以后,对数据库中的记录的改变就是永久性的,即使数据库系统遇到故障也不会丢失提交事务的操作。
eg:
create table user(
    id int primary key auto_increment,
    name char(32),
    balance int
);
insert into user(name,balance)
values
('jason',1000),
('kevin',1000),
('tony',1000);

事务操作步骤
1.先开启事务操作
start transaction;
2.进行修改操作
# 买支付100元
update user set balance=900 where name='jason';
# 中介拿走30元
update user set balance=1030 where name='kevin';
# 卖家拿到70元
update user set balance=1070 where name='tony';
3.这时可以进行回滚操作
rollback;
4.如果确认数据没有问题,不需要回滚,则可以提交事务
commit;

 

 


  • 用户管理

1.创建用户
create user 用户名 identified by '密码'
eg:create user Leo identified by 'ultramanleo007'
2.修改密码
set password for 用户名 = Password('新密码');# 指定用户
set password = Password('新密码');# 当前登录用户
3.重命名用户
rename user 旧用户名 to 新用户名; 
eg:rename user Zero to Leo;
4.删除用户
drop user 用户名;
5.查看用户访问权限
show grants for 用户名;
6.授予访问权限
grant select on db_dbcx.* to 用户名;
7.撤销权限
revoke select on db_dbcx.* from 用户名;
# 操作权限的单位
1.整个服务器:grant all/revoke all
2.整个数据库:on db.*
3.特定表:on db.t1


  • 索引

1.索引的概念
    类似于书的目录,一种能加快查询速度的数据结构。
2.索引也是键,能使存储引擎快速查询到记录
    主键  primary key
        可以加快查询,有其他的功能
    唯一键 unique
        可以加快查询,有其他的功能
    索引键 index key
        只能加快查询
    外键  foreign key
        与索引无关,不能提升查询速度
聚集索引
    即主键,叶子结点放的一条条完整的记录
辅助索引     即唯一键和索引键,叶子结点存放的是该字段对应的那条记录的主键值 覆盖索引     只在辅助索引的叶子结点就找到了想要的数据     
select name from user where name='Leo'; 非覆盖索引     在叶子结点可以查到记录对应的主键值,再使用主键的值通过聚集索引查找到需要的数据     select age from user where name='Leo'; 3.索引的使用 (1)在表中有大量数据的前提下,创建索引速度会很慢 (2)在索引创建完毕后,对表的查询性能会大幅度提升,但是写的性能会降低   一般什么字段查询慢就将该字段制作成索引,但有例外需要整体考虑

  • 其他辅助知识补充(了解为主)

视图

1.视图概念
用select查询得到的结果可被当成一张表,并被保存下来以供下次使用,该表称为视图
2.视图使用
使用视图可以避免重复查询同样的数据
3.语法
create view teacher2course as
select * from teacher inner join course on teacher.tid = course.teacher_id;
一般不推荐使用视图
    1.视图记录不会随着表的更改而更改
    2.创建过多视图会导致使用上的不便

触发器

1.触发器的概念
在满足对某张表数据的增、删、改的情况下,自动触发的功能。
2.触发器使用场景
对某一张表数据增删改行为执行前后需自动执行的额外操作,即另一段SQL语句。
3.语法
create trigger 触发器的名字 before/after insert/update/delete on 表名 for each row
begin# 删除触发器
    sql语句
end
4.触发器命名习惯 
eg:tri_before_insert_test01
即触发器简写_前或后_操作_表名
5.删除触发器
drop trigger tri_after_insert_cmd;

存储过程

1.存储过程的概念
SQL中的自定义函数
2.语法
# SQL语句需要由分号结尾,故定义存储过程需要先重定义结束符
delimiter $$
# 类似创建函数
create procedure p1()
# 存储过程需要执行的操作,前后需分别加begin和end
begin
    select * from cmd;
end $$
# 存储过程定义结束,将结束符修改回分号
delimiter ;
​
# 像调用函数一样调用存储过程
call p1()

数据库中的一些函数(用于存储过程)

1.可以在cmd终端使用help 函数名查看相关信息
2.移除指定字符
Trim、Lrim、Rrim
SELECT TRIM('  bar   ');                    # 'bar'
SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx');  # 'barxxx'
SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx');     # 'bar'
SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz'); # 'barx'
3.大小写转换
LowerUpper
4.获取左右指定个数字符
LeftRight
5.返回读音相似的值(英文环境效果明显)
eg: where Soundex(name)=Soundex('J.Lie')
6.日期格式 date_format(一般采用年--日)
eg:记录发布博客时间的表
create table blog (
    id int primary key auto_increment,
    name varchar(16),
    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');
其它时间函数
adddate 增加一个日期 
addtime 增加一个时间
datediff    计算两个日期差值

流程控制(用于存储过程)

1.分支结构 if
if i = 1 THEN
    SELECT 1;
ELSEIF i = 2 THEN
    SELECT 2;
ELSE
    SELECT 7;
END IF;
2.循环结构 while
SET num = 0 ;
WHILE num < 10 DO
    SELECT
        num;
    SET num = num + 1;
END WHILE;

作业

1、基于搜索查询并总结归纳“数据库三大设计范式”

为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库,必须满足一定的范式。
1.第一范式
    确保每列保持原子性
    eg: 不满足
        用户表(用户名,家庭地址)
        原因:家庭地址可以进一步细分为查询频率更改的省市
        满足
        用户表(用户名,省,城市,详细地址)
2.第二范式
    满足第一范式的同时,确保表中的每列都和主键相关而不是只和主键的某一部分相关(联合主键),即在一个数据库表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中
    eg: 不满足
        学生选课表(学号,学生姓名,课程编号,课程名称)
        原因:学号和课程编号作为联合主键,课程名称只依赖于课程编号,而和学号没有关系
        满足
        学生表(学号,学生姓名)
        课程表(课程编号,课程名称)
        选课表(学号,课程名称)
3.第三范式
    满足第二范式的同时,确保表中的每列都和主键直接相关,而不是间接相关
    eg:不满足
    学生班级表(学号,学生姓名,班级编号,班级名称,班级信息)
    满足
    原因:班级相关的列与主键(学号)没有直接关系
    学生表(学号,学生姓名,班级编号)
    班级表(班级编号,班级名称,班级信息)

2、基于pymysql实现用户注册登录

import pymysql

def getUserInfo():
    """获取输入的用户信息"""
    userName = input('请输入用户名:').strip()
    password = input('请输入密码:').strip()
    userInfo = {'userName': userName, 'password': password}
    return userInfo

def isNone(userInfo):
    """校验用户信息是否为空"""
    if userInfo.get('userName') == '' or userInfo.get('password') == '':
        print('用户名或密码为空,请重新输入!!!')
        return True
    return False

def createLink2MySQL():
    """创建连接"""
    conn = pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='ultra12345',
        database='db_dbcx',
        charset='utf8',
        # 自动提交
        autocommit=True
    )
    return conn

def operateUserInfo(sql, userInfo, conn):
    """操作用户信息表"""
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute(sql, userInfo)
    res = cursor.fetchall()
    return res

def register(conn):
    """注册"""
    # 1.获取用户信息
    userInfo = getUserInfo()
    # 2.判断用户信息是否为空
    if isNone(userInfo):
        return
    # 3.用户信息不为空,查询用户名是否已存在
    sql = 'select * from user_info where user_name=%s'
    realUserInfo = operateUserInfo(sql, userInfo.get('userName'), conn)
    if not realUserInfo:
        # 写入
        sql = 'insert into user_info(user_name,password) values(%s,%s)'
        res = operateUserInfo(sql, (userInfo.get('userName'), userInfo.get('password')), conn)
        print('用户{}注册成功!!!'.format(userInfo.get('userName')))
    else:
        print('用户名已存在,请重新输入!!!')
        return False
    return


def login(conn):
    """登录"""
    # 1.获取用户信息
    userInfo = getUserInfo()
    # 2.判断用户信息是否为空
    if isNone(userInfo):
        return
    # 3.用户信息不为空,查询是否有记录的用户名和密码均与输入的用户信息相同
    sql = 'select * from user_info where user_name=%s and password=%s'
    res = operateUserInfo(sql, (userInfo.get('userName'), userInfo.get('password')), conn)
    if not res:
        print('用户名或密码不正确!!!请重新输入')
        return
    else:
        print('用户{}登录成功!!!'.format(userInfo.get('userName')))
    return


"""主流程"""
funcDict = {'1': register, '2': login}
print('欢迎来到用户注册登录系统 ver4.1\n'
      '******** 1.注册 ***********\n'
      '******** 2.登录 ***********\n')
# 先建立数据库连接
conn = createLink2MySQL()
while True:
    funcNum = input('请输入想要执行的功能:').strip()
    if funcNum in funcDict:
        funcName = funcDict.get(funcNum)
        funcName(conn)
    else:
        print('该功能不存在,请重新选择!!!')
View Code

 

 

posted @ 2021-09-12 12:08  Leguan001  阅读(62)  评论(0)    收藏  举报