目录
一、简介
操作数据库的标准流程是:
1、建立数据库连接
2、使用SQL语句做增删改查
3、关闭数据库连接
在PyQt中,数据库连接由QSqlDatabase提供,sql语句的执行则由QSqlQuery负责。
二、QSqlDatabase
一个QSqlDatabase的实例就代表一个到数据库的连接,这个连接通过数据库驱动来访问数据库,在PyQt中,数据库驱动是QSqlDriver(一个抽象类)子类的一个实例。除非是开发自己的数据库驱动,不然在应用上一般是不会用到QSqlDriver的。
QSqlDatabase的完整介绍及API见:QSqlDatabase详细说明
1、创建数据库连接
创建一个数据库连接一般是通过调用QSqlDatabase的静态方法addDatabase()来实现的:
@staticmethod
def addDatabase(type: str,
connectionName: str = ...) -> QSqlDatabase
'''
type: 数据库类型
connectionName: 这个连接的名字,如果不传这个连接就是应用的默认数据库连接
'''
示例:
db = QSqlDatabase.addDatabase('QMYSQL')
db.setHostName('127.0.0.1')
db.setPort(3306)
db.setDatabaseName('course')
db.setUserName('root')
db.setPassword('123456')
# 打开数据库连接
db.open()
2、PyQt支持的数据库类型
类型名(用于addDatabase type参数) | 对应数据库 |
---|---|
QDB2 | IBM DB2 (version 7.1 and above) |
QIBASE | Borland InterBase |
QMYSQL / MARIADB | MySQL or MariaDB (version 5.0 and above) |
QOCI | Oracle Call Interface Driver |
QODBC | Open Database Connectivity (ODBC) - Microsoft SQL Server and other ODBC-compliant databases |
QPSQL | PostgreSQL (versions 7.3 and above) |
QSQLITE | SQLite version 3 |
3、添加mysql驱动
QT的sql模块是把数据库驱动作为插件来使用的,而不是集成到qt环境中,所以如果qt环境中缺少某个数据库驱动的话,是无法连接到这个数据库的。
QSqlDatabase的静态方法drivers()可以用于列出当前可用的数据库驱动:
目前所用的pyqt和qt版本
PyQt6 6.5.0
PyQt6-Qt6 6.5.1默认就带有的数据库驱动:
print(QSqlDatabase.drivers())
'''
['QSQLITE', 'QODBC', 'QPSQL']
'''
可以看到,默认是不带mysql驱动的。
MySQL驱动可以下载qt源码自己编译,也可以去网上找现成的库,我没有qt的环境,编译不了,这里推荐一个github链接,里面有各个版本的qt对应的MySQL驱动:MySql驱动
一定要下载和自己的qt版本一样的mysql驱动,不然驱动是用不了的。
这边下载了qsqlmysql.dll_Qt_SQL_driver_6.5.1_MSVC2019_64-bit.zip,解压后:
qsqlmysqld.dll是debug版本,qsqlmysql.dll是release版本,这里就用release版本。
先把qsqlmysql.dll拷贝到qt的驱动目录下:
然后再把 libmysql.dll拷贝到qt的bin目录下:
这里要说明一下,本机安装的是mysql8,其所用的openssl库是1.1版本的, 所以跟libmysql.dll一块拷到bin目录下的openssl库要用1.1版本的,而不能用下载的包里面的3版本的,否则就会报
SSL connection error: unknown error number QMYSQL: Unable to connect 错误。
三、QSqlQuery
一旦用QSqlDatabase创建好数据库连接并打开之后,就可以用这个连接来创建一个QSqlQuery实例,后面的数据库操作都是在这个QSqlQuery实例之上进行。详细见:QSqlQuery官方文档
1、创建查询并执行Sql语句
首先,要创建一个QSqlQuery实例
构造函数:
QSqlQuery(query: str = '', db: QSqlDatabase = QSqlDatabase())
'''
query:要执行的SQL语句,如果传递了sql语句,则在实例化QSqlQuery对象的时候就会
自动执行这条sql语句,后面就不再需要调用exec()了。
db: 数据库连接,不传则使用应用默认的数据库连接(如果有的话)
'''
其次,如果在创建QSqlQuery实例的时候没有传Sql语句,那就需要调用exec()来执行sql语句。
1、把sql语句直接传递给exec()
query = QSqlQuery()
query.exec("SELECT * FROM artist")
while(query.next()){
doSomething();
}
2、先用prepare()准备sql语句,然后调用exec()执行
query = QSqlQuery()
query.prepare("SELECT * FROM artist")
query.exec()
while(query.next()){
doSomething();
}
简单的sql语句就直接用exec()执行就行了,prepare()主要是配合bindValue()来实现绑定值的。
sql语句一般都是有筛选条件(where)的,而筛选所用的值很少有直接给出,一般都在程序变量中,这时候我们就要先用prepare()准备sql语句并提供占位符,然后用bindValue()把占位符和变量绑定,实现把变量的值传递给sql语句,从而让QSqlQuery执行。
Oracle数据库通过使用冒号名称语法来识别占位符,例如:name。ODBC只是使用?字符。Qt支持这两种语法,但有一个限制,即不能将它们混合在同一个查询中。
我们姑且把 :name 称为具名占位符, ?成为匿名占位符
bindValue()有两个函数签名:
bindValue(const QString &placeholder, const QVariant &val, QSql::ParamType paramType = QSql::In)
'''
通过名称来给占位符绑定值,该方法需要指定名字,所以只有具名占位符可用
'''
bindValue(int pos, const QVariant &val, QSql::ParamType paramType = QSql::In)
'''
通过位置来给占位符绑定值,具名占位符和匿名占位符均可用
'''
下面看示例:
# 具名占位符
query.prepare("select * from tb_product where prod_name=:name and prod_price=:price")
query.bindValue(0, '苹果')
query.bindValue(1, '8.89')
# 注意 :(冒号)不能少,即 :name 一起才是一个占位符
query.bindValue(":name", '苹果')
query.bindValue(":price", '8.89')
对于具名占位符 :name和:price来说,上面这两种绑定值的方式都可以
# 匿名占位符
query.prepare("select * from tb_product where prod_name=? and prod_price=?")
# 匿名占位符只能通过这种方式绑定值
query.bindValue(0, '苹果')
query.bindValue(1, '8.89')
2、判断执行结果
成功执行了一条SQL语句的话,查询会处于活动状态,即 isActive() 会返回True。
对于增、删、改来说,可以通过 numRowsAffected() 来判断生效的记录数。
对于查询来说,可以通过 size() 来得到查询出的记录数。
下面是两个示例:
#插入
query.exec("insert into tb_product values(NULL, '冬枣', 9.89, '冬枣很好吃,我喜欢吃')")
if query.isActive():
print(self.sql_query.numRowsAffected())
else:
print("insert fail:", query.lastError().text())
#查询
query.prepare("select * from tb_product where prod_name=? and prod_price=?")
query.bindValue(0, '苹果')
query.bindValue(1, '8.89')
query.exec()
if query.isActive():
print(query.size())
while query.next():
for i in range(query.record().count()):
print(query.value(i), end=' ')
print('')
else:
print("select fail:", query.lastError().text())
3、定位到有效的记录上
对于查询出的结果集来说,我们想访问某条记录,那必须先定位到这条记录,然后才能获取。
QSqlQuery()提供了几个方法用于定位记录:
next():定位到下一条记录上,如果当前已是最后一条记录,该方法返回False,否则返回True
previous():定位到上一条记录上,如果当前是第一条记录,该方法返回False,否则返回True
first():定位到第一条记录上
last():定位到最后一条记录上
seek(int index, bool relative = false):定位到指定记录上
'''
如果relative为False(默认),则seek()遵循以下规则:
如果index为负数,query会定位到第一条记录之前,并且seek返回False;
否则,query尝试定位到index指定的记录处,失败则定位到最后一条记录之后并且seek返回false,成功seek则seek返回true
如果relative为True,则seek()遵循以下规则:
...
'''
4、获取记录的值
获取记录的值通过 value() 方法,可以传入索引获取,也可以传入列名获取。
那要怎么指定列名呢,我们并不总是指定数据库表结构的,可以通过record()方法获取QSqlRecord,这是一个对查询集的一个数据库描述,从里面获取列名,下面通过一个代码示例来说明:
# 准备sql语句并使用占位符
self.sql_query.prepare("select prod_name as name, prod_price as price from tb_product where prod_name=? and prod_price=?")
# 匿名索引只能通过位置来绑定值
self.sql_query.bindValue(0, '苹果')
self.sql_query.bindValue(1, '8.89')
# 执行sql语句
self.sql_query.exec()
# 判断执行是否成功
if self.sql_query.isActive():
# 打印查询到的记录数
print(self.sql_query.size())
# 拿到record
record = self.sql_query.record()
# 打印出列名
for i in range(record.count()):
print(record.field(i).name(), end=' ')
print('')
# 从结果集中取值
while self.sql_query.next():
for i in range(record.count()):
# 这里我们还是通过索引来取值
print(self.sql_query.value(i), end=' ')
print('')
else:
# 如果sql执行失败则打印出来失败描述
print("select fail:", self.sql_query.lastError().text())