之前一直用的python2.7版本,用到的是MySQLdb这个模块。然而又经常在资料中有看到连接mysql使用的是pymysql模块,于是就好奇这两个到底有什么不同?于是,对这两个模块进行比较,看看究竟有什么区别。最后发现,其实差别不大,使用方法上几乎相同。
¶MySQLdb与PyMySQL的相关介绍MySQLdb只支持py2.x版本,而pymysql支持2.7以及3.x版本
¶MySQLdb目前支持MySQL版本3.23 - 5.5 并且 Python版本2.4 - 2.7
安装命令 pip install MySQL-python
MySQLdb 是基于_mysql 的python简易包装器(官方文档中的用的是thin wapper,不知道这样翻译对不对),它能兼容 Python DB API interface (version 2)[PEP-0249],所以大部分使用方式还是根据[PEP-0249]定义。只有MySQLdb模块中与接口定义不同的时候,参考MySQLdb模块文档[MySQLdb]。其实MySQLdb只是定义了一些顶层的一些函数和属性,底层的很多东西在_mysql。_mysql 模块
如果要编写可跨数据库移植的应用,使用 MySQLdb模块而避免直接使用_mysql模块,因为我们使用MySQLdb模块基本上使用的是高级别的函数。_mysql 是基于实现MySQL C API的接口,更多内容参见 MySQL documentation。
所以第3点可以概括为,MySQLdb实现了 Python 数据库 API 规范 V2.0,基于 MySQL C API 上建立的。
¶PyMySQL安装环境python版本
CPython : 2.7 and >= 3.4
PyPy : Latest version
MySQL Server版本
MySQL >= 5.5
安装命令 pip install pymysql
pymysql是基于PEP 249 的纯python MySQL客户端库。大多数的API与mysqlclient 和MySQLdb兼容。注意的是,pymysql并不支持低级别的_mysql接口。
因此,pymysql基本可以代替MySQLdb,解决因为python版本带来的问题。
¶含有格式化操作符的sql语句的执行
由于含有格式化操作符的sql语句经常出现各种写法,因此这里做一个总结。下面我们先建立链接,进而创建一个table。1
2
3
4
5
6
7
8
9import MySQLdb
conn = MySQLdb.connect(host='localhost',
user = 'root',
passwd ='',
db = 'user_information',
charset='utf8')
#实参均是字符串形式
cursor = conn.cursor()1
2
3
4
5
6
7import pymysql
connection = pymysql.connect(host='localhost',
user='root',
password='',
db='db',
charset='utf8')
cursor = conn.cursor()
这里我们写出两个模块的connect()操作,可以发现,MySQLdb密码的参数名是passwd。这个自己写的时候容易出错。稍微提一下哈。
然后我们在连接上创建数据表,如下:1
2
3
4
5
6
7
8
9
10
11def ():
sql = """CREATE TABLE client(
client_id int auto_increment,
name varchar(20),
age int,
phone char(11),
PRIMARY KEY(client_id)
)
"""
cursor.execute(sql)
#后面的操作均在本表的基础上进行
以上都是铺垫,下面才正式进入sql语句的执行部分。
如果我们经常Ctrl C + V的话,基本很难发现这个小细节。因为这跟字符串匹配有点类似。如果不参照各种模板,基本上按照字符串匹配符和参数的写法写的。我自己写出来的sql语句就是这样的:1
2
3sql = "INSERT INTO client(name,age,phone)
VALUES(%s,%d,%s)" % ('converse', 27 ,'12345654321')
cursor.execute(sql)
。。很显然,这就心塞的报错了。(不就是一个插入语句么,怎么就会错?目前问题出现的原因不详。)
使用mysqldb模块:
使用pymysql模块:
就说一下该情况出现的解决方式吧。
其实百度一下也很容易解决,用 ’ ’ 将字符串格式化操作符包裹起来就正确了。那么含有字符串格式化操作符的sql语句的格式该如何进行书写?下面进行总结。(不论使用哪个模块,均可采用以下两种方式执行含有字符串格式化操作符的sql语句,因此是通用的不区分模块进行书写)
执行含有字符串格式化操作符的sql语句有以下两种方式:
¶sql语句的拼接方式采用%1
2
3sql = "INSERT INTO client(name,age,phone)
VALUES('%s','%d','%s')" % ('converse', 27 ,'12345654321')
cursor.execute(sql)
可以看到,格式化操作符如:%d,%s均被 ’ '包裹起来变成 ‘%d’, ‘%s’。
采用 .format()1
2
3sql = "INSERT INTO client(name,age,phone)
VALUES('{0}','{1}','{2}')".format('converse', 27 ,'12345654321')
cursor.execute(sql)
可以看到,如:{0}, {1}均被 ’ '包裹起来变成 ‘{0}’, ‘{1}’。
在执行,可以看到,数据被成功插入到数据表中。然而后面可以验证,这种方式是存在SQL注入的隐患。
¶sql语句的传参方式1
2sql = "INSERT INTO client(name,age,phone) VALUES (%s, %d, %s)"
cursor.execute(sql,('converse', 27 ,'12345654321'))
这种方式是以参数传递的方式先组合sql语句再执行组合后的sql。在官方的pymysql模块例子中,sql语句执行也是采用传参的方式,可以点击查看demo。
¶防SQL注入sql注入原理就是用户输入动态的构造了意外sql语句,造成了意外结果,是攻击者有机可乘。
下面我们在pymysql模块中进行操作,两种sql方式之间的差别。拼接方式
首先定义一个查询函数如下:1
2
3
4
5
6
7
8def select_data_Withcatch():
#方式1:拼接方式
sql = "SELECT name,age,phone FROM client where name = '%s'"%("'or1='1")
res = cursor.execute(sql)#返回的是结果集的数目
print res
results = cursor.fetchall()#返回所有结果
for i in results:
print i
通过拼接后的sql语句可以表示为:1sql = "SELECT name,age,phone FROM client where name = ''or1= '1'"
由于1='1’总是为真,所以sql等价于select name,age,phone from client,因此该函数的执行结果如下,返回表中所有数据信息。
传参方式1
2
3
4
5
6
7
8
9
10
11def select_data_withParameters():
#方式2 传入参数方式
sql = "SELECT name,age,phone FROM client where name = %s"
res = cursor.execute(sql, "'or'1")
print cursor.mogrify(sql, "'or'1")
#内部执行参数化生成的SQL语句,对特殊字符进行了加转义,避免注入语句生成。打印出来实际对MYSQL执行的sql语句
print res
results = cursor.fetchall()
print results
for i in results:
print i
我们先看执行结果,同样的输入,采用传参方法可以避免SQL注入。
因为,通过参数生成SQL语句对特殊字符进行了加转义,避免注入语句生成。其中cursor.mogrify()打印出来的是实际对数据库进行操作的SQL语句。该方法能够返回调用execute()方法真正操作数据库的SQL语句。
该方法只存在于pymysql模块中。
以上就是两个模块的总结,以及经常执行sql语句的两种方式总结。以后就知道什么时候带 ’‘ 什么时候不带 ’‘。再也不搞混了。
总结:pymysql更好,不存在Python版本问题,用法兼容MySQLdb
含有字符串格式化操作符的sql语句,采用传参方式更安全
参考链接:
[在使用pymysql对mysql进行操作时,使用%s给excute传入参数时出错]----> https://www.jianshu.com/p/855fdb50c26c