Выполнение подготовленных запросов: DatabaseRead/Bind

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

bool DatabaseRead(int request)

При первом вызове после DatabasePrepare или DatabaseReset функция DatabaseRead выполняет запрос и устанавливает внутренний указатель результатов запроса на первую полученную запись (если запрос предполагает возврат записей). Прочитать значения полей записи, то есть указанных в запросе столбцов, позволяют DatabaseColumn-функции.

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

Функция возвращает true при успешном выполнении. Значение false используется как индикатор ошибки (например, база может быть заблокирована или занята), а также при штатном достижении конца результатов, поэтому следует анализировать код в _LastError. В частности, значение ERR_DATABASE_NO_MORE_DATA (5126) указывает на то, что результаты закончились.

Внимание! В тех случаях, когда DatabaseRead используется для выполнения запросов, которые не возвращают данных, такие как INSERT, UPDATE и т.д., функция сразу возвращает false и устанавливает код ошибки ERR_DATABASE_NO_MORE_DATA, если запрос выполнился успешно.

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

int r = DatabasePrepare(db"SELECT... WHERE...?",
   param));                            // компилируем запрос (опционально с параметрами)
while(DatabaseRead(r))                 // выполнение запроса (на первой итерации)
{                                      //    и цикл по записям результата
   int count;
   DatabaseColumnInteger(r0count); // чтение одного поля из текущей записи
   double number;
   DatabaseColumnDouble(r1number); // чтение другого поля из текущей записи
   ...                                 // типы и кол-во колонок в записи определяет программа,
                                       // обрабатываем полученные значения count, number, etc.
}                                      // цикл прерывается по достижению конца результатов
DatabaseFinalize(r);

Обратите внимание: поскольку запрос (чтение данных по условиям) фактически выполняется только однажды (на самой первой итерации), не нужно вызывать DatabaseReset, как мы это делали при записи меняющихся данных. Однако, если мы захотим еще раз выполнить запрос и "пройтись" по новым результатам, вызов DatabaseReset был бы необходим.

bool DatabaseReadBind(int request, void &object)

Функция DatabaseReadBind работает по аналогичному принципу с DatabaseRead: первый вызов исполняет SQL-запрос и, в случае успеха (наличия подходящих данных в результате), заполняет переданную по ссылке структуру object полями первой записи; последующие вызовы продолжают перемещение внутреннего указателя по записям в результатах запроса, заполняя структуру данными очередной записи.

Структура должна иметь в качестве членов только числовые типы и/или строки (массивы не разрешены), не может быть наследником или содержать статические члены объектных типов.

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

Если количество полей в структуре меньше количества полей в записи, будет произведено частичное чтение. Оставшиеся данные можно получить с помощью соответствующих DatabaseColumn-функций.

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

В простейшем случае, когда мы рассчитываем некую общую величину по записям базы, например, вызвав агрегатную функцию вроде SUM(столбец), COUNT(столбец) или AVERAGE(столбец), результатом запроса будет единственная запись с единственным полем.

SELECT SUM(swap) FROM trades;

Поскольку чтение результатов связано DatabaseColumn-функциями, мы отложим разработку примера до следующего раздела, где они представлены.