一.执行数据库命令
CREATE DATABASE IF NOT EXISTS bak_db; //创建一个名为bak_db的数据库
USE bak_db;
CREATE TABLE IF NOT EXISTS water ( //创建一个名为water的数据表
id INT AUTO_INCREMENT PRIMARY KEY,
data_value VARCHAR(255) NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO write (data_value, timestamp) VALUES
('RandomValue1', NOW() - INTERVAL FLOOR(RAND() * 365) DAY),
('RandomValue2', NOW() - INTERVAL FLOOR(RAND() * 365) DAY),
('RandomValue3', NOW() - INTERVAL FLOOR(RAND() * 365) DAY),
// 加上其他17行以生成20个总条目
('RandomValue20', NOW() - INTERVAL FLOOR(RAND() * 365) DAY);
请注意,以上 SQL 代码需要在数据库管理工具或 SQL 客户端中执行。data_value
字段被设置成一个简单的字符串,你可以根据需要调整它。timestamp
字段自动生成当前时间,并随机减去最多365天,以模拟不同的时间点。
二.设计Qt程序
1.打开Qt Creator并创建一个新的项目,命名为project2
。
编辑.pro文件
QT += core gui sql
QT += widgets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = MeterReadingsDownloader
TEMPLATE = app
CONFIG += c++11
SOURCES += main.cpp \
mainwindow.cpp \
databasemanager.cpp
HEADERS += mainwindow.h \
databasemanager.h
FORMS += mainwindow.ui
2.创建DataManager类
创建一个datamanager.h
头文件,定义DataManager
类,这个类会处理数据库连接和数据抽取的逻辑:
#ifndef DATABASEMANAGER_H
#define DATABASEMANAGER_H
#include <QString>
#include <QSqlQuery>
class DatabaseManager {
public:
DatabaseManager();
void downloadData(const QString& fileName, const QString& dateOption);
};
#endif // DATABASEMANAGER_H
3.创建一个datamanager.cpp
源文件,实现DataManager
类的方法:
#include "databasemanager.h"
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QFile>
#include <QTextStream>
#include <QDebug>
DatabaseManager::DatabaseManager()
{
}
void DatabaseManager::downloadData(const QString& fileName, const QString& dateOption)
{
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("localhost");
db.setDatabaseName("your_database"); //数据库名
db.setUserName("your_username"); //用户名
db.setPassword("your_password"); //用户密码
if (!db.open()) {
qDebug() << "Database connection failed:" << db.lastError();
return;
}
// Prepare the SQL query based on the dateOption
QString queryStr;
if (dateOption == "按天") {
queryStr = "SELECT reading_date, SUM(reading) FROM meter_readings GROUP BY reading_date;";
} else if (dateOption == "按月") {
queryStr = "SELECT DATE_FORMAT(reading_date, '%Y-%m') AS month, SUM(reading) FROM meter_readings GROUP BY month;";
} else if (dateOption == "按年") {
queryStr = "SELECT YEAR(reading_date) AS year, SUM(reading) FROM meter_readings GROUP BY year;";
}
QSqlQuery query(queryStr, db);
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qDebug() << "Couldn't open file for writing.";
db.close();
return;
}
QTextStream out(&file);
while (query.next()) {
out << query.value(0).toString() << "," << query.value(1).toString() << "\n";
}
file.close();
db.close();
}
4.编写main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QMainWindow>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
5.创建UI界面
在 mainwindow.ui
使用 Qt Designer 来创建一个简单的界面,包括一个下拉菜单(QComboBox)和一个按钮(QPushButton)用于触发下载。
6.编写mainwindow.h文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_downloadButton_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
7.编写mainwindow.cpp文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "databasemanager.h"
#include <QFileDialog>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Assume the comboBox and downloadButton have been set up in the UI designer with these object names
connect(ui->downloadButton, &QPushButton::clicked, this, &MainWindow::on_downloadButton_clicked);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_downloadButton_clicked()
{
QString dateOption = ui->comboBox->currentText();
QString fileName = QFileDialog::getSaveFileName(this, tr("保存CSV文件"), "", tr("CSV文件 (*.csv)"));
if (!fileName.isEmpty()) {
DatabaseManager dbManager;
dbManager.downloadData(fileName, dateOption);
}
}
三.注意
-
数据库认证信息安全: 数据库的用户名和密码不应该硬编码在源代码中。应该使用配置文件、环境变量或者安全的加密存储方式来管理敏感信息。
-
错误处理: 数据库连接、SQL查询、文件操作等都有可能失败,因此需要有充分的错误处理机制,比如使用Qt的异常处理以及
QSqlQuery
的错误检查功能。 -
SQL注入防范: 使用参数化查询或Qt的SQL绑定功能来防止SQL注入攻击。
-
资源管理: 注意资源管理,确保数据库连接和文件句柄在使用完毕后能够被正确释放。可以利用Qt的父子对象机制及智能指针来管理资源。
-
文件保存格式: 当创建CSV文件时,确保字段值中没有包含逗号或新行等可能引起格式混淆的字符。如果有,应该正确地转义这些值或用引号包围。
-
字符编码: 文件应按照一个统一的字符编码进行保存,例如UTF-8。这对于国际化很重要,特别是当内容包含非ASCII字符时。
-
文件路径和权限: 用户选择的文件保存路径需要有写入权限,且要处理路径不存在或文件无法创建等情况。
-
Qt版本兼容性: 确保你使用的QT版本与你的代码兼容。如果你需要在不同的操作系统上部署,还要考虑跨平台的兼容性问题。
-
内存管理: 当使用Qt时,特别是在创建对象时,要注意父子关系,因为Qt使用的是父子层次结构来管理内存。
-
用户体验: 文件导出可能需要一些时间,特别是数据量大的时候。考虑使用异步编程或者单独的工作线程来处理长时间运行的任务,以避免阻塞UI线程。
-
数据类型匹配: 当处理数据库和CSV文件时,确保数据类型在转换过程中保持一致。
-
测试: 进行充分的单元测试和集成测试来确保应用程序按预期工作。