You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt技术问询:QSql模型多表操作、插入及性能优化问题

问题1:在线数据处理场景优化方案

针对QSqlQueryModel每次调用setQuery()引发全量重绘、GUI性能下降的问题,可通过自定义子类实现增量更新,避免全量查询与重绘:

核心思路

放弃每次新增记录时全量执行setQuery(),改为手动向模型追加单条/批量记录,仅触发新增行的局部重绘,同时同步写入数据库。

自定义子类实现示例

#include <QSqlQueryModel>
#include <QSqlRecord>
#include <QSqlQuery>

class CustomSqlQueryModel : public QSqlQueryModel
{
    Q_OBJECT
public:
    explicit CustomSqlQueryModel(QObject *parent = nullptr) : QSqlQueryModel(parent) {}

    // 追加单条记录(同时写入数据库)
    bool appendRecord(const QSqlRecord &record)
    {
        // 1. 先写入数据库
        QSqlQuery query;
        QString insertSql = QString("INSERT INTO 你的表名 (%1) VALUES (%2)")
                            .arg(record.fieldNames().join(","))
                            .arg(QStringList().fill("?", record.count()).join(","));
        query.prepare(insertSql);
        for (int i = 0; i < record.count(); ++i) {
            query.addBindValue(record.value(i));
        }
        if (!query.exec()) return false;

        // 2. 增量更新模型,仅重绘新增行
        beginInsertRows(QModelIndex(), rowCount(), rowCount());
        insertRow(rowCount());
        for (int col = 0; col < record.count(); ++col) {
            setData(index(rowCount()-1, col), record.value(col));
        }
        endInsertRows();
        return true;
    }

    // 批量追加记录
    bool appendRecords(const QList<QSqlRecord> &records)
    {
        if (records.isEmpty()) return true;

        // 1. 批量写入数据库(开启事务提升效率)
        QSqlDatabase::database().transaction();
        bool allOk = true;
        for (const auto &record : records) {
            QSqlQuery query;
            QString insertSql = QString("INSERT INTO 你的表名 (%1) VALUES (%2)")
                                .arg(record.fieldNames().join(","))
                                .arg(QStringList().fill("?", record.count()).join(","));
            query.prepare(insertSql);
            for (int i = 0; i < record.count(); ++i) {
                query.addBindValue(record.value(i));
            }
            if (!query.exec()) {
                allOk = false;
                break;
            }
        }
        if (allOk) {
            QSqlDatabase::database().commit();
        } else {
            QSqlDatabase::database().rollback();
            return false;
        }

        // 2. 批量更新模型
        beginInsertRows(QModelIndex(), rowCount(), rowCount() + records.size() - 1);
        for (const auto &record : records) {
            insertRow(rowCount());
            for (int col = 0; col < record.count(); ++col) {
                setData(index(rowCount()-1, col), record.value(col));
            }
        }
        endInsertRows();
        return true;
    }

    // 若需支持编辑,重写setData与flags
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override
    {
        if (!index.isValid() || role != Qt::EditRole) return false;
        bool ok = QSqlQueryModel::setData(index, value, role);
        if (ok) emit dataChanged(index, index);
        return ok;
    }

    Qt::ItemFlags flags(const QModelIndex &index) const override
    {
        Qt::ItemFlags flags = QSqlQueryModel::flags(index);
        flags |= Qt::ItemIsEditable;
        return flags;
    }
};

使用说明

在线获取到新数据后,直接调用appendRecord()或appendRecords(),无需再执行setQuery():

// 假设从在线接口获取到一条QSqlRecord格式的新数据
QSqlRecord newRecord = getOnlineData();
customModel->appendRecord(newRecord);

问题2:多表数据读写场景解决方案

核心问题分析

QSqlTableModel仅支持单表操作:

  • 用关联查询结果初始化模型后,模型元数据与单表不匹配,insertRecord()无法识别目标表与字段,导致回写失败;
  • 调用setTable()会重置模型元数据为单表结构,与之前关联查询的列不一致,引发视图显示异常。

针对性方案

方案1:用QSqlRelationalTableModel实现关联展示+单表写入

如果只需对其中一张主表进行写入操作(比如test表),可通过QSqlRelationalTableModel设置表关联,同时保留单表写入能力:

#include <QSqlRelationalTableModel>
#include <QSqlRelation>

QSqlRelationalTableModel *model = new QSqlRelationalTableModel(this);
model->setTable("test");
// 设置关联:test表的`关联字段`关联test2表的`关联键`,展示test2表的`显示字段`
model->setRelation(2, QSqlRelation("test2", "test_id", "name"));
model->setEditStrategy(QSqlTableModel::OnFieldChange); // 实时写入
model->select();

// 插入记录(仅写入test表,关联字段需手动设置)
QSqlRecord record = model->record();
record.setValue("test_field1", "value1");
record.setValue("test_id", 1); // 关联test2表的test_id
model->insertRecord(-1, record);

方案2:手动用QSqlQuery处理多表写入+视图同步

如果需要同时写入两张表,需手动开启事务执行插入,之后刷新视图:

// 开启事务
QSqlDatabase::database().transaction();

// 插入test表
QSqlQuery query1;
query1.prepare("INSERT INTO test (field1, field2) VALUES (?, ?)");
query1.addBindValue("val1");
query1.addBindValue("val2");
if (!query1.exec()) {
    QSqlDatabase::database().rollback();
    return;
}
// 获取插入的主键(用于关联test2表)
int testId = query1.lastInsertId().toInt();

// 插入test2表
QSqlQuery query2;
query2.prepare("INSERT INTO test2 (test_id, field3) VALUES (?, ?)");
query2.addBindValue(testId);
query2.addBindValue("val3");
if (!query2.exec()) {
    QSqlDatabase::database().rollback();
    return;
}

// 提交事务并刷新视图
QSqlDatabase::database().commit();
model->select(); // 全量刷新,或用自定义模型的增量更新方法

关于QSqlQuery::exec()与视图同步

执行exec()修改数据库后,视图不会自动同步,需手动触发模型更新:

  • 简单场景调用model->select()全量刷新;
  • 性能敏感场景用自定义模型的增量更新方法(如问题1中的appendRecord),避免全量重绘。

内容的提问来源于stack exchange,提问作者Arthur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 03:25:29