Обработка SQL-запросов
Синтаксические правила
QSqlQuery QSqlDatabase::exec() ::= символьный литерал или символьная переменная
Описание
Выполняет подготовленный (без параметров) SQL-запрос.
Для получения кода завершения выполненного SQL-запроса используется функция lastError()
.
Если текст
пуст, то запрос не выполняется и код завершения не генерируется.
Возвращаемое значение
Указатель на QSqlQuery-объект.
Пример
QSqlDatabase db = QSqlDatabase::addDatabase( "QLINTER"); db.setUserName( "SYSTEM" ); db.setPassword( "MANAGER8" ); if( !db.open() ) { cout << db.lastError().driverText().toLocal8Bit().data() << endl; return 1; } QSqlQuery q; QSqlQuery q = db.exec( QString( "select name from person;" ) ); while ( q.next() ) { cout << q.value(0).toString().toLocal8Bit().data() << "\n"; }
Первый вызов next()
позиционирует QSqlQuery
на первую запись в наборе данных. Последующие вызовы
next()
передвигают указатель на следующую запись и так до тех пор,
пока не будет достигнут конец выборки. В этой точке next()
вернет
false.
Функция value()
возвращает значение поля в виде QVariant
.
Поля нумеруются, начиная с 0, в порядке их следования в предложении SELECT
.
Класс QVariant
может хранить большое количество типов данных языка C++ и Qt,
в том числе int
и QString
. Различные типы данных, которые могут
храниться в БД, переводятся в соответствующие типы C++ и Qt, и сохраняются в виде QVariant
.
Например, VARCHAR
представляется в виде QString
, а DATETIME
– как
QDateTime
.
Класс QSqlQuery
предоставляет целый набор функций для навигации по набору данных:
first()
, last()
, prev()
, seek()
и
at()
. Они удобны в использовании, но при больших объемах выборки могут оказаться медлительными и
ресурсоемкими. С целью оптимизации при работе с большими наборами данных можно вызвать
QSqlQuery::setForwardOnly(true)
перед exec()
, а затем выполнять просмотр
набора данных с помощью next()
(в этом случае получаем однонаправленные наборы данных,
т.е. такие наборы, навигация по которым может осуществляться только вперед, с помощью next()
).
SQL-запрос можно передавать не только как аргумент функции exec()
, но и напрямую,
конструктору QSqlQuery
(в этом случае функция exec()
вызывается
автоматически из конструктора):
QSqlQuery query("SELECT title, year FROM cd WHERE year >= 1998");
Проверку на наличие ошибок и выдачу диагностического сообщения можно выполнить следующим образом:
if (!query.isActive()) cout << query.lastError().driverText().toLocal8Bit().data() << endl;
Выполнение SQL-запросов манипулирования данными аналогично запросам выборки данных:
QSqlQuery query("INSERT INTO cd (id, artistid, title, year) " "VALUES (203, 102, 'Living in America', 2002)");
После выполнения такого запроса QSqlQuery::numRowsAffected()
возвращает количество добавленных записей.
При необходимости вставить в SQL-запрос значения переменных или, когда нежелательно, или невозможно перевести аргументы SQL-предложения в строковый вид, можно построить параметризованный запрос с помощью функции prepare()
. Текст параметризованного запроса вместо реальных значений содержит параметры, которые заполняются фактическими значениями после создания запроса, например:
QSqlDatabase db = QSqlDatabase::addDatabase( "QLINTER" ); db.setUserName( "SYSTEM" ); db.setPassword( "MANAGER8" ); if( !db.open() ) { cout << db->lastError().driverText().toLocal8Bit().data() << endl; return 1; } QSqlQuery query(db); query.prepare("INSERT INTO cd (year, make, model, color) " "VALUES (?, ?, ?, ?)"); query.addBindValue(70); query.addBindValue(QString ("FORD")); query.addBindValue(QString("PANTERA")); query.addBindValue(QString("BLACK")); query.exec();
После создания запроса (вызовом prepare()
), параметры запроса заполняются фактическими значениями с помощью функции bindValue()
или addBindValue()
,
после чего запрос исполняется вызовом exec()
. Параметризованные запросы можно выполнять в цикле. Перед началом цикла создается запрос, а в теле цикла производится заполнение параметров новыми значениями и исполнение запроса.
Параметризованные запросы очень часто используются в тех случаях, когда в БД нужно записать двоичные данные или строки, которые содержат символы из наборов, не принадлежащих диапазону ASCII или Unicode.
Каждое соединение с БД может поддерживать только одну активную транзакцию, поэтому множественные подключения могут оказаться полезными в том случае, когда необходимо одновременно запустить несколько транзакций. При использовании нескольких соединений в Qt-приложении имеется одно неименованное соединение, которое используется по умолчанию объектами QSqlQuery
, если им явно не указать с каким соединением они должны работать.
В дополнение к QSqlQuery
, Qt предоставляет класс QSqlCursor
, производный от QSqlQuery
. Этот класс расширяет функциональность предка большим числом дополнительных методов, которые позволяют отказаться от написания SQL-запросов для SQL-операций, таких как: SELECT
, INSERT
, UPDATE
и DELETE
.
Следующий пример демонстрирует выполнение и обработку результата SELECT-запроса с помощью методов класса QSqlCursor
:
QSqlCursor cursor("cd"); cursor.select("year >= 1998");
Эквивалентный вариант с использованием QSqlQuery
:
QSqlQuery query("SELECT id, artistid, title, year FROM cd " "WHERE year >= 1998");
Навигация по выборке выполняется точно так же, как и в QSqlQuery
, за одним исключением – вместо порядкового номера поля функции value()
можно передать его имя:
while (cursor.next()) { cout << cursor.value("title").toString().toLocal8Bit().data() << "\n"; }
Для вставки записи в таблицу предварительно нужно создать новую запись QSqlRecord
с помощью вызова primeInsert()
, а затем для каждого из полей вызвать функцию setValue()
. После всего этого можно выполнить вставку записи функцией insert()
:
QSqlCursor cursor("cd"); QSqlRecord buffer = cursor.primeInsert(); buffer.setValue("year", 71); buffer.setValue("model", "BUICK"); buffer.setValue("make", "FIAT"); buffer.setValue("color", "BLACK"); cursor.insert();
Чтобы изменить запись, нужно:
-
позиционировать
QSqlCursor
на запись, которая должна подвергнуться изменениям (например, с помощьюselect()
иnext()
); -
получить указатель на
QSqlRecord
вызовомprimeUpdate()
; -
записать новые значения функцией
setValue()
; -
вызвать
update()
, чтобы отправить сделанные изменения в базу данных:
QSqlCursor cursor("cd"); cursor.select("personid = 125"); if (cursor.next()) { QSqlRecord buffer = cursor.primeUpdate(); buffer.setValue("color", "BLACK"); buffer.setValue("year", buffer.value("year").toInt() + 1); cursor.update(); }
Процедура удаления записи похожа на процедуру изменения:
QSqlCursor cursor("cd"); cursor.select("personid = 128"); if (cursor.next()) { cursor.primeDelete(); cursor.del(); }
См. также: lastError()
.