Обработка 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().