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