QSqlQuery理论总结
一、概述
QSqlQuery类简而言之其实就是来执行SQL语言的工具类,QSqlQuery封装了在QSqlDatabase上执行的SQL查询中创建、导航和检索数据所涉及的功能。它可用于执行DML(数据操作语言)语句,如SELECT、INSERT、UPDATE和DELETE,以及DDL(数据定义语言)语句,如CREATE TABLE。它也可以用于执行非标准SQL的数据库特定命令(例如SET DATESTYLE=ISO for PostgreSQL)。也就是支持不同数据库的SQL特性。
成功执行的SQL语句将查询的状态设置为active,以便isActive()返回true。否则查询的状态将被设置为inactive。在任何一种情况下,当执行新的SQL语句时,查询都定位在无效记录上。在检索值之前,必须将活动查询导航到有效记录(以便isValid()返回true)。
对于某些数据库,如果在调用commit()或rollback()时存在一个活动查询(即SELECT语句),则提交或回滚将失败。也就是要在Select 执行完,提交回滚才会成功。详情请参见isActive()。
二、使用
1. 数据查询
导航主要是指在我们执行了查询后,我们应该会得到一个记录表,我们要在这个记录表里索引数据的时候就要用这个导航,导航记录的功能如下:
next()
previous()
first()
last()
seek()
这些函数允许我们向前、向后或任意地通过查询返回的记录。如果我们只需要通过结果向前移动(例如,通过使用next() ),您可以使用 setForwardOnly(),这将节省大量的内存开销,并提高某些数据库的性能。一旦活动查询定位到有效记录上,就可以使用value()检索数据。所有数据都是使用 QVariants 从SQL后端传输的,也就是每个字段的数据都是一个 QVariants 类型。
查询例子例子:
QSqlQuery query("SELECT country FROM artist");
while (query.next()) {
QString country = query.value(0).toString();
doSomething(country);
}
要访问查询返回的数据,请使用value(int)。SELECT语句返回的数据中的每个字段都是通过传递字段在语句中的位置来访问的,从0开始。这使得使用SELECT *查询是不可取的,因为返回字段的顺序是不确定的。
为了提高效率,没有通过名称访问字段的函数(除非您使用带名称的预准备查询,如下所述)。要将字段名转换为索引,请使用record().indexOf(),例如:
QSqlQuery query("SELECT * FROM artist");
int fieldNo = query.record().indexOf("country");
while (query.next()) {
QString country = query.value(fieldNo).toString();
doSomething(country);
}
2.查询参数绑定
查询参数绑定其实就是在使用基本上所有的查询、删除、更新、插入数据的时候,得跟一些关键字,我们用参数化好像更安全些。
QSqlQuery 支持准备好的查询执行和将参数值绑定到占位符。有些数据库不支持这些特性,因此Qt模拟了所需的功能。例如,Oracle和ODBC驱动程序有适当的准备好的查询支持,Qt就用的现成的,但对于不支持此功能的数据库,Qt自己实现了该功能,例如在执行查询时用实际值替换占位符。使用numRowsAffected()来确定非SELECT查询影响了多少行,使用size()来确定SELECT查询检索了多少行。
Oracle数据库使用冒号-名称语法来标识占位符,例如 :name 。ODBC简单地使用 ? 字符。Qt同时支持这两种语法,但限制是不能在同一个查询中混合使用它们。我推荐就还是直接用ODBC连接就好啦。
我们还可以使用boundValues()检索单个变量(映射)中所有字段的值。使用如下
//方法一
QMapIterator<QString, QVariant> i(query.boundValues());
while (i.hasNext()) {
i.next();
cout << i.key().toUtf8().data() << ": "
<< i.value().toString().toUtf8().data() << Qt::endl;
}
//方法二
QList<QVariant> list = query.boundValues().values();
for (int i = 0; i < list.size(); ++i)
cout << i << ": " << list.at(i).toString().toUtf8().data() << Qt::endl;
注意:并非所有SQL操作都支持绑定值。这个是每个数据库自己支不支持的问题,参考数据库系统的文档来检查它们是否可用。
3. 绑定值的方法
下面我们将分别使用四种不同的绑定方法给出相同的示例,以及一个将值绑定到存储过程的示例。(下面不仅仅局限于 Insert ,我们也可以用 Select、Update等等)
1. 使用命名占位符的命名绑定:
QSqlQuery query;
query.prepare("INSERT INTO person (id, forename, surname) "
"VALUES (:id, :forename, :surname)");
query.bindValue(":id", 1001);
query.bindValue(":forename", "Bart");
query.bindValue(":surname", "Simpson");
query.exec();
2. 使用命名占位符的位置绑定:
QSqlQuery query;
query.prepare("INSERT INTO person (id, forename, surname) "
"VALUES (:id, :forename, :surname)");
query.bindValue(0, 1001);
query.bindValue(1, "Bart");
query.bindValue(2, "Simpson");
query.exec();
3. 使用位置占位符绑定值(版本1):
QSqlQuery query;
query.prepare("INSERT INTO person (id, forename, surname) "
"VALUES (?, ?, ?)");
query.bindValue(0, 1001);
query.bindValue(1, "Bart");
query.bindValue(2, "Simpson");
query.exec();
4. 使用位置占位符绑定值(版本2):
QSqlQuery query;
query.prepare("INSERT INTO person (id, forename, surname) "
"VALUES (?, ?, ?)");
query.addBindValue(1001);
query.addBindValue("Bart");
query.addBindValue("Simpson");
query.exec();
5. 将值绑定到存储过程:
这段代码调用一个名为AsciiToInt()的存储过程,通过其in形参向其传递一个字符,并在out形参中获取其结果。
QSqlQuery query;
query.prepare("CALL AsciiToInt(?, ?)");
query.bindValue(0, "A");
query.bindValue(1, 0, QSql::Out);
query.exec();
int i = query.boundValue(1).toInt(); // i is 65
注意,未绑定的参数将保留它们的值。
警告:在创建QSqlQuery之前,必须加载SQL驱动程序并打开连接。此外,当查询存在时,连接必须保持打开状态;否则,QSqlQuery的行为是未定义的。
三、常见问题
很多问题会跟不同数据有关系,推荐最好使用官方的写法: QSqlError(“0“, “QODBC3: Unable to execute statement“, “[Microsoft][ODBC 驱动程序管理器] 函数序列错误“)