执行 SQL 语句

QSqlQuery 类提供了一个执行 SQL 语句和遍历查询结果集的接口。

在下文中描述的 QSqlQueryModelQSqlTableModel 类提供了一个更高级别的接口,用于访问数据库。如果您不熟悉 SQL,您可以直接跳转到下一节(《使用 SQL 模型类》)。

执行查询

要执行一个 SQL 语句,只需创建一个 QSqlQuery 对象,并调用 QSqlQuery::exec(),如下所示

    QSqlQuery query;
    query.exec("SELECT name, salary FROM employee WHERE salary > 50000");

QSqlQuery 构造函数接受一个可选项 QSqlDatabase 对象,该对象指定了要使用哪个数据库连接。在上面的示例中,我们没有指定任何连接,因此使用的是默认连接。

如果在执行过程中发生错误,exec() 将返回 false。错误信息可以通过 QSqlQuery::lastError() 获得。

QSqlQuery 一次提供一条记录的结果集访问。在调用 exec() 之后,QSqlQuery 的内部指针位于第一条记录的前一个位置。我们必须调用一次 QSqlQuery::next() 来向前推进到第一条记录,然后重复调用 next() 来访问其他记录,直到它返回 false。下面是一个遍历所有记录的典型循环

    while (query.next()) {
        QString name = query.value(0).toString();
        int salary = query.value(1).toInt();
        qDebug() << name << salary;
    }

QSqlQuery::value() 函数返回当前记录中字段的值。字段是通过零基索引指定的。 QSqlQuery::value() 返回一个 QVariant,这是一个可以存储各种 C++ 和 Qt 核心数据类型(如 intQStringQByteArray)的类型。不同的数据库类型会自动映射到最接近的 Qt 等效类型。在代码片段中,我们使用 QVariant::toString() 和 QVariant::toInt() 将变体转换为 QStringint

有关使用 Qt 支持的数据库的推荐类型的概述,请参阅此表格

您可以使用 QSqlQuery::next()、QSqlQuery::previous()、QSqlQuery::first()、QSqlQuery::last() 和 QSqlQuery::seek() 在数据集中进行导航。当前行索引由 QSqlQuery::at() 返回,而对于支持该功能的数据库,结果集中的总行数可以通过 QSqlQuery::size() 获取。

要确定数据库驱动程序是否支持特定功能,请使用 QSqlDriver::hasFeature()。在以下示例中,我们调用 QSqlQuery::size() 来确定支持该功能的底层数据库结果集的大小;否则,我们将导航到最后一条记录,并使用查询的位置告诉我们有多少条记录。

    QSqlQuery query;
    int numRows;
    query.exec("SELECT name, salary FROM employee WHERE salary > 50000");

    QSqlDatabase defaultDB = QSqlDatabase::database();
    if (defaultDB.driver()->hasFeature(QSqlDriver::QuerySize)) {
        numRows = query.size();
    } else {
        // this can be very slow
        query.last();
        numRows = query.at() + 1;
    }

如果在结果集中进行导航,并且只用 next() 和 seek() 进行向前浏览,可以在调用 exec() 之前调用 QSqlQuery::setForwardOnly(true),这是一个简单的优化,可以显著加快在大结果集上进行操作时的查询速度。

插入、更新和删除记录

QSqlQuery 可以执行任意 SQL 语句,而不仅仅是 SELECT 语句。以下示例显示了如何使用 INSERT 将记录插入到表中的示例。

    QSqlQuery query;
    query.exec("INSERT INTO employee (id, name, salary) "
               "VALUES (1001, 'Thad Beaumont', 65000)");

如果您想同时插入多条记录,通常更有效的方法是将查询与实际插入的值分开。这可以通过占位符来完成。Qt 支持两种占位符语法:命名绑定和位置绑定。以下是一个命名绑定的示例

    QSqlQuery query;
    query.prepare("INSERT INTO employee (id, name, salary) "
                  "VALUES (:id, :name, :salary)");
    query.bindValue(":id", 1001);
    query.bindValue(":name", "Thad Beaumont");
    query.bindValue(":salary", 65000);
    query.exec();

以下是一个位置绑定的示例

    QSqlQuery query;
    query.prepare("INSERT INTO employee (id, name, salary) "
                  "VALUES (?, ?, ?)");
    query.addBindValue(1001);
    query.addBindValue("Thad Beaumont");
    query.addBindValue(65000);
    query.exec();

这两种语法都与 Qt 提供的所有数据库驱动程序一起使用。如果数据库原生支持该语法,Qt 将简单地将查询转发到 DBMS;否则,Qt 通过预处理查询来模拟占位符语法。最终由 DBMS 执行的实际查询可通过 QSqlQuery::executedQuery() 获取。

当插入多条记录时,您只需要调用 QSqlQuery::prepare() 一次。然后根据需要多次调用 bindValue() 或 addBindValue(),然后调用 exec()。

除了性能之外,占位符的一个优点是您可以轻松指定任意值,无需担心转义特殊字符。

更新记录的操作与将其插入到表中相似

    QSqlQuery query;
    query.exec("UPDATE employee SET salary = 70000 WHERE id = 1003");

您还可以使用命名或位置绑定将参数关联到实际值。

最后,这是一个 DELETE 语句的示例

    QSqlQuery query;
    query.exec("DELETE FROM employee WHERE id = 1007");

事务

如果底层数据库引擎支持事务,QSqlDriver::hasFeature(QSqlDriver::Transactions) 将返回 true。您可以使用 QSqlDatabase::transaction() 来启动事务,然后是要在事务上下文中执行的 SQL 命令,最后是 QSqlDatabase::commit() 或 QSqlDatabase::rollback。在使用事务时,您必须在创建查询之前启动事务。

示例

    QSqlDatabase::database().transaction();
    QSqlQuery query;
    query.exec("SELECT id FROM employee WHERE name = 'Torild Halvorsen'");
    if (query.next()) {
        int employeeId = query.value(0).toInt();
        query.exec("INSERT INTO project (id, name, ownerid) "
                   "VALUES (201, 'Manhattan Project', "
                   + QString::number(employeeId) + ')');
    }
    QSqlDatabase::database().commit();

事务可以用来确保复杂操作是原子的(例如,查找外键和创建记录),或者提供一个在中间取消复杂更改的手段。

© 2024 The Qt Company Ltd. 本文档中包含的贡献文档的版权属于各自的所有者。本文档提供的文档根据自由软件基金会发布的条款,许可在 GNU自由文档许可证版本1.3 之下。Qt及其相关标识是芬兰和/或其他国家的The Qt Company Ltd.的商标。所有其他商标均为各自所有者的财产。