1、操作数据库是程序员常用的技能之一,mysql数据库是中小型项目的首选,Python标准数据库接口为Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口,Python数据库接口支持非常多的数据库,不同的数据库你需要下载不同的DB API模块,例如你需要访问Oracle数据库和Mysql数据,你需要下载Oracle和MySQL数据库模块。
2、DB-API 是一个规范. 它定义了一系列必须的对象和数据库存取方式, 以便为各种各样的底层数据库系统和多种多样的数据库接口程序提供一致的访问接口。如果想Python3操作MySQL,就需要安装MySQL的驱动PyMySQL:pip install PyMySQL
1、引入API模块
2、获取与数据库的连接:数据库连接对象和游标对象
3、执行SQL语句和存储过程
4、关闭数据库连接
为了使用基础数据库系统,首先必须连接它。连接数据库需要使用恰当名称的connect函数。该函数有多个参数,具体使用哪个参数需要根据数据库类型进行选择。DB-API定义了下表所示的参数作为准则(建议将这些参数按表中的顺序传递),参数类型为字符串类型
参数名 | 描述 | 是否可选 |
dsn | 数据源名称,给出该参数表示数据库依赖 | 否 |
user | 用户名 | 是 |
password | 用户密码 | 是 |
host | 主机名(MySQL服务器IP地址) | 是 |
database | 数据库名称 | 是 |
port | 数据库端口号(整形) | 是 |
例1:
import pymysql
Mysql = pymysql.connect(user="root",password="123456",host="localhost",database="world",port=3306)
print(Mysql)
"""
"""
注:
1、由上面的例子的输出可以看出:connect()函数返回的是一个对象(连接对象)
2、在给connect()函数传入参数时(建立数据库连接)最好使用默认参数的方式传入参数,即在传入参数时指定值属于哪个变量。如果直接使用位置参数的话连接数据库时可能会报错
1、要想操作数据库,光连接数据是不够的,必须拿到操作数据库的游标,才能进行后续的操作,比如读取数据、添加数据。通过获取到的数据库连接实例Mysql(例1中返回的连接对象)下的cursor()方法来创建游标
2、connect()函数返回的是一个连接对象,这个连接对象表示目前和数据库的会话。连接对象支持以下方法
方法名 | 描述 |
cursor() | 返回连接对象的游标对象 |
commit() | 提交当前事务 |
rollback() | 回滚当前事务(可能不可用) |
close() | 关闭连接:关闭连接后连接对象和游标均不可用 |
注:
1、rollback()方法可能不可用,因为不是所有的数据库都支持事务
2、commit()方法总是可用的,不过如果数据库不支持事务,它就没有任何作用。完成插入并且做出某些更改后确保已经进行了提交,这样才可以将这些修改真正地保存到文件中
3、cursor()方法返回游标对象。通过游标执行SQL查询并检查结果,游标比连接支持更多方法,而且在程序中更好用
方法名 | 描述 |
callproc(func[,args]) | 使用给定的名称和参数(可选)调用已命名的数据库程序 |
close() | 关闭游标对象:关闭后游标不可用 |
execute(op[,args]) | 执行SQL操作,可能使用参数 |
executemany(op,args) | 对序列中的每个参数执行SQL操作 |
fetchone() | 取得结果集的下一行:把查询结果集中的下一行保存为序列或None |
fetchmany([size]) | 获取结果集的下n行:获取查询结果集中的多行,默认尺寸为arraysize |
fetchall() | 获取结果集中的所有行:将所有行作为序列的序列 |
nextset() | 调至下一个可用的结果集 |
setinputsizes(size) | 为参数预先定义内存区域 |
setoutputsizes(size[,col]) | 为获取大数据值设定缓冲区尺寸 |
名称 | 描述 |
arraysize | fetchmany中返回的行数,只读 |
description | 结果列描述的序列,只读 |
rowcount | 结果中的行数,只读 |
注:
1、游标对象中最重要的属性是execute*()和fetch*()方法。所有对数据库服务器的请求都由这两个方法完成
2、对fetchmany([size]) 方法来说,设置一个合理的arraysize属性很有用
3、在不需要时最好关掉游标对象
例2:
import pymysql
def dbConnect():
#建立数据库连接
dbMysql = pymysql.connect(user="root",password="123456",host="localhost",database="demo",port=3306)
#使用cursor()创建一个游标对象dbCursor
dbCursor = dbMysql.cursor()
#使用execute()方法执行SQL查询
SQL = "SELECT roleId,LEVEL FROM roleinfo WHERE roleId=5529676068169717607"
dbCursor.execute(SQL)
#使用fetchone()方法获取单条数据
data = dbCursor.fetchone()
dbCursor.close()
dbMysql.close()
return ("查询出的数据为:",data)
if __name__ == "__main__":
print(dbConnect())
#('查询出的数据为:', (5529676068169717607, 107))
例2_1:
import pymysql
import datetime
def SelectData():
# 打开数据库连接
conn = pymysql.connect(user="root", password="123456", host="localhost", database="demo", port=3306)
# 获取游标
cursor = conn.cursor()
SelectSql = """SELECT roleId,roleName FROM roleinfo WHERE LEVEL > 108"""
InsertSql = """INSERT INTO USER (id,NAME,age,TIME) VALUES(%s,'%s',%s,'%s')""" % (6, "小红", 13, datetime.datetime.now())
cursor.execute(SelectSql)
data = cursor.fetchone()
print('查询出来的原始数据为:', data)
print("execute方法影响的行数",cursor.rowcount)
print("execute方法影响的列名",cursor.description)
print('--------------')
cursor.execute(InsertSql)
conn.commit()
print("execute方法影响的行数",cursor.rowcount)
print("execute方法影响的列名",cursor.description)
cursor.close()
conn.close()
SelectData()
"""
查询出来的原始数据为: None
execute方法影响的行数 0
execute方法影响的列名 (('roleId', 8, None, 8, 8, 0, False), ('roleName', 253, None, 256, 256, 0, False))
--------------
execute方法影响的行数 1
execute方法影响的列名 None
"""
注:
1、这里感觉就是执行SQL语句是一回事,获取查询的数据又是另一回事:即执行SQL语句后把数据查询出来,但此时还没有获取(没有将查询结果赋值给一个变量:使用对应方法先获取再赋值给变量)。fetchone()函数必须跟exceute()函数结合使用,并且在exceute()函数之后使用
2、rowcount属性用于返回execute方法影响的行数:查询操作不会影响到行数,所以返回为None
3、description属性用于返回查询结果的列名(一个序列)
例3:
import pymysql
def CreateTable():
#打开数据库连接
conn = pymysql.connect(user="root",password="123456",host="localhost",database="demo",port=3306)
#获取游标
cursor=conn.cursor()
print(cursor)
#创建user表
cursor.execute('drop table if exists user')
sql="""CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`age` int(11) NOT NULL,
`time` DATETIME,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=0"""
try:
cursor.execute(sql)
print('创建数据表成功')
except Exception: #常规错误的基类
print("创建数据表失败")
finally:
cursor.close()#先关闭游标
conn.close()#再关闭数据库连接
CreateTable()
"""
1、SQL语句正确时,则会在对应数据库中正常创建一个名为user的表
2、如果SQL语句错误时,这会执行异常语句:输出创建数据表失败(如:cursor.execute(sqll))
"""
例4:
import pymysql
import datetime
def InsertData():
#打开数据库连接
conn = pymysql.connect(user="root",password="123456",host="localhost",database="demo",port=3306)
#获取游标
cursor=conn.cursor()
sql="""INSERT INTO USER (id,NAME,age,TIME) VALUES(%s,'%s',%s,'%s')""" % (6,"小红",13,datetime.datetime.now())
try:
cursor.execute(sql)
#提交到数据库执行
conn.commit()
print('插入数据成功')
except Exception: #常规错误的基类
print("插入数据失败")
#如果发生错误就回滚
conn.rollback()
finally:
cursor.close()#先关闭游标
conn.close()#再关闭数据库连接
InsertData()
#补充下Python字符串格式化
print("当前时间为:%s" % str(datetime.datetime.now()))
print("当前时间为:{0}".format(str(datetime.datetime.now())))
"""
插入数据成功
当前时间为:2019-12-21 19:34:41.512668
当前时间为:2019-12-21 19:34:41.512668
"""
注:
1、上面代码如果在使用execute()方法执行SQL语句后,没有使用commit()提交事务,那数据也不会正常插入到数据库中(但不会报错)
2、在编写SQL语句时,一定需要注意下数据库中对应字段的值的类型
3、上面例子中的SQL语句为:
"""INSERT INTO USER (id,NAME,age,TIME) VALUES(%s,'%s',%s,'%s')""" % (134, 'NULL', 13, datetime.datetime.now())
⑴可以看出上面SQL语句中在使用%s进行格式化的时候,有些字段加了冒号,有些没有加冒号,这个是因为:在冒号的字段在数据库中为字符串类型等,未加冒号的为数字类型等
⑵也就是说使用%s格式化输出的SQL语句要与:直接在数据库中执行的SQL语句一致。特别是一些特殊类型的值,一定要注意在格式化是需不需要加冒号
⑶感觉(134, 'NULL', 13, datetime.datetime.now()是Python的语法,(%s,'%s',%s,'%s')就是数据库的语法了
4、下面两个SQL语句是不一样的:
⑴第一个:数字3是以int类型写入数据库的,'null'是以字符串类型写进数据库的
⑵第二个:数字'4'是以字符串类型写入数据库的。null是特殊的值,表示空(相当于python中的None:Python中没有null,使用None表示空)
例4_1:插入多行数据
import pymysql
import datetime
def InsertData():
#打开数据库连接
try:
conn = pymysql.connect(user="root",password="123456",host="localhost",database="demo",port=3306)
#获取游标
cursor=conn.cursor()
sql = """INSERT INTO USER VALUES(%s,%s,%s,%s)"""
time1 = str(datetime.datetime.now())
time2 = str(datetime.datetime.now())
time3 = str(datetime.datetime.now())
try:
insert = cursor.executemany(sql, [(1, 'wen', 20,time1), (5, 'tom', 10,time2), (6, 'test', 30,time3)])
print('批量插入返回受影响的行数:', insert)
# 提交到数据库执行
conn.commit()
print('插入数据成功')
except pymysql.err.IntegrityError: # 执行SQL语句失败时触发
print("插入数据失败")
# 如果发生错误就回滚
conn.rollback()
finally:
cursor.close()
conn.close()
except pymysql.err.OperationalError:#数据库连接时错误触发
print("数据库连接失败检查账户信息")
InsertData()
"""
3
插入数据成功
"""
注:
1、批量插入多条sql语句采用的是executemany(sql,args)函数,返回受影响的行数。
2、args参数是一个包含多个元组的列表,每个元组对应一条mysql中的一条数据。这里的%s不需要加引号,否则插入数据的数据会类型错误
Python查询MySQL使用fetchone()获取单条数据,使用fetchall()方法获取全部数据
⑴fetchone(): 获取一个查询结果集,结果集是一个对象。未查询到数据时返回None
⑵fetchall():全部的返回结果行。未查询到数据时返回None
⑶rowcount: 这一个只读属性,并返回执行execute()方法后影响的行数
例5:
import pymysql
def SelectData():
#打开数据库连接
conn = pymysql.connect(user="root",password="123456",host="localhost",database="demo",port=3306)
#获取游标
cursor=conn.cursor()
sql="""SELECT roleId,roleName FROM roleinfo WHERE LEVEL > 100"""
try:
cursor.execute(sql)
#获取所有记录列表
data = cursor.fetchall()
print('查询出来的原始数据为:',data)
#获取查询出来的数据列名
cols = cursor.description
print(cols)
for row in data:
print(row)
print("{0}值为{1}".format(cols[0][0],row[0]))
print("{0}值为{1}".format(cols[1][0], row[1]))
print({cols[0][0]: row[0], cols[1][0]: row[1]})
except Exception: #常规错误的基类
print("查询数据失败")
finally:
cursor.close()
conn.close()
SelectData()
"""
查询出来的原始数据为: ((5529676068169717607, 'Sam_林'), (5529676068239812551, '枫落'), (5529676070679048129, '余生有你'))
(('roleId', 8, None, 8, 8, 0, False), ('roleName', 253, None, 256, 256, 0, False))
(5529676068169717607, 'Sam_林')
roleId值为5529676068169717607
roleName值为Sam_林
{'roleName': 'Sam_林', 'roleId': 5529676068169717607}
(5529676068239812551, '枫落')
roleId值为5529676068239812551
roleName值为枫落
{'roleName': '枫落', 'roleId': 5529676068239812551}
(5529676070679048129, '余生有你')
roleId值为5529676070679048129
roleName值为余生有你
{'roleName': '余生有你', 'roleId': 5529676070679048129}
"""
例5_1:
import pymysql
def SelectData():
#打开数据库连接
conn = pymysql.connect(user="root",password="123456",host="localhost",database="demo",port=3306)
#获取游标
cursor=conn.cursor()
sql="""SELECT roleId,roleName FROM roleinfo WHERE LEVEL > 100"""
try:
cursor.execute(sql)
"""
#取3条数据
resTuple=cursor.fetchmany(3)
"""
#获取单个记录列表
data = cursor.fetchone()
print('查询出来的原始数据为:',data)
except Exception: #常规错误的基类
print("查询数据失败")
finally:
cursor.close()
conn.close()
SelectData()
#查询出来的原始数据为: (5529676068169717607, 'Sam_林')
注:由上面例子的输出结果可以看出
1、Python查询Mysql使用fetchone()方法获取单条数据, 即使实际查询出来的数据有多个但也只会返回最后一个查询出来的数据。使用fetchall()方法获取多条数据,也就是全部返回
2、fetchall()返回的是一个嵌套元组:嵌套元组中的每个元素是查询出来的每一行数据,所有数据行又组成一个大元组。可以使用for循环得到每个数据行(元组元素)中的具体值
3、从execute()函数的查询结果中取数据,以元组的形式返回游标所在处的一条数据,如果游标所在处没有数据,将返回空元组,该数据执行一次,游标向下移动一个位置
4、从exceute()函数结果中获取游标所在处的size条数据,并以元组的形式返回,元组的每一个元素都也是一个由一行数据组成的元组,如果size大于有效的结果行数,将会返回cursor.arraysize条数据,但如果游标所在处没有数据,将返回空元组。查询几条数据,游标将会向下移动几个位置
例6:
import pymysql
def UpdatData():
#打开数据库连接
conn = pymysql.connect(user="root",password="123456",host="localhost",database="demo",port=3306)
#获取游标
cursor=conn.cursor()
sql="""UPDATE USER SET age=34 WHERE id=1"""
try:
#执行SQL语句
cursor.execute(sql)
#提交到数据库执行
conn.commit()
print('更新数据成功')
except Exception: #常规错误的基类
print("更新数据失败")
#如果发生错误就回滚
conn.rollback()
finally:
cursor.close()#先关闭游标
conn.close()#再关闭数据库连接
UpdatData()
例7:
import pymysql
def DeleteData():
#打开数据库连接
conn = pymysql.connect(user="root",password="123456",host="localhost",database="demo",port=3306)
#获取游标
cursor=conn.cursor()
sql="""DELETE FROM USER WHERE ID = 5"""
try:
#执行SQL语句
cursor.execute(sql)
#提交到数据库执行
conn.commit()
print('删除数据成功')
except Exception: #常规错误的基类
print("删除数据失败")
#如果发生错误就回滚
conn.rollback()
finally:
cursor.close()#先关闭游标
conn.close()#再关闭数据库连接
DeleteData()
DB API中定义了一些数据库操作的错误及异常,下表列出了这些错误和异常
异常 | 超类 | 描述 |
StandardError | 所有异常的泛型基类 | |
Warning | StandardError | 在非致命错误发生时引发 |
Error | StandardError | 错误异常基类 |
InterfaceError | Error | 数据库接口错误 |
DatabaseError | Error | 与数据库相关的错误基类 |
DataError | DatabaseError | 处理数据时出错 |
OperationalError | DatabaseError | 数据库执行命令时出错 |
IntegrityError | DatabaseError | 数据完整性错误 |
InternalError | DatabaseError | 数据库内部错误 |
ProgrammingError | DatabaseError | SQL执行失败 |
NotSupportedError | DatabaseError | 试图执行数据库不支持的特性 |
例8:
import pymysql
import datetime
def InsertData():
#打开数据库连接
try:
conn = pymysql.connect(user="root",password="123456",host="localhost",database="demo",port=3306)
#获取游标
cursor=conn.cursor()
sql = """INSERT INTO USER (id,NAME,age,TIME) VALUES(%s,'%s',%s,'%s')""" % (10, "小红", 13, datetime.datetime.now())
try:
cursor.execute(sql)
# 提交到数据库执行
conn.commit()
print('插入数据成功')
except pymysql.err.IntegrityError:#执行SQL语句失败时触发
print("插入数据失败")
# 如果发生错误就回滚
conn.rollback()
finally:
cursor.close()
conn.close()
except pymysql.err.OperationalError:#数据库连接时错误触发
print("数据库连接失败检查账户信息")
InsertData()
注:
1、在处理数据库异常时需要引用Pymysql模块:这里感觉应该是这些异常时Pymsq包里面的异常,不是Python自带的异常,因此需要引用Pymysql模块
2、这个例子中的异常处理是捕获一个具体的异常,如果想捕获所有的异常,则可以就只写except(except后不带任何异常)
例8_1:上面例8不能这样写
import pymysql
import datetime
def InsertData():
#打开数据库连接
try:
conn = pymysql.connect(user="root",password="123456",host="localhost",database="demo",port=3306)
#获取游标
cursor=conn.cursor()
except pymysql.err.OperationalError:#数据库连接时错误触发
print("数据库连接失败检查账户信息")
sql = """INSERT INTO USER (id,NAME,age,TIME) VALUES(%s,'%s',%s,'%s')""" % (10, "小红", 13, datetime.datetime.now())
try:
cursor.execute(sql)
# 提交到数据库执行
conn.commit()
print('插入数据成功')
except pymysql.err.IntegrityError: # 执行SQL语句失败时触发
print("插入数据失败")
# 如果发生错误就回滚
conn.rollback()
finally:
cursor.close()
conn.close()
InsertData()
注:
1、如果这样写的话,会提示conn、cursor是局部变量,如果使用global关键字的话,又会提示conn、cursor没有定义:这里感觉是try语句内的变量跟函数体内的变量是差不多的吧,只是说try内部的变量即使使用global关键字也不行
2、分析上面的语句逻辑:连接数据库没有问题时就返回游标对象、执行SQL语句等。因此后面所有的代码都应该属于try语句下的,而不应该单独写(与第一个try语句同一缩进)
每一个插入数据库中的数据都对应一个数据类型,每一列数据对应同一个数据类型,不同列对应不同的数据类型。在数据库操作的过程中,为了能够正确与基础SQL数据库进行数据交互操作,DB-API定义了用于特殊类型和值得构造函数及常量
构造函数和特殊值的名称 | 描述 |
Date(yr,mo,dy) | 日期值对象 |
Time(hr,min,sec) | 时间值对象 |
Timestamp(yr,mo,dy,hr,min,sec) | 时间戳对象 |
DateFromTicks(ticks) | 创建自新纪元以来秒数的对象 |
TimeFromTicks(ticks) | 创建自新纪元以来秒数的时间值对象 |
TimestampFromTicks(ticks) | 创建自新纪元以来秒数的时间戳值对象 |
Binary(string) | 对应二进制长字符串值对象 |
STRING | 描述字符串列对象 |
BINARY | 描述二进制长列对象 |
NUMBER | 描述数字列对象 |
DATETIME | 描述日期的时间列对象 |
ROWID | 描述row ID列对象 |
注:新纪元指1970-01-01 00:00:01 utc时间
1、事务机制可以确保数据一致性,事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性
⑴原子性(atomicity):一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做
⑵一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的
⑶隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰
⑷持久性(durability):持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响
2、Python DB API 2.0 的事务提供了两个方法commit或rollback
3、对于支持事务的数据库, 在Python数据库编程中,当游标建立之时,就自动开始了一个隐形的数据库事务
4、commit()方法游标的所有更新操作,rollback()方法回滚当前游标的所有操作。每一个方法都开始了一个新的事务
例9:
# -*- coding: utf-8 -*-
import pymysql
class MyDB():
def __init__(self , host="localhost", username="root", password="123456", port=3306, database="demo"):
'''类例化,处理一些连接操作'''
self.host = host
self.username = username
self.password = password
self.database = database
self.port = port
self.cur = None
self.con = None
# connect to mysql
try:
self.con = pymysql.connect(host = self.host, user = self.username, password = self.password, port = self.port, database = self.database)
self.cur = self.con.cursor()
except :
print("DataBase connect error,please check the db config")
def close(self):
'''结束查询和关闭连接'''
self.con.close()
def create_table(self,sql_str):
'''创建数据表'''
try:
self.cur.execute(sql_str)
except Exception as e:
print(e)
def query_formatrs(self,sql_str):
'''查询数据,返回一个列表,里面的每一行是一个字典,带字段名
cursor 为连接光标
sql_str为查询语句
'''
try:
self.cur.execute(sql_str)
rows = self.cur.fetchall()
r = []
for x in rows:
r.append(dict(zip(self.cur.column_names,x)))
return r
except:
return False
def query(self,sql_str):
'''查询数据并返回
cursor 为连接光标
sql_str为查询语句
'''
try:
self.cur.execute(sql_str)
rows = self.cur.fetchall()
return rows
except:
return False
def execute_update_insert(self,sql):
'''
插入或更新记录 成功返回最后的id
'''
self.cur.execute(sql)
self.con.commit()
return self.cur.lastrowid
if __name__ == "__main__":
mydb = MyDB()
#创建表
#mydb.create_table('create table user (id varchar(20) primary key, name varchar(20))')
#插入数据
#mydb.execute_update_insert("insert into user (id, name) values ('1', 'Michael')")
# 查询数据表
mydb_new = MyDB()
results = mydb.query("SELECT * FROM user")
print(results)
for row in results:
print(row)
#关闭数据库
mydb.close()
注:
1、这个例子中主要需要学习下在定义实例对象时的方法:数据库连接对象的定义和游标对象的定义
2、感觉还可以将数据库连接对象的定义和游标对象单独定义在一个方法里面,只是说这样的话,在增删改查的方法中都得先在类中调用类内部的方法(定义连接对象的定义和游标对象的方法)