Работа с СУБД Oracle через интерфейс OCCI - Повышение производительности

ОГЛАВЛЕНИЕ


Повышение производительности

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

void setDataBuffer(
int paramIndex,
void *buffer,
Type type,
sb4 size,
ub2 *length,
sb2 *ind = NULL,
ub2 *rc = NULL);
paramIndex
Номер параметра
buffer
указатель на буфер с данными
type
Тип данных в буфере
size
Размер элемента в буфере
length
Текущая длина данных в текущей ячейке буфера
ind
Индикатор. Указывает когда данные пустые, в случае если -1 было вставлено NULL - значение, в случае вызова подпрограммы в запросе -1 указывает что возвращено NULL.
rc
Код возврата. Этот параметр неприменим к данным передаваемым методами Statement-a, но для данных возвращаемых из вызовов процедур возвращает параметро-зависимый код ошибки.

При инициализации данных используя setDataBuffer данные считываются последовательно из памяти, т.е. после каждой итерации(количество заранее указывается) происходит смещение указателя на адрес следующего элемента. Метод setDataBuffer можно использовать совместно с Statement* stmt->setXXX или без него. Рассмотрим оба варианта использования этого метода

Допустим, в базе создана таблица следующей вида:

CREATE TABLE tb01
(
id INTEGER,
data NUMBER(10),
val VARCHAR(20)
)
NOLOGGING

Код выполняющий вставку в таблицу:

/*
Массивы данных для вставки
*/
int ids[] = {1,2,3,4};
ub2 ids_rc[] = {0,0,0,0};
unsigned long datas[] = {1000,2000,3000,4000};
/*
Строковые переменные, в документации по OCCI не указана 2-я размерность массива(10) - ее нужно указывать
*/
char vals[4][10] = {"Value", "Value666", "Value677", "Val4545"};
stmt->setSQL("Insert into tb01(id,data,val) values(:1,:2,:3)");
int i = 0;
/*
Определяем массив длин значений в массиве строковых данных и заполняем длины
*/
ub2 valsLen[4];
for (i = 0; i < 4; i++)
  {
  valsLen[i] = strlen(vals[i]) + 1;
  }
   /*
  Устанавливаем максимальное количество итераций
  */
  stmt->setMaxIterations(4);
  /*
  Заполнение буфера, вызывается 1 раз на все значения. Указывается номер параметра, адрес данных,типа данных,
  размер ячейки данных (чтобы OCCI знал на сколько передвинуть указатель для позиционирования на следующую ячейку),
  указатель на длины данных в ячейках. Предполагается что данные идут в памяти последоваетельно - за концом
  одной строки начало следуюшей
  */
  stmt->setDataBuffer(3,vals,OCCI_SQLT_STR, sizeof(vals[0]),valsLen);
  /*
  sizeof(vals[0]) == 10 для этого случая
  */
for (i = 0; i < 4; i++)
    {
    stmt->setInt(1,ids[i]);
     stmt->setInt(2,datas[i]);
  /*
  Добавление итерации + фактическое смещение указателя данных и накапливание данных из setInt
  */
     if (i != 3) stmt->addIteration();
    } 
/*
Выполнение всех добавленых итераций
*/
stmt->executeUpdate();

Важно понимать, что в данном случае объявление char vals[4][10] = {"Value", "Value666", "Value677", "Val4545"}; нельзя заменить на char* vals[4] - и заполнить адреса произвольно, данные должны идти последовательно. Теперь рассмотрим тот же вариант, но когда все данные содержатся в массивах:

/*
Массив id, а также массив длин элементов и массив кодов возврата(для примера).
Естественно sizeof(ids[0]) == sizeof(ids[1]) == sizeof(ids[2]) == sizeof(ids[3]) - объявлено таким образом для наглядности
*/
int ids[] = {1,2,3,4};
ub2 ids_len[] = {sizeof(ids[0]),sizeof(ids[1]),sizeof(ids[2]),sizeof(ids[3])};
ub2 ids_rc[] = {0,0,0,0};

unsigned long datas[] = {1000,2000,3000,4000};
ub2 datas_len[] = {sizeof(datas[0]),sizeof(datas[1]),sizeof(datas[2]),sizeof(datas[3])};

char vals[4][10] = {"Value", "Value666", "Value677", "Val4545"};
ub2 valsLen[4];
for (i = 0; i < 4; i++)
     {
     valsLen[i] = strlen(vals[i]) + 1;
     }

stmt->setSQL("Insert into tb01(id,data,val) values(:1,:2,:3)");
int i = 0;
/*
Установка переменных
*/
stmt->setDataBuffer(1,ids,OCCIINT,sizeof(ids[0]),ids_len,NULL,ids_rc);
stmt->setDataBuffer(2,datas,OCCIINT,sizeof(datas[0]),datas_len);
stmt->setDataBuffer(3,vals,OCCI_SQLT_STR, sizeof(vals[0]),valsLen);
/*
Выполнить 4 итерации
*/
stmt->executeArrayUpdate(4);

В использовании stmt->setMaxIterations(4); нет необходимости Подобным образом можно не только вставлять данные, но и извлекать их. Код из документации по OCCI иллюстрирующий это:

 int empno[5];
char ename[5][11];
ub2 enameLen[5];
ResultSet *resultSet = stmt->executeQuery("select empno, ename from emp");
resultSet->setDataBuffer(1, &empno, OCCIINT);
resultSet->setDataBuffer(2, ename, OCCI_SQLT_STR, sizeof(ename[0]), enameLen);
rs->next(5); // сливаем 5 строк, enameLen[i] хранит длину ename[i]

Заключение

В общем, это все что я хотел написать - надеюсь приведенные здесь примеры помогут вам быстро начать использование Oracle в ваших проектах на C++. OCCI очень простой и мощный интерфейс работы с Oracle. Здесь не затронуты вопросы использования BLOB, использование потоков Stream, объектное программирование и Object Type Translator (OTT), получение метаданных, разработка многопоточных приложений например используя ConnectionPool и т.д. По всем этим вопросам стоит обратиться к документации по Oracle - Oracle C++ Call Interface Programmer’s Guide ссылку на которую я приводил в начале статьи.