您的位置:首页 > 数据库 > MySQL

MySQL高级笔记——Pymysql

2019-01-29 02:12 375 查看
版权声明:欢迎转载~ 转载请注明来源及作者,谢谢! https://blog.csdn.net/qq_42442369/article/details/86684942

pymyql是Python中操作MySQL的模块,支持python3,用法和MySQLdb一样,不过后者不支持3.x版本。

pymysql版本:0.8.0
python版本:3.6.3
mysql版本:5.7.19

1. 安装

pip install pymysql

2. 使用操作

2.1 执行SQL

import pymysql
"""
python 连接数据库,任意数据库:
1、建立连接
2、获取操作的游标
3、调用对应的api函数,譬如 execute, 执行 SQL 语句
4、提交 commit
5、关闭游标
6、关闭连接
"""
# 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='mysql1802', charset='utf8')
# 创建游标
cursor = conn.cursor()

# 执行SQL,并返回收影响行数
"""
select 查询数据记录
"""
# effect_row = cursor.execute("select * from emp")
# print(effect_row)
# l_1 = cursor.fetchone()
# print(l_1)
# l_all = cursor.fetchall()
# print(l_all)

"""
带参数的查询
fetchmany ,参数 size, 需要获取的记录数,超过总记录条数,不会报错,只是返回所有记录
"""
# cursor.execute("select * from emp where deptno=%s", (30,))
# for emp in cursor.fetchmany(20):
#     print(emp)

"""
update
"""
# effect_row = cursor.execute("update emp set sal = sal + 100 where empno = %s", (7499,))
# print(effect_row)

"""
insert
插入一条和多条

SQL 语句:
insert into emp(empno, ename, job, mgr, hiredate, sal, comm, deptno) values
('9996', 'test1', 'CLERK', '7782', '1988-05-03 00:00:00', '2466.34', '59.66', '10'),
('9997', 'test2', 'CLERK', '7782', '1988-02-03 00:00:00', '3466.34', '59.66', '10')
在python中:
cursor.executemany("insert into emp(empno, ename, job, mgr, hiredate, sal, comm, deptno) values(%s,%s,%s,%s,%s,%s,%s,%s)",
[('9996', 'test1', 'CLERK', '7782', '1988-05-03 00:00:00', '2466.34', '59.66', '10'),
('9997', 'test2', 'CLERK', '7782', '1988-02-03 00:00:00', '3466.34', '59.66', '10')])
"""
# 执行 insert 一条记录
# effect_row = cursor.execute("insert into emp(empno, ename, job, mgr, hiredate, sal, comm, deptno) values(%s,%s,%s,%s,%s,%s,%s,%s)",
#                                 ('9992', 'test0', 'CLERK', '7782', '1988-01-03 00:00:00', '2266.34', '55.66', '10'))
# print(effect_row)

# insert 多条记录
# effect_row = cursor.executemany("insert into emp(empno, ename, job, mgr, hiredate, sal, comm, deptno) values(%s,%s,%s,%s,%s,%s,%s,%s)",
#                                 [('9996', 'test1', 'CLERK', '7782', '1988-05-03 00:00:00', '2466.34', '59.66', '10'),
#                                  ('9997', 'test2', 'CLERK', '7782', '1988-02-03 00:00:00', '3466.34', '59.66', '10')])
# print(effect_row)

"""
delete
"""
effect_row = cursor.execute("delete from emp where empno=%s",(9992,))
print(effect_row)

effect_row = cursor.executemany("delete from emp where empno=%s", ((9996,), (9997,)))
print(effect_row)

# 提交,不然无法保存新建或者修改的数据
conn.commit()

# 关闭游标, 一定是先关闭游标,再关闭 connection
cursor.close()
# 关闭连接
conn.close()

注意:存在中文的时候,连接需要添加charset=‘utf8’,否则中文显示乱码。

2.2 执行SQL

import pymysql

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='mysql1802', charset='utf8')
cursor = conn.cursor()
cursor.execute("select * from t_student")

# 获取剩余结果的第一行数据
row_1 = cursor.fetchone()
print(row_1)
# 获取剩余结果前n行数据
# row_2 = cursor.fetchmany(3)

# 获取剩余结果所有数据
# row_3 = cursor.fetchall()

conn.commit()
cursor.close()
conn.close()

2.3 获取新创建数据自增ID

可以获取到最新自增的ID,也就是最后插入的一条数据ID

"""
获取 insert 后的最新的自增id
"""
import pymysql

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='mysql1802')
cursor = conn.cursor()
effect_row = cursor.execute("insert into t_student(stuNo, stuName) values(%s,%s)",
('1088', 'test0'))

conn.commit()
# 获取自增id
new_id = cursor.lastrowid
"""
如果 insert 的表,是以 id 作为主键进行update和delete的,
这个 lastrowid 对于 单条 数据的插入,具有很大意义
因为我们后续的对本条记录的操作都是 类似如下:
delete from t_student where id = new_id的值
update t_student set 字段=新的值 where id = new_id的值

不是用 lastrowid,必须手动使用如下方式,但是此方式有可能 幻读
select id from t_student order by id desc limit 1
select max(id) from t_student

使用如下方式查找,通过刚刚insert的数据来获取对应的id
但是 insert 的记录有可能不唯一,取出多条记录来
获取多条记录中最大的,不是还是有 幻读 的可能
cursor.execute('select id from t_student where stuNo=%s and stuName=%s', ('1088', 'test0'))

要完全避免 幻读 ,必须设置 事务 的隔离级别为 最高 串行化
"""

print(new_id)

cursor.close()
conn.close()

2.4 移动游标

操作都是靠游标,那对游标的控制也是必须的

注:在fetch数据时按照顺序进行,可以使用cursor.scroll(num,mode)来移动游标位置,如:
cursor.scroll(1,mode=‘relative’) # 相对当前位置移动
cursor.scroll(2,mode=‘absolute’) # 相对绝对位置移动

import pymysql

"""
移动游标的位置

"""
# 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='mysql1802', charset='utf8')
# 创建游标
cursor = conn.cursor()

effect_row = cursor.execute("select * from emp")
print(cursor.fetchone())

# 相对移动,往后移动 1 步
cursor.scroll(-1, mode='relative')
print(cursor.fetchone())

# 相对移动,往前移动 2 步
cursor.scroll(2, mode='relative')
print(cursor.fetchone())

# 绝对移动,下标从 0 开始
cursor.scroll(0, mode='absolute')
print(cursor.fetchone())

# 提交,不然无法保存新建或者修改的数据
conn.commit()

# 关闭游标, 一定是先关闭游标,再关闭 connection
cursor.close()
# 关闭连接
conn.close()

2.5. fetch数据类型

关于默认获取的数据是元祖类型,如果想要或者字典类型的数据,即:

"""
默认返回的是 元组
可以修改为 dict
字典的 key 是字段名,所以 复杂的sql语句,需要使用 as 来重命名字段名
"""
import pymysql

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='mysql1802', charset='utf8')
# 游标设置为字典类型
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("select * from emp")

row_1 = cursor.fetchone()
print(row_1)

conn.commit()
cursor.close()
conn.close()

2.6 调用存储过程

a、调用无参存储过程

import pymysql

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='mysql1802')
# 游标设置为字典类型
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 无参数存储过程
cursor.callproc('p2')
# 等价于
# cursor.execute("call p2()")

row_1 = cursor.fetchone()
print(row_1)

conn.commit()
cursor.close()
conn.close()

b、调用有参存储过程

""""
调用带参数的存储过程
"""
import pymysql

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='mysql1802')
# 游标设置为字典类型
cursor = conn.cursor()
# 无参数存储过程
cursor.callproc('p1', args=(10,))

row_1 = cursor.fetchone()
print(row_1)

conn.commit()
cursor.close()
conn.close()

3 关于pymysql防注入

3.1 字符串拼接查询,造成注入

正常查询语句:

import pymysql

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='mysql1802')
cursor = conn.cursor()
user="u1"
passwd="u1pass"
#正常构造语句的情况
sql="select user,pass from t_student where user='%s' and pass='%s'" % (user,passwd)
#sql=select user,pass from t_student where user='u1' and pass='u1pass'
row_count=cursor.execute(sql) row_1 = cursor.fetchone()
print row_count,row_1

conn.commit()
cursor.close()
conn.close()

构造注入语句:

"""
SQL 注入,正常用户的实现

create table puser(
id int auto_increment primary key,
username varchar(100),
pwd varchar(100)
)

insert into puser(username, pwd) values('admin', 'admin');
"""

import pymysql

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='mysql1802')
cursor = conn.cursor()
user = "admin"
passwd = "admin"
# 正常构造语句的情况
sql = "select username, pwd from puser where username='%s' and pwd='%s'" % (user, passwd)

# 验证通过,该用户名和密码存在
row_count = cursor.execute(sql)
row_1 = cursor.fetchone()
print(row_count, row_1)

conn.commit()
cursor.close()
conn.close()

3.2 避免注入,使用pymysql提供的参数化语句

正常参数化查询

"""
恶意构造SQL注入

通过这个示例告诉大家,一定不能拼接SQL

拼接后的SQL是:
select username, pwd from puser where username='u1' or '1' -- ' and pwd='123456789'
"""

import pymysql

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='mysql1802')
cursor = conn.cursor()
user = "u1' or '1'-- "
passwd = "123456789"
# 正常构造语句的情况
sql = "select username, pwd from puser where username='%s' and pwd='%s'" % (user, passwd)

# 验证通过,该用户名和密码存在
row_count = cursor.execute(sql)
row_1 = cursor.fetchone()
print(row_count, row_1)

conn.commit()
cursor.close()
conn.close()

构造注入,参数化查询注入失败

"""
通过这样的参数提交,不会被SQL注入
非法字符会被自动添加 \ 转义
select username, pwd from puser where username='u1\' or \'1\' -- ' and pwd='123456789'
"""
import pymysql

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='mysql1802')
cursor = conn.cursor()
user = "u1' or '1'-- "
passwd = "123456789"
# 正常构造语句的情况
sql = "select username, pwd from puser where username='%s' and pwd='%s'"

# 验证通过,该用户名和密码存在
row_count = cursor.execute(sql, args=(user, passwd))
row_1 = cursor.fetchone()
print(row_count, row_1)

conn.commit()
cursor.close()
conn.close()

结论:excute执行SQL语句的时候,必须使用参数化的方式,否则必然产生SQL注入漏洞。

4. 使用with简化连接过程

每次都连接关闭很麻烦,使用上下文管理,简化连接过程

import pymysql
import contextlib

"""
使用上下文管理器
"""

# 定义上下文管理器,连接后自动关闭连接
@contextlib.contextmanager
def mysql(host='127.0.0.1', port=3306, user='root', passwd='123456', db='mysql1802', charset='utf8'):
conn = pymysql.connect(host=host, port=port, user=user, passwd=passwd, db=db, charset=charset)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
try:
yield cursor
finally:
conn.commit()
cursor.close()
conn.close()

# 执行sql
with mysql() as cursor:
print(cursor)
row_count = cursor.execute("select * from t_student")
row_1 = cursor.fetchone()
print(row_1)

项目:

1、实现 scrapyd 的其他几个接口
2、改造一个scrapy项目,将数据存储从 mongodb该到 mysql 数据库中

至少至少50%时间调试;

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: