杰瑞科技汇

python中QSqlQuery

QSqlQuery 是 Qt SQL 模块中的一个核心类,它代表一个对数据库执行的 SQL 语句,你可以把它想象成一个“命令游标”或“语句执行器”,它允许你执行 SQL 查询(SELECT)、更新(INSERT, UPDATE, DELETE)、管理事务等。

python中QSqlQuery-图1
(图片来源网络,侵删)

核心概念

在使用 QSqlQuery 之前,必须确保已经:

  1. 加载数据库驱动:Qt 支持多种数据库(如 SQLite, MySQL, PostgreSQL, ODBC 等),你需要告诉 Qt 你要使用哪种驱动。
  2. 建立数据库连接:通过 QSqlDatabase 类创建一个到特定数据库的连接。

下面我们以最常见的 SQLite 为例,因为它不需要安装额外的数据库服务器,非常适合学习和演示。


基本环境设置

确保你已经安装了 PyQt5PySide2/PySide6

pip install PyQt5

导入必要的模块:

python中QSqlQuery-图2
(图片来源网络,侵删)
import sys
from PyQt5.QtSql import QSqlDatabase, QSqlQuery
from PyQt5.QtWidgets import QApplication, QMessageBox

建立数据库连接

这是使用任何 QSql 类的第一步。

def create_connection():
    """创建并返回一个到SQLite数据库的连接"""
    db = QSqlDatabase.addDatabase('QSQLITE')  # 指定使用SQLite驱动
    db.setDatabaseName('example.db')          # 设置数据库文件名,如果不存在会自动创建
    if not db.open():
        QMessageBox.critical(None, "错误", f"无法连接到数据库: {db.lastError().text()}")
        return False
    return True

QSqlQuery 的主要用法

一旦建立了连接,你就可以创建 QSqlQuery 对象来执行SQL了。

1 执行非查询语句 (DDL/DML)

这类语句不返回结果集,CREATE, INSERT, UPDATE, DELETE

直接在构造函数中执行

python中QSqlQuery-图3
(图片来源网络,侵删)
query = QSqlQuery("CREATE TABLE IF NOT EXISTS users ("
                  "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                  "name TEXT NOT NULL, "
                  "age INTEGER)", db)
if not query.isActive():
    print(f"创建表失败: {query.lastError().text()}")
else:
    print("表 'users' 创建成功或已存在。")

使用 prepare()exec() (推荐,尤其适用于参数化查询)

这种方式更安全、更高效,可以防止SQL注入。

# 准备SQL语句
query = QSqlQuery(db)
query.prepare("INSERT INTO users (name, age) VALUES (:name, :age)")
# 绑定参数
query.bindValue(":name", "Alice")
query.bindValue(":age", 30)
# 执行
if not query.exec_():
    print(f"插入数据失败: {query.lastError().text()}")
else:
    print("插入数据成功。")
# 可以重复绑定并执行,插入另一条数据
query.bindValue(":name", "Bob")
query.bindValue(":age", 25)
query.exec_()
print("插入数据成功。")

2 执行查询语句

对于 SELECT 语句,你需要遍历结果集。

query = QSqlQuery("SELECT id, name, age FROM users", db)
# 1. 检查查询是否成功执行
if not query.isActive():
    print(f"查询失败: {query.lastError().text()}")
else:
    # 2. 遍历结果集
    print("\n--- 用户列表 ---")
    while query.next():  # 移动到下一条记录,如果存在则返回True
        # 3. 获取字段值
        # 方法一:按列号获取 (从0开始)
        id = query.value(0)
        name = query.value(1)
        age = query.value(2)
        # 方法二:按列名获取 (更清晰,推荐)
        # id = query.value("id")
        # name = query.value("name")
        # age = query.value("age")
        print(f"ID: {id}, 姓名: {name}, 年龄: {age}")

3 更新和删除数据

更新和删除数据与插入类似,使用 UPDATEDELETE 语句。

# 更新数据
query = QSqlQuery(db)
query.prepare("UPDATE users SET age = :new_age WHERE name = :name")
query.bindValue(":new_age", 31)
query.bindValue(":name", "Alice")
if query.exec_():
    print(f"成功更新了 {query.numRowsAffected()} 条记录。")
else:
    print(f"更新失败: {query.lastError().text()}")
# 删除数据
query.prepare("DELETE FROM users WHERE name = :name")
query.bindValue(":name", "Bob")
if query.exec_():
    print(f"成功删除了 {query.numRowsAffected()} 条记录。")
else:
    print(f"删除失败: {query.lastError().text()}")

4 事务处理

事务可以确保一组数据库操作要么全部成功,要么全部失败,保证数据的一致性。

def transaction_example():
    if not create_connection():
        return
    db = QSqlDatabase.database()
    query = QSqlQuery(db)
    try:
        # 1. 开始事务
        db.transaction()
        # 2. 执行多个操作
        query.prepare("INSERT INTO users (name, age) VALUES (?, ?)") # ?是另一种占位符
        query.addBindValue("Charlie")
        query.addBindValue(40)
        query.exec_()
        query.prepare("INSERT INTO users (name, age) VALUES (?, ?)")
        query.addBindValue("David")
        query.addBindValue(35)
        query.exec_()
        # 3. 如果所有操作都成功,则提交事务
        db.commit()
        print("事务提交成功!")
    except Exception as e:
        # 4. 如果发生任何错误,则回滚事务
        db.rollback()
        print(f"发生错误,事务已回滚: {e}")
    # 关闭连接
    db.close()

完整示例代码

下面是一个将上述所有功能整合在一起的完整、可运行的示例。

import sys
from PyQt5.QtSql import QSqlDatabase, QSqlQuery
from PyQt5.QtWidgets import QApplication, QMessageBox, QWidget, QVBoxLayout, QPushButton, QTextEdit
class DatabaseDemo(QWidget):
    def __init__(self):
        super().__init__()
        self.db = None
        self.initUI()
    def initUI(self):
        self.setWindowTitle('QSqlQuery 示例')
        self.setGeometry(300, 300, 500, 400)
        self.layout = QVBoxLayout()
        self.btn_connect = QPushButton('连接数据库并创建表')
        self.btn_insert = QPushButton('插入数据')
        self.btn_query = QPushButton('查询数据')
        self.btn_update = QPushButton('更新数据')
        self.btn_delete = QPushButton('删除数据')
        self.btn_transaction = QPushButton('事务示例')
        self.text_output = QTextEdit()
        self.text_output.setReadOnly(True)
        self.layout.addWidget(self.btn_connect)
        self.layout.addWidget(self.btn_insert)
        self.layout.addWidget(self.btn_query)
        self.layout.addWidget(self.btn_update)
        self.layout.addWidget(self.btn_delete)
        self.layout.addWidget(self.btn_transaction)
        self.layout.addWidget(self.text_output)
        self.setLayout(self.layout)
        self.btn_connect.clicked.connect(self.connect_and_create_table)
        self.btn_insert.clicked.connect(self.insert_data)
        self.btn_query.clicked.connect(self.query_data)
        self.btn_update.clicked.connect(self.update_data)
        self.btn_delete.clicked.connect(self.delete_data)
        self.btn_transaction.clicked.connect(self.transaction_example)
    def connect_and_create_table(self):
        self.db = QSqlDatabase.addDatabase('QSQLITE')
        self.db.setDatabaseName('example.db')
        if not self.db.open():
            self.log_message(f"无法连接到数据库: {self.db.lastError().text()}")
            return
        query = QSqlQuery(self.db)
        create_table_sql = """
        CREATE TABLE IF NOT EXISTS employees (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            position TEXT,
            salary REAL
        )
        """
        if not query.exec(create_table_sql):
            self.log_message(f"创建表失败: {query.lastError().text()}")
        else:
            self.log_message("数据库连接成功,表 'employees' 已准备就绪。")
    def log_message(self, message):
        self.text_output.append(message)
    def insert_data(self):
        if not self.check_connection():
            return
        query = QSqlQuery(self.db)
        query.prepare("INSERT INTO employees (name, position, salary) VALUES (?, ?, ?)")
        # 插入第一条
        query.addBindValue("张三")
        query.addBindValue("软件工程师")
        query.addBindValue(12000.50)
        if not query.exec_():
            self.log_message(f"插入失败: {query.lastError().text()}")
            return
        # 插入第二条
        query.addBindValue("李四")
        query.addBindValue("项目经理")
        query.addBindValue(25000.00)
        query.exec_()
        self.log_message(f"成功插入 {query.numRowsAffected()} 条记录。")
    def query_data(self):
        if not self.check_connection():
            return
        query = QSqlQuery("SELECT * FROM employees", self.db)
        if not query.isActive():
            self.log_message(f"查询失败: {query.lastError().text()}")
            return
        self.log_message("\n--- 员工列表 ---")
        while query.next():
            id = query.value("id")
            name = query.value("name")
            position = query.value("position")
            salary = query.value("salary")
            self.log_message(f"ID: {id}, 姓名: {name}, 职位: {position}, 薪资: {salary:.2f}")
    def update_data(self):
        if not self.check_connection():
            return
        query = QSqlQuery(self.db)
        query.prepare("UPDATE employees SET salary = salary * 1.1 WHERE position = ?")
        query.addBindValue("软件工程师")
        if query.exec_():
            self.log_message(f"成功更新了 {query.numRowsAffected()} 名软件工程师的薪资。")
        else:
            self.log_message(f"更新失败: {query.lastError().text()}")
    def delete_data(self):
        if not self.check_connection():
            return
        query = QSqlQuery(self.db)
        query.prepare("DELETE FROM employees WHERE name = ?")
        query.addBindValue("李四")
        if query.exec_():
            self.log_message(f"成功删除了 {query.numRowsAffected()} 条记录。")
        else:
            self.log_message(f"删除失败: {query.lastError().text()}")
    def transaction_example(self):
        if not self.check_connection():
            return
        self.db.transaction()
        query = QSqlQuery(self.db)
        try:
            query.prepare("INSERT INTO employees (name, position, salary) VALUES (?, ?, ?)")
            query.addBindValue("王五")
            query.addBindValue("测试工程师")
            query.addBindValue(8000.00)
            query.exec_()
            # 模拟一个错误,看看事务是否会回滚
            # 如果取消下面这行的注释,事务将失败,之前的插入也会被撤销
            # self.db.rollback() 
            # raise Exception("手动触发错误")
            self.db.commit()
            self.log_message("事务提交成功:新员工王五已入职。")
        except Exception as e:
            self.db.rollback()
            self.log_message(f"事务失败,已回滚: {e}")
    def check_connection(self):
        if not self.db or not self.db.isOpen():
            self.log_message("错误:数据库未连接。")
            return False
        return True
    def closeEvent(self, event):
        if self.db and self.db.isOpen():
            self.db.close()
        event.accept()
if __name__ == '__main__':
    app = QApplication(sys.argv)
    demo = DatabaseDemo()
    demo.show()
    sys.exit(app.exec_())
功能 方法 说明
创建实例 QSqlQuery(sql, db)QSqlQuery(db) 可以直接带SQL创建,或先创建再准备。
准备语句 query.prepare(sql) 使用参数化查询(name 或 )的首选方法。
绑定参数 query.bindValue(":name", "Alice")query.addBindValue("Alice") 为预语句中的占位符赋值。
执行语句 query.exec_() 执行准备好的或直接的SQL语句。
检查状态 query.isActive() 检查上一个操作是否成功执行。
获取错误 query.lastError().text() 获取上一个操作的错误信息。
获取影响行数 query.numRowsAffected() 获取 INSERT, UPDATE, DELETE 影响的行数。
遍历结果 query.next() 将游标移动到下一条记录,返回是否存在下一条。
获取值 query.value(0)query.value("name") 获取当前记录的指定列值。
事务 db.transaction(), db.commit(), db.rollback() 通过 QSqlDatabase 对象管理事务。

QSqlQuery 功能强大且灵活,是 Qt 应用程序中进行数据库操作的基础,掌握它的用法对于开发数据驱动的桌面应用至关重要。

分享:
扫描分享到社交APP
上一篇
下一篇