Содержание


Практическое использование MySQL++

Часть 6. Специализированные формы запросов

Comments

Серия контента:

Этот контент является частью # из серии # статей: Практическое использование MySQL++

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Практическое использование MySQL++

Следите за выходом новых статей этой серии.

Запросы с параметрами или шаблоны запросов (template queries) представляют особый интерес в тех случаях, когда приходится выполнять множество почти одинаковых по форме команд SQL, отличающихся лишь фактическими параметрами в условиях отбора данных. С них мы и начнём, а затем рассмотрим некоторые другие специализированные запросы: команды создания таблиц в базе данных и процедуры извлечения, добавления и изменения данных при помощи специализированных структур SSQLS (Specialized SQL Structures).

1. Параметризованные запросы

Библиотека MySQL++ предоставляет возможность формирования и использования так называемых шаблонов запросов (template queries), то есть, запросов, в которых конкретные элементы условий выбора строк данных из таблиц заменены формальными параметрами. Можно провести некоторую аналогию с форматом функции printf(), широко применяемой в языке программирования C: вы передаёте MySQL++ строку, содержащую постоянные части запроса и форматные шаблоны (placeholdres) для переменных элементов, которые впоследствии будут заменяться соответствующими значениями.

1.1. Пример формирования и использования шаблона запроса

Снова обратимся к таблице товаров wares, с которой мы работали в предыдущих статьях. Её структура остаётся неизменной:

name  CHAR(25) NOT NULL (Наименование товара)
num   INT   (Количество единиц товара)
price REAL  (Цена единицы товара)

Рассмотрим следующий код, в котором демонстрируется практическое применение шаблона запроса с параметрами:

#include <mysql++.h>
#include <iostream>
#include <iomanip>

use namespace std;

int main( int argc, char *argv[] )
{
  try
  {
    // установление соединения с тестовой базой данных
    mysqlpp::Connection con( "test_db", "localhost", "tdb_user", "tdb_password" );
    // формирование строки шаблона запроса
    mysqlpp::Query query = con.query( "SELECT * FROM wares WHERE name = %0q" );
    // активизация созданного шаблона запроса
    query.parse();

    // выполнение первого запроса - информация обо всех столах
    mysqlpp::StoreQueryResult res1 = query.store( "Стол" );
    // выполнение второго запроса - информация обо всех шкафах
    mysqlpp::StoreQueryResult res2 = query.store( "Шкаф" );
    // Примечание: вывод извлечённых по запросам данных здесь
    // не производится в целях экономии места.
    // Пользуясь исходным кодом примеров из предыдущих статей цикла,
    // вы без труда организуете вывод с целью проверки результатов.

    query.reset();  // строка ранее сохранённого шаблона запроса удаляется
    // теперь можно сформировать новую строку шаблона запроса
    query << "UPDATE wares SET name = %0q WHERE name = %1q";
    query.parse();
    mysqlpp::SimpleResult res3 = query.execute( "Шкаф книжный", res2[0][0].c_str() );
    // Для проверки результатов изменения данных здесь можно вывести
    // содержимое всей таблицы wares
  }
  catch( const mysqlpp::BadQuery &erq )
  {
    cerr << "Ошибка запроса: " << erq.what() << endl;
    return -1;
  }
  catch( const mysqlpp::Exception &ex )
  {
    cerr << "Ошибка: " << ex.what() << endl;
    return -1;
  }
  return 0;
}

Принцип использования шаблона запроса прост: сначала формируется строка запроса, содержащая формальные параметры, затем с помощью функции parse() этот шаблон запроса активизируется. После этого вы можете многократно обращаться к данному шаблону, вызывая любой метод класса Query и передавая в него требуемые параметры. В данном примере применялся метод Query::execute().

1.2. Настройка элементов шаблона запроса

Как было показано в приведённом выше примере, при формировании строки шаблона запроса в объекте типа Query используются пронумерованные элементы шаблона, размещаемые в тех позициях строки, где в дальнейшем будут выполняться подстановки фактических параметров. Вызываемая после создания шаблона функция parse() сообщает Query-объекту о том, что переданная ему строка является шаблоном и требует соответствующей обработки:

query << "SELECT (%2:field1, %3:field2) FROM wares WHERE %1:sel_field = %0q:str";
query.parse();

Вообще говоря, элементы, обозначающие места расположения формальных параметров в строке шаблона, имеют следующий формат:

%NNN[модификатор][:имя][:]

Обязательными являются только символ % (процент) и номер элемента NNN (может содержать до трёх цифр). Нумерация элементов начинается с нуля. Порядок следования параметров передаётся в объект SQLQueryParms.

"Модификаторы":

  • % - собственно символ "процент";
  • "" - не заключать в кавычки и не экранировать содержимое фактического параметра;
  • q - выполнять экранирование спец.символов фактического параметра с помощью MySQL C API-функции mysql_escape_string() и заключить фактический параметр в одиночные кавычки, если это необходимо, в зависимости от типа используемого значения;
  • Q - Заключить содержимое фактического параметра в кавычки, но не выполнять экранирование. Иногда это позволяет сэкономить немного времени на обработку, если точно известно, что строка не содержит символов, требующих экранирования.

Для любого элемента можно определить ":имя", которое будет использоваться в объекте SQLQueryParms. Имя может содержать алфавитно-цифровые символы и символ подчёркивания. Для того, чтобы явно обозначить окончание имени, можно использовать необязательный символ "двоеточие" (например, "%2:field1:"). Если после имени должно располагаться двоеточие, то необходимо записать два "хвостовых" двоеточия подряд - "%3:field2::". В этом случае первое "хвостовое" двоеточие будет интерпретировано, как окончание имени, а второе останется "как есть", без изменений.

1.3. Передача фактических параметров во время выполнения

Для того, чтобы передать фактические значения параметров для подстановки их в строку запроса, необходимо вызвать метод Query::store( const SQLString &parameter0 [, ..., const SQLString &parameterN] ). Кроме того, соответствующим образом перегружаемыми являются и другие методы выполнения запросов Query::storein(), Query::use() и Query::execute(). В списке аргументов parameter0 соответствует первому передаваемому фактическому параметру и т.д. Всего можно задать до 25 передаваемых параметров. Для определённого в предыдущем подразделе шаблона запроса передача фактических значений параметров может выглядеть следующим образом:

StoreQueryResult res = query.store( "Шкаф книжный", "name", "name", "price" );

после чего строка запроса должна принять такой вид:

SELECT (name, price) FROM wares WHERE name = "Шкаф книжный"

У читателя может возникнуть вопрос: почему в строке шаблона запроса так нелогично пронумерованы формальные параметры - не в порядке их естественного следования? Ответ - в следующем подразделе.

1.4. Значения параметров, принимаемые по умолчанию

Механизм формирования шаблонов запросов позволяет определять значения параметров по умолчанию. Значение, устанавливаемое по умолчанию, присваивается элементу массива Query::template_defaults с соответствующим индексом. Вы можете выполнять присваивание, обращаясь с нужному элементу по его номеру (позиции) или по имени (если оно было определено в шаблоне запроса), например:

query.template_defaults[3] = "price";

или (с тем же результатом)

query.template_defaults["field2"] = "price";

Здесь обнаруживается большое сходство с механизмом определения параметров по умолчанию в функциях языка C++: если параметры с присвоенными им значениями по умолчанию расположены в конце списка, то при вызове методов выполнения запросов класса Query не обязательно явно указывать все передаваемые значения. Если запрос принимает четыре параметра, а для двух последних (по порядку номеров) вы установили значения по умолчанию, то при вызове метода выполнения запроса в него могут быть переданы как минимум два явных фактических параметра.

В этом и заключается причина такой "беспорядочной" нумерации формальных параметров в шаблоне запроса из подраздела 1.2. Параметры, значения которых предполагается менять более часто, получают "начальные" номера в списке. Параметры, вероятность изменения значений которых минимальна, размещаются ближе к концу списка с тем, чтобы присвоить им значения по умолчанию и пользоваться ими во всех подходящих случаях. Например:

query.template_defaults["field1"] = "name";
query.template_defaults["field2"] = "price";
StoreQueryResult res1 = query.store( "Шкаф книжный", "name" );
StoreQueryResult res2 = query.store( 5000.00, "price" );
StoreQueryResult res3 = query.store( 20, "num" );

В результате для res1 запрос будет выглядеть так:

SELECT (name, price) FROM wares WHERE name = "Шкаф книжный"

для res2:

SELECT (name, price) FROM wares WHERE price = 5000.00

и для res3:

SELECT (name, price) FROM wares WHERE num = 20

Очевидно, что использование значений по умолчанию наиболее полезно, когда некоторые параметры остаются неизменными (имена полей, включаемых в выборку данных в приведённом примере), а другие параметры всё время меняются.

2. Создание таблиц БД

Здесь мы приступаем к рассмотрению специализированных структур SSQLS (Specialized SQL Structures), которые позволяют определять структуры языка C++, соответствующие формату SQL-таблиц. Любая SSQLS-структура содержит члены-переменные, соответствующие отдельным полям SQL-таблицы. Но кроме этого в подобной структуре имеются методы, операторы и члены-данные, используемые для обеспечения внутренней функциональности MySQL++.

SSQLS-структуры создаются при помощи макросов, определённых в файле ssqls.h. Это специализированный заголовочный файл библиотеки MySQL++, который не включается автоматически через заголовочный файл mysql++.h. Если вы хотите воспользоваться функциональными возможностями SSQLS-структур, то должны явно включить файл ssqls.h в свой исходный код.

2.1. Процедура создания таблицы базы данных

Если для создания таблицы требуется выполнение запроса:

CREATE TABLE wares (
  name CHAR(25) NOT NULL,
  num INT,
  price REAL )

то в терминах языка C++ соответствующая структура может быть определена следующим образом (разумеется, при условии использования библиотеки MySQL++):

sql_create_3( wares, 1, 3,
  mysqlpp::sql_char, name,
  mysqlpp::sql_int, num,
  mysqlpp::sql_real, price )

Эта макрокоманда объявляет структуру wares, в которой содержатся имена всех полей SQL-таблицы с указанием соответствующего типа данных. В библиотеке MySQL++ определены C++-типы практически для всех MySQL-типов данных в форме sql_*.

Обобщённый синтаксис макрокоманды для создания SSQLS-структуры выглядит так:

sql_create_N( имя, comp_count, set_count, тип1, поле1, ..., типN, полеN )

Здесь N - количество полей в таблице и соответственно количество членов-переменных структуры, "имя" - имя структуры (имя таблицы), "тип#" - тип данных члена-переменной, а "поле#" - имя этого члена-переменной (совпадает с именем поля таблицы).

Второй и третий параметры требуют дополнительных разъяснений. Макрокоманда sql_create_N добавляет члены-функции и операторы в каждую SSQLS-структуру, чтобы обеспечить сравнение одного экземпляра такой структуры с другим. Эти функции сравнивают первые "comp_count" полей (членов-переменных) в данной структуре. В нашем примере создания структуры wares значение comp_count равно 1, поэтому при сравнении двух структур типа wares будут сравниваться значения только одного поля name.

Это свойство наиболее эффективно, когда ключевые поля таблицы занимают первые позиции в струкуре SSQLS, а значение параметра comp_count равно количеству этих ключевых полей.

Значение comp_count не должно быть меньше 1. Это требование текущей реализации SSQLS-структур в библиотеке MySQL++.

К SSQLS-структуре, например, к объявленной выше структуре wares можно применять алгоритмы стандартной библиотеки STL и методы стандартных контейнеров:

std::set<wares> res;
query.storein(res);
cout << res.lower_bound(wares("Стол"))->name << endl;

Этот фрагмент кода выводит самое первое найденное наименование товара в наборе результатов, которое начинается с цепочки символов "Стол".

Третий параметр макрокоманды sql_create_N - числовое значение set_count. Если оно не равно нулю, то в структуру добавляется член-метод set() для установки значений того количества полей этой структуры, которое соответствует значению set_count. Так например, в рассматриваемой нами структуре метод set() будет выполнять установку значений всех трёх полей: name, num и price.

3. Извлечение, добавление и модификация данных

В этом разделе я приведу примеры исходного кода для выполнения извлечения, добавления и модификации данных в таблицах без комментариев непосредственно в коде. Примеры просты, и с помощью данной статьи и всех предыдущих читатель разберётся в них без затруднений.

3.1. Извлечение данных

Разумеется, для извлечения данных из таблицы мы будем использовать SSQLS-структуру.

#include <mysql++.h>
#include <ssqls.h>
#include <iostream>
#include <vector>

sql_create_3( wares, 1, 3,
  mysqlpp::sql_char, name,
  mysqlpp::sql_int, num,
  mysqlpp::sql_real, price )

using namespace std;

int main( int argc, char *argv[] )
{
  try
  {
    mysqlpp::Connection con( "test_db", "localhost", "tdb_user", "tdb_password" );
    mysqlpp::Query query = con.query( "SELECT name,price FROM wares" );
    vector<wares> res;
    query.storein( res );
    cout << "Прайс-лист:" << endl;
    vector<wares>::iterator itr;
    for( itr = res.begin(); itr != res.end(): itr++ )
      cout << it->name << '\t' << it->price << " руб." << endl;
  }
  catch( const mysqlpp::BadQuery &erq )
  {
    cerr << "Ошибка запроса: " << erq.what() << endl;
    return -1;
  }
  catch( const mysqlpp::Exception &ex )
  {
    cerr << "Ошибка: " << ex.what() << endl;
    return -1;
  }
  return 0;
}

Обратите внимание на использование контейнера vector из стандартной библиотеки для хранения результатов запроса.

3.2. Добавление данных в таблицу

#include <mysql++.h>
#include <ssqls.h>
#include <iostream>
#include <vector>

sql_create_3( wares, 1, 3,
  mysqlpp::sql_char, name,
  mysqlpp::sql_int, num,
  mysqlpp::sql_real, price )

using namespace std;

int main( int argc, char *argv[] )
{
  try
  {
    mysqlpp::Connection con( "test_db", "localhost", "tdb_user", "tdb_password" );
    wares row( "Стеллаж", 10, 1200.00 );
    mysqlpp::Query query = con.query();
    query.insert( row );
    query.execute();
    cout.setf( ios::left );
    cout << setw(26) << "Наименование товара" <<
            setw(11) << "Количество" <<
            setw(11) << "Цена" << endl << endl;
    std::vector<wares>::const_iterator it;
    for( it = res.begin(); it != res.end(); it++ )
    {
      cout.setf( ios::left );
      cout << setw(25) << it->name.c_str() <<
              setw(10) << it->num <<
              setw(10) << it->price << endl;
    }
  }
  catch( const mysqlpp::BadQuery &erq )
  {
    cerr << "Ошибка при выполнении SQL-запроса: " << erq.what() << endl;
    return -1;
  }
  catch( const mysqlpp::Exception &ex )
  {
    cerr << "Ошибка: " << ex.what() << endl;
    return -1;
  }
  return 0;
}

3.3. Изменение существующих данных

Операция редактирования любого поля данных выполняется почти так же просто, как и добавление строк данных.

#include <mysql++.h>
#include <ssqls.h>
#include <iostream>
#include <vector>

sql_create_3( wares, 1, 3,
  mysqlpp::sql_char, name,
  mysqlpp::sql_int, num,
  mysqlpp::sql_real, price )

using namespace std;

int main( int argc, char *argv[] )
{
  try
  {
    mysqlpp::Connection con( "test_db", "localhost", "tdb_user", "tdb_password" );
    mysqlpp::Query query = 
       con.query("SELECT * FROM wares WHERE name = \"Стол компьютерный\"");
    mysqlpp::StoreQueryResult res = query.store();
    wares row = res[0];
    wares original_row = row; // копия необходима для выполнения замены данных
    row.price = 2800.00;
    query.update( original_row, row );
    query.execute();
    // вывод данных для проверки (см.предыдущий пример)
  }
  catch( const mysqlpp::BadQuery &erq )
  {
    cerr << "Ошибка при выполнении SQL-запроса: " << erq.what() << endl;
    return -1;
  }
  catch( const mysqlpp::Exception &ex )
  {
    cerr << "Ошибка: " << ex.what() << endl;
    return -1;
  }
  return 0;
}

4. Заключение

Итак, мы убедились, что с помощью библиотеки MySQL++ можно без затруднений формировать шаблоны запросов с подставляемыми параметрами и многократно использовать эти шаблоны в исходном коде приложения, а специальные структуры SSQLS (Specialized SQL Structures) являются удобным инструментом во многих случаях, когда требуется извлечение, добавление и модификация данных в таблицах.

В данной статье рассматривались специализированные формы запросов. В заключительной, седьмой статье цикла будет продемонстрировано практическое применение библиотеки MySQL++ в многопоточных приложениях.


Ресурсы для скачивания


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=514245
ArticleTitle=Практическое использование MySQL++: Часть 6. Специализированные формы запросов
publish-date=08312010