何时或如何将 QSqlTableModel 上的 fetchMore() 与 SQLite 数据库一起使用以使 rowCount() 工作?

Posted

技术标签:

【中文标题】何时或如何将 QSqlTableModel 上的 fetchMore() 与 SQLite 数据库一起使用以使 rowCount() 工作?【英文标题】:When or how to use fetchMore() on QSqlTableModel with a SQLite database for rowCount() to work? 【发布时间】:2014-12-09 18:07:30 【问题描述】:

我的 DataTable 类派生自 QAbstractTableModel。它在内部使用 QSqlTableModel 对象从 db 表中获取数据。它代表 db 中每一行的记录(它做的更多,但记录数始终是 db 表中的行数)。

使用 mysql,我的 DataTable::rowCount() 实现只需调用 QSqlTableModel 上的 rowCount(),效果很好。

现在使用 SQLite,如果 db 表中有超过 256 行,Qt 的 SQLite 驱动程序会返回 256 的行数,所以我的 DataTable 类也返回 256 - 这是错误的。 documentation 告诉我打电话给while (sql_model->canFetchMore()) sql_model->fetchMore();。在创建内部 QSqlTableModel 之后立即调用 fetchMore() 实际上会导致以下 rowCount() 调用返回正确的值。但是一旦数据库中的某些内容发生了变化(我的类将在 QSqlTableModel 上调用 insertRow() 或 setData()),下一个 QSqlTableModel::rowCount() 调用将再次返回 256。

数据库仅由我的类修改,而我的类又使用该特定的 QSqlTableModel 对象(或使用我的 DataTable 作为模型的视图可以更新某些内容)。所以没有其他进程可以将行插入数据库。

那么我的 DataTable 类什么时候应该为 rowCount() 调用 fetchMore() 以始终返回实际的行数? 我在想我的班级应该将 QSqlTableModel 发出的一些信号连接到一个可以调用 fetchMore() 的插槽,尽管 我不确定这是否是正确/可靠的方法?


更新

这里有一些代码来演示基本问题。

QSqlTableModel *model = new QSqlTableModel(0, database); //QSqlDatabase
model->setTable("tablename");
qDebug() << "0 row count" << model->rowCount(); //0 row count 0 
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
qDebug() << "1 row count" << model->rowCount(); //1 row count 256 
while (model->canFetchMore()) model->fetchMore();
qDebug() << "2 row count" << model->rowCount(); //2 row count 1520 
//... other methods ...
model->setData(model->index(0, 0), "TEST");
model->submitAll();
qDebug() << "3 row count" << model->rowCount(); //3 row count 256 
while (model->canFetchMore()) model->fetchMore();
qDebug() << "4 row count" << model->rowCount(); //4 row count 1520 

加载sql模型后,rowCount()返回256(1),所以需要调用fetchMore()。 rowCount() 然后返回实际的行数。 稍后,数据发生变化,rowCount() 再次返回 256 (3)。

所以似乎 fetchMore() 必须在 sql 模型上的每次写入操作之后调用。但是,与其将这个 while/canFetchMore()/fetchMore() 循环放在修改模型的每个方法的末尾,我想知道连接 beforeInsert(QSqlRecord&)、beforeUpdate(int, QSqlRecord&) 和beforeDelete(int) 向一个插槽发出信号,然后调用 fetchAll()?这是否可靠和合适?

更正:不是在*信号之前(太早),但可能是 layoutChanged()、dataChanged()、rowsInserted() 和 rowsRemoved()。


更新 2

关于 SQL 的注意事项:我知道理论上我可以向数据库发送单独的 SELECT COUNT SQL 查询,但这并不能回答问题。只要能避免SQL,我就不会写SQL。在我看来,发送这样的 SQL 查询违背了面向对象的 QAbstractTableModel 类的目的。加上 rowCount() 是 const (不应该发送查询)并且应该很快。无论如何,这不会修复 rowCount()。

我最终将一个调用 fetchMore() 的插槽连接到相关信号(见上文)AND 断言所有内容都已在 rowCount() 中获取:assert(!sql_model-&gt;canFetchMore())

这是因为 rowCount() 无法向我报告正确的行数计数为失败状态,因此断言。换句话说,我宁愿我的应用程序崩溃也不愿使用不正确的行数。

仅将其连接到 dataChanged() 信号(如 first answer:I would probably try to use dataChanged signal. 中所建议的那样)是不够的。我已将其连接到 dataChanged(const QModelIndex&amp;, const QModelIndex&amp;)rowsInserted(const QModelIndex&amp;, int, int)rowsRemoved(const QModelIndex&amp;, int, int)layoutChanged()

似乎有效,断言还没有失败。

如果有人能具体确认这一点(或解释为什么它并不总是有效),我将不胜感激。

【问题讨论】:

【参考方案1】:

您面临的困境与我最近遇到的类似。我编写了一个 QT gui 程序,它执行以下操作--

我。连接并查询 Oracle 数据库 ii.在 QTableView 中显示查询结果 iii.将 QTableView 的结果导出到 .csv 文件 iv.将 .csv 文件导入 loacl sqlite3 数据库 v.连接并查询本地sqlite3数据库 六。在另一个 QTableview 中执行查询并显示结果

在步骤 ii.(即,将结果导出到 .csv)中,我观察到虽然在 QTableView 上生成了 543 条记录,但只有 256 条记录被导出到 .csv 文件中。

我正在使用,

int rows=model->rowCount();
int columns=model->columnCount();

    for (int i = 0; i < rows; i++)
    
        for (int j = 0; j < columns; j++)
        
            textData += model->data(model->index(i,j)).toString();
            textData += ", ";      // for .csv file format
        
        textData += "\n";             // (optional: for new line segmentation)
    

    QFile csvfile("/home/aj/ora_exported.csv");
    if(csvfile.open(QIODevice::WriteOnly|QIODevice::Truncate))
    
        QTextStream out(&csvfile);
        out<<textData;
    
    csvfile.close();

事实证明,模型在获取所有结果之前读取了 model->rowcount

所以按照 SO 社区的建议,我使用了---

while (model->canFetchMore())
       model->fetchMore();

    int rows=model->rowCount();
    int columns=model->columnCount();

    for (int i = 0; i < rows; i++)
    
        for (int j = 0; j < columns; j++)
        
            textData += model->data(model->index(i,j)).toString();
            textData += ", ";      // for .csv file format
        
        textData += "\n";             // (optional: for new line segmentation)
    

所有记录(全部 543 条)都填充到 .csv 文件中。

您可以在here 参考我的问题。

【讨论】:

感谢您的回答。尽管您遇到了同样的问题,但您的应用程序/场景似乎与我的非常不同。可能最重要的区别是您的方法不是 const 方法,因此可以调用 fetchMore()。就我而言,受影响的方法(我的 rowCount() 实现)是 const,因此它不能(也不应该)调用 fetchMore() - 调用我的 rowCount() 时必须已经获取行数。所以它真的根本不适用于我的情况。 非常感谢。 @RicoRicochet 我最终使用了它并转换为 Python for pyqt5 并将其留在这里以防其他人将来需要它。我在initializedModel `while self.model.canFetchMore(): self.model.fetchMore() self.model.rowCount()`的末尾添加了这个 @MikR 很高兴为您提供帮助。已经有7年左右了。很长一段时间后,我打开了***。妈的,时光飞逝。【参考方案2】:

当前答案没有完全回答问题(提到 dataChanged() 信号,但没有提到其他信号),所以我正在编写自己的答案。

已经有一段时间了,我相信我已经涵盖了所有案例: 我最终将一个调用 fetchMore() 的插槽连接到相关信号并断言所有内容都已在我的 DataTable::rowCount() 方法中获取: 断言(!sql_model->canFetchMore())

(当然 rowCount() 是一个 const 方法,所以如果还没有获取任何内容,我就无法获取,但这无论如何都不是 getter 的工作;断言很好,因为 canFetchMore() 也是 const .)

信号:dataChanged(const QModelIndex&, const QModelIndex&), rowsInserted(const QModelIndex&, int, int), rowsRemoved(const QModelIndex&, int, int) 和 layoutChanged()

我正在使用断言来确保我的模型接收到正确的行数,否则应用程序将崩溃(如果不是所有提到的信号都已连接,则会发生这种情况,例如 layoutChanged())。这对我来说很重要,因为在我的情况下,不正确的行数可能会导致数据丢失。

到目前为止,断言没有失败,所以我假设这可以解决它。

【讨论】:

【参考方案3】:

根据我的经验,Qt 的 SQLite 驱动程序以 256 行的步长将行插入模型中。取数据结束QSqlTableModel::rowCount() 将返回正确的值,无需调用 fetchMore()。这仅取决于您在何时何地调用 rowCount()。

例如,如果用户对rowsInserted()信号进行一些计算,他的slot方法会被多次调用,并且每次最后一行的索引都会递增到256。

因此,您需要在数据获取结束时使用 rowCount()(在某些重写的 QSqlTableModel 方法中,获取通常未完成),或者不依赖中间行计数值并在每次更改时重复计算。

这是一个一般性的答案,因为您没有发布任何代码,我们可以看到您到底想要做什么,但您可以看到我指向的位置。

编辑:

QSqlTableModel::submitAll() 调用之后,模型被重新填充,这就是为什么需要再次重复 fetchMore() 的原因。在哪里调用 fetchMore() 取决于用例。它可以在 submitAll() 之后或在某个插槽中调用,这里没有一般的答案。我可能会尝试使用 dataChanged 信号。但是,获取数据的目标始终应该是在视图中显示数据的主要目标,并且大多数情况下视图会自行完成。

在我的一个应用程序中,我依靠表格视图为我获取数据。将模型设置为表格视图后,我要么调用 QTableView::scrollToBottom() 为我获取数据(在我的情况下,用户应该在底部看到最新数据,滚动后行数具有正确的值)或在 rowsInserted() 插槽中进行计算当用户滚动表格时,再次以 256 行为步长自动获取数据。

知道应用程序是否需要在表中的某处显示行数是非常重要的,或者例如对于一列数据的汇总值,使用额外的QSqlQuery 来获取额外的信息(例如:select count(*) from xyz)然后从大表模型中读取数据通常效率更高。 表格模型主要用于向表格视图提供数据

【讨论】:

感谢您的回答。我在问题中添加了一些代码。我想知道是否应该在触发某些(请参阅问题)信号时调用 fetchMore() 。另外,我不确定“您需要在数据提取结束时使用 rowCount()”是什么意思,调用 rowCount() 如何修复 rowCount() (返回 256)?如果我实际上可以“不依赖中间行数”,那么我可能不得不在加载时将整个表读入内存(在内存中工作)并计算内存中的行数(听起来很浪费)而不是调用 rowCount() ... 嗨!我已经更新了我的答案。也许这可以帮助你一点。 回答您的评论:我没有说调用 rowCount() 将修复 rowCount(),而是仅在获取所有数据而不是获取所有数据时才使用 rowCount()在它之前。 哦,好主意 - 在我的 DataTable::rowCount() 中,在我调用 sql_model->rowCount() 之前,我可以assert(!sql_model-&gt;canFetchMore())(或至少抛出一个错误)。我不能调用 fetchMore(),因为与 rowCount() 不同,它不是 const,但程序至少会失败,而不是默默地认为表有 256 行。 对不起,我不接受这个答案(至少像这样)。 I would probably try to use dataChanged signal. 是错误的,因为 fetchMore() 也必须由某些其他信号触发。而且我还在等待有人确认这个 signal->fetchMore() 方法总是有效的——或者解释为什么它不会。您还建议使用 SQL (SELECT COUNT),但我没有在我的应用程序中编写 SQL。您是正确的,获取更多数据通常是由 Qt 世界中的视图完成的,但这无济于事(行数错误,可能没有视图)。

以上是关于何时或如何将 QSqlTableModel 上的 fetchMore() 与 SQLite 数据库一起使用以使 rowCount() 工作?的主要内容,如果未能解决你的问题,请参考以下文章

何时或为啥要使用右外连接而不是左连接?

何时或为何在 Oracle 数据库中使用“SET DEFINE OFF”

如何将方法标记为过时或弃用?

如何用 QSqlTableModel 中的列替换行?

QSqlTableModel/ QTableView 解析查询到xml

如何在保留选择的同时刷新 QSqlTableModel?