while (query.next()) {
QString title = query.value(0).toString();
int year = query.value(1).toInt();
cerr << qPrintable(title) << ": " << year << endl;
}
Мы вызываем функцию next() один раз для позиционирования QSqlQuery на первую запись полученного набора. Последующие вызовы next() продвигают указатель записи на одну позицию дальше, пока не будет достигнут конец, когда функция next() возвращает false. Если результирующий набор (result set) пустой (или запрос завершается неудачей), первый вызов функции next() возвратит false.
Функция value() возвращает значение поля, как QVariant. Поля пронумерованы начиная с 0 в порядке их указания в команде SELECT. Класс QVariant может содержать многие типы С++ и Qt, включая int и QString. Другие типы данных, которые могут храниться в базе данных, преобразуются в соответствующие типы С++ и Qt и хранятся в QVariant. Например, VARCHAR представляется в виде QString, a DATETIME — в виде QDateTime.
Класс QSqlQuery содержит некоторые другие функции для просмотра результирующего набора: first(), last(), previous() и seek(). Эти функции удобны, но для некоторых баз данных они могут выполняться медленнее и расходовать памяти больше, чем функция next(). При работе с большими наборами данных мы можем осуществить простую оптимизацию, вызывая функцию QSqlQuery::setForwardOnly(true) перед вызовом exec(), и только затем использовать next() для просмотра результирующего набора.
Ранее мы задавали запрос SQL в аргументе функции QSqlQuery::exec(), но, кроме того, мы можем передавать его непосредственно конструктору, который сразу же выполнит его:
QSqlQuery query("SELECT title, year FROM cd WHERE year >= 1998");
Мы можем проверить наличие ошибки, вызывая функцию isActive() для запроса:
if (!query.isActive())
QMessageBox::warning(this, tr("Database Error"),
query.lastError().text());
Если ошибки нет, запрос становится «активным» и мы можем использовать next() для перемещения по результирующему набору.
Выполнение команды INSERT осуществляется почти так же просто, как и команды SELECT:
QSqlQuery query("INSERT INTO cd (id, artistid, title, year) "
"VALUES (203, 102, 'Living in America', 2002)");
После этого функция numRowsAffected() возвращает количество строк, которые были изменены инструкцией SQL (или —1, если возникла ошибка).
Если нам необходимо вставлять много записей или если мы хотим избежать преобразования значений в строковые данные (и правильного преобразования специальных символов), мы можем использовать функцию prepare() для указания полей в шаблоне запроса и затем присваивания им необходимых нам значений. Qt поддерживает как стиль Oracle, так и стиль ODBC для всех баз данных, применяя, где возможно, «родной» интерфейс базы данных или имитируя его в противном случае. Ниже приводится пример, в котором используется синтаксис Oracle для представления поименованных полей:
QSqlQuery query;
query.prepare("INSERT INTO cd (id, artistid, title, year) "
"VALUES (:id, :artistid, :title, :year)");
query.bindValue(":id", 203);
query.bindValue(":artistid", 102);
query.bindValue(":title", "Living in America");
query.bindValue(":year", 2002);
query.exec();
Ниже приводится тот же пример позиционного представления полей в стиле ODBC:
QSqlQuery query;
query.prepare("INSERT INTO cd (id, artistid, title, year) "
"VALUES (?, ?, ?, ?)");
query.addBindValue(203);
query.addBindValue(102);
query.addBindValue("Living in America");
query.addBindValue(2002);
query.exec();
После вызова функции exec() мы можем вызвать bindValue() или addBindValue() для присваивания новых значений, затем снова вызвать exec() для выполнения запроса уже с новыми значениями.