Обработка SQL-запросов
Синтаксические правила
QSqlQuery QSqlDatabase::exec(< SQL-запрос >) < SQL-запрос >::= символьный литерал или символьная переменная
Описание
Выполняет подготовленный (без параметров) SQL-запрос.
Для получения кода завершения выполненного SQL-запроса используется функция lastError()
.
Если текст < SQL-запроса >
пуст, то запрос не выполняется и код завершения не генерируется.
Возвращаемое значение
Указатель на 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()
.