bool Sqlite3::exportMergeTableData(const QString ¤tDate, const QString &csvFilePath)
{
QString allSql = QString("select bms%1.*,bm%2Voltage.*,bms%3Temperature.* from bms%4 join bms%5Voltage on bms%6.batteryID = bms%7Voltage.batterID join bms%8Temperature on bms%9.batteryID = bms%19Temperature.batteryID").arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate);
qDebug()<<allSql;
QElapsedTimer timer;
timer.start();
QSqlQuery query;
if (!query.exec(allSql)) {
qDebug() << "查询失败:" << query.lastError().text();
return false;
}
QFile csvFile(csvFilePath);
if (!csvFile..open(QIODevice::WriteOnly | QIODevice::Text)) {
qDebug() << "无法创建 CSV 文件:" << csvFile.errorString();
return false;
}
QTextStream out(&csvFile);
for (int i = 0; i < query.record().count(); ++i) {
out << query.record().fieldName(i);
if (i< query.record().count() - 1)
out << ",";
}
out << "\n";
while (query.next()) {
for (int i = 0; i < query.record().count(); ++i) {
out << query.value(i).toString();
if (i < query.record().count() - 1)
out << ",";
}
out << "\n";
}
csvFile.close();
qDebug()<<"导出时间:"<<timer.elapsed();
db.close();
qDebug() << "表" << tableName << "已成功导出到" << csvFilePath;
return true;
}
概述:
这是一个传入数据表的日期和文件保存路径,然后根据日期组合成数据表,在把三个数据表的数据合并后导出成csv文件的函数,经过优化导出6000行数据,从87678ms缩短到563ms。
问题:
导出一个6000行的数据,需要耗时87678ms,非常慢,而且没有使用线程,导致用户界面卡顿严重。正常的最大导出数据有8w多行,更卡,无法正常使用。
分析问题:
经过测试,发现问题出现在这段代码里面的,query.value(i).toString();位置。每次都是单个字段读取,数据表数据又非常大,所以这里耗时非常大,但这里就大概需要82655ms。
while (query.next()) {
for (int i = 0; i < query.record().count(); ++i) {
out << query.value(i).toString();
if (i < query.record().count() - 1)
out << ",";
}
out << "\n";
}
问题处理:
优化这段代码,改成这种方式,优化后6000行代码,只需要563ms,优化思路如下:
1、减少query.value(i) 的调用次数:
使用QSqlRecord一次性获取整行数据,而不是逐列调用
2、批量写入csv文件,积累多行后一起写入,减少IO的开销
进一步优化:
如果数据量非常庞大,可以考虑:
1、分页查询:使用LIMIT和OFFSET分批次读取数据
2、多线程处理:将数据读取和数据写入分离到不同的线程
优化后的方案:
bool Sqlite3::exportMergeTableData(const QString ¤tDate, const QString &csvFilePath)
{
QString allSql = QString("select bms%1.*,bm%2Voltage.*,bms%3Temperature.* from bms%4 join bms%5Voltage on bms%6.batteryID = bms%7Voltage.batterID join bms%8Temperature on bms%9.batteryID = bms%19Temperature.batteryID").arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate).arg(currentDate);
qDebug()<<allSql;
QElapsedTimer timer;
timer.start();
QSqlQuery query;
if (!query.exec(allSql)) {
qDebug() << "查询失败:" << query.lastError().text();
return false;
}
QFile csvFile(csvFilePath);
if (!csvFile..open(QIODevice::WriteOnly | QIODevice::Text)) {
qDebug() << "无法创建 CSV 文件:" << csvFile.errorString();
return false;
}
QTextStream out(&csvFile);
for (int i = 0; i < query.record().count(); ++i) {
out << query.record().fieldName(i);
if (i< query.record().count() - 1)
out << ",";
}
out << "\n";
const int bufferSize = 1000;
QString buff;
int rowCount = 0;
QSqlRecord record = query.record();
int columnCount = record.count();
while (query.next()) {
QString rowData;
for(int i = 0;i<columnCount;++i){
rowData.append(query.value(i).toString());
if(i<columnCount-1){
rowData.append(",");
}
}
buffer.append(rowData).append("\n");
rowCount++;
if(rowCount%bufferSize == 0){
out<<buffer;
out.flush();
buffer.clear();
}
}
if(!buffer.isEmpty()){
out<<buffer;
}
csvFile.close();
qDebug()<<"导出时间:"<<timer.elapsed();
db.close();
qDebug() << "表" << tableName << "已成功导出到" << csvFilePath;
return true;
}