18-pymysql 操作mysql数据库详解

阿里云服务器优惠

什么是 PyMySQL

PyMySQL 是在 Python3.x 版本中用于连接 MySQL 服务器的一个库,Python2中则使用mysqldb。

PyMySQL 安装

pip install PyMySQL

连接数据官方案例

# 表结构CREATE TABLE `users` (  
`id` int(11) NOT NULL AUTO_INCREMENT,  # id 整型 不能为空 自动增长   
`email` varchar(255) COLLATE utf8_bin NOT NULL,  # 邮箱 可变字符串 区分大小写,不能为空    `password` varchar(255) COLLATE utf8_bin NOT NULL,  # 密码 可变字符串 区分大小写,不能为空  
PRIMARY KEY (`id`)  # id 为主键) 
ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
AUTO_INCREMENT=1 ;
# InnoDB 引擎 默认 utf-8 编码 区分大小写 自动增长从1开始

python操作

import pymysql.cursors
connection = pymysql.connect(host='localhost',                                                            
                             user='user',                                                            
                             password='passwd',                             
                             db='db',                             
                             charset='utf8mb4',                             
                             cursorclass=pymysql.cursors.DictCursor)
try:   
with connection.cursor() as cursor:       
# 创建一条新的记录        
sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)"        cursor.execute(sql, ('webmaster@python.org', 'very-secret'))
    # 连接完数据库并不会自动提交,所以需要手动 commit 你的改动    connection.commit()
    with connection.cursor() as cursor:      
# 读取单条记录    

sql = "SELECT `id`, `password` FROM `users` WHERE `email`=%s"        
              cursor.execute(sql, ('webmaster@python.org',))       
               result = cursor.fetchone()     
              print(result)
finally:  
              connection.close()

这里注意连续用了两处 with 好处就在于 with 结束后会自动 close cursor 而免去了 cursor.close()

输出

{'password': 'very-secret', 'id': 1}

数据库连接

connection = pymysql.connect(host='localhost',                              
                             user='user',                             
                             password='passwd',                             
                             db='db',                             
                             charset='utf8mb4',                             
                             cursorclass=pymysql.cursors.DictCursor)

这里除了 host、user、password 等还有很多参数可以选择 详见

参数 解释
host 数据库服务器地址,默认 localhost
user 用户名,默认为当前程序运行用户
password 登录密码,默认为空字符串
database 默认操作的数据库
port 数据库端口,默认为 3306
charset 数据库编码
connect_timeout 连接超时时间,默认 10,最小 1,最大 31536000
max_allowed_packet 发送给服务器的最大数据量,默认为 16MB

游标

连接完数据库,接着就该获取游标,之后才能进行执行、提交等操作 cursor = connection.cursor()

查询时,默认返回的数据类型为元组,可以修改返回类型 几种常用游标类型:

Cursor: 默认,元组类型•DictCursor: 字典类型•SSCursor: 无缓冲元组类型•SSDictCursor: 无缓冲字典类型

无缓冲游标类型,适用于数据量很大,一次性返回太慢,或者服务端带宽较小

创建连接时,通过cursorclass 参数指定类型:

connection = pymysql.connect(host='localhost',                             
                             user='root',                             
                             password='root',                             
                             db='db',                             
                             charset='utf8',                             
                             cursorclass=pymysql.cursors.DictCursor)

也可以在创建游标时指定类型:

cursor = connection.cursor(cursor=pymysql.cursors.DictCursor)

创建数据库表

如果数据库连接存在我们可以使用execute()方法来为数据库创建表,如下所示创建表EMPLOYEE:

import pymysql
# 打开数据库连接
db = pymysql.connect("localhost","testuser","test123","TESTDB" )
# 使用 cursor() 方法创建一个游标对象 
cursorcursor = db.cursor()
# 使用 execute() 方法执行 SQL,如果表存在则删除
cursor.execute("DROP TABLE IF EXISTS EMPLOYEE")
# 使用预处理语句创建表
sql = """CREATE TABLE EMPLOYEE (         
                FIRST_NAME  CHAR(20) NOT NULL,         
                LAST_NAME  CHAR(20),      
                AGE INT,      
                SEX CHAR(1),     
                INCOME FLOAT )"""
cursor.execute(sql)
# 关闭数据库连接db.close()

数据库插入操作

以下实例使用执行 SQL INSERT 语句向表 EMPLOYEE 插入记录:

import pymysql
# 打开数据库连接
db = pymysql.connect("localhost","testuser","test123","TESTDB" )
# 使用cursor()方法获取操作游标 
cursor = db.cursor()
# SQL 插入语句
sql = """INSERT INTO EMPLOYEE(FIRST_NAME,    
                LAST_NAME, AGE, SEX, INCOME)    
                VALUES ('Mac', 'Mohan', 20, 'M', 2000)"""
try:  
# 执行sql语句 
cursor.execute(sql) 
# 提交到数据库执行 
db.commit()except:  
# 如果发生错误则回滚 
db.rollback()
# 关闭数据库连接db.close()

以上例子也可以写成如下形式:

import pymysql
# 打开数据库连接
db = pymysql.connect("localhost","testuser","test123","TESTDB" )
# 使用cursor()方法获取操作游标 
cursor = db.cursor()
# SQL 插入语句
sql = "INSERT INTO EMPLOYEE(FIRST_NAME, \   
                LAST_NAME, AGE, SEX, INCOME) \    
                VALUES ('%s', '%s',  %s,  '%s',  %s)" % \    
                ('Mac', 'Mohan', 20, 'M', 2000)
try:   # 执行sql语句  
cursor.execute(sql)  
# 执行sql语句  
db.commit()except: 
# 发生错误时回滚  
db.rollback()
# 关闭数据库连接
db.close()

数据库查询操作

  • Python查询Mysql使用 fetchone() 方法获取单条数据, 使用fetchall() 方法获取多条数据。

  • fetchone(): 该方法获取下一个查询结果集。结果集是一个对象•*fetchall(): *接收全部的返回结果行.

  • rowcount: 这是一个只读属性,并返回执行execute()方法后影响的行数。

实例:

查询EMPLOYEE表中salary(工资)字段大于1000的所有数据:

import pymysql
# 打开数据库连接
db = pymysql.connect("localhost","testuser","test123","TESTDB" )
# 使用cursor()方法获取操作游标
cursor = db.cursor()
# SQL 查询语句
sql = "SELECT * FROM EMPLOYEE \     
WHERE INCOME > %s" % (1000)
try: 
# 执行SQL语句  
cursor.execute(sql)  
# 获取所有记录列表 
                results = cursor.fetchall()  
                for row in results:    
                fname = row[0]     
                lname = row[1]    
                age = row[2]  
                sex = row[3]    
                income = row[4]     
                # 打印结果   
                print ("fname=%s,lname=%s,age=%s,sex=%s,income=%s" % \                      (fname, lname, age, sex, income ))
except: 
    print ("Error: unable to fetch data")
# 关闭数据库连接
db.close()

以上脚本执行结果如下:

fname=Mac, lname=Mohan, age=20, sex=M, income=2000

数据库更新操作

更新操作用于更新数据表的的数据,以下实例将 TESTDB 表中 SEX 为 ‘M’ 的 AGE 字段递增

import pymysql
# 打开数据库连接db = pymysql.connect("localhost","testuser","test123","TESTDB" )
# 使用cursor()方法获取操作游标 cursor = db.cursor()
# SQL 更新语句sql = "UPDATE EMPLOYEE SET AGE = AGE + 1 WHERE SEX = '%c'" % ('M')try:   # 执行SQL语句   cursor.execute(sql)   # 提交到数据库执行   db.commit()except:   # 发生错误时回滚   db.rollback()
# 关闭数据库连接db.close()

删除操作

删除操作用于删除数据表中的数据,以下实例演示了删除数据表 EMPLOYEE 中 AGE 大于 20 的所有数据:

import pymysql
# 打开数据库连接
db = pymysql.connect("localhost","testuser","test123","TESTDB" )
# 使用cursor()方法获取操作游标 
cursor = db.cursor()
# SQL 删除语句
sql = "DELETE FROM EMPLOYEE WHERE AGE > %s" % (20)
try:  
# 执行SQL语句  
cursor.execute(sql)  
# 提交修改  
db.commit()except:  
# 发生错误时回滚 
db.rollback()
# 关闭连接
db.close()

执行事务

事务机制可以确保数据一致性。

事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。

  • 原子性atomicity)。一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
  • 一致性consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
  • 隔离性isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性durability)。持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

Python DB API 2.0 的事务提供了两个方法 commit 或 rollback。

实例

# SQL删除记录语句
sql = "DELETE FROM EMPLOYEE WHERE AGE > %s" % (20)
try:  
# 执行SQL语句 
cursor.execute(sql) 
# 向数据库提交
db.commit()except:  
# 发生错误时回滚  
db.rollback()

对于支持事务的数据库, 在Python数据库编程中,当游标建立之时,就自动开始了一个隐形的数据库事务。

commit()方法游标的所有更新操作,rollback()方法回滚当前游标的所有操作。每一个方法都开始了一个新的事务。

两个close

一般用于最后结束对数据库的操作,可在 finally 中写

关闭数据库连接 connection.close()

关闭游标 cursor.close()

还有防止 sql 注入

通过操作输入来修改后台SQL语句

#正常构造语句的情况
sql="select user,pass from tb7 where user='%s' and pass='%s'" % (user,passwd)
row_count=cursor.execute(sql)
#拼接语句被构造成下面这样,永真条件,此时就注入成功了。
select user,pass from tb7 where user='u1' or '1'-- ' and pass='u1pass'

要避免这种情况可以使用 pymysql 提供的参数化查询

#
#避免注入,使用pymysql提供的参数化语句
user="u1' or '1'-- "passwd="u1pass"
#执行参数化查询
row_count=cursor.execute("select user,pass from tb7 where user=%s and pass=%s",(user,passwd))
# 内部执行参数化生成的SQL语句,对特殊字符进行了加\转义,避免注入语句生成。
sql=cursor.mogrify("select user,pass from tb7 where user=%s and pass=%s",(user,passwd))print(sql)
# 被转义的语句
select user,pass from tb7 where user='u1\' or \'1\'-- ' and pass='u1pass'