ODBC API

Автор: Антон Баула
Опубликовано: 15.10.2001

Для кого предназначена эта статья
Что такое ODBC и зачем оно надо?
Работа с СУБД через ODBC API
Шаг 1. Устанавливаем соединение с базой данных
Шаг 2. Выполняем запрос к СУБД
Шаг 3. Получаем данные из СУБД
Шаг 4. Завершение работы

Демонстрационная программа (Исходные тексты) - 5 KB

Для кого предназначена эта статья

Если вы не используете MFC для доступа к СУБД или вы хотите узнать, как работают классы CDatabase и CRecordset из этой библиотеки, то эта статья для вас.

Что такое ODBC и зачем оно надо?

Если объяснить просто, то для большинства разработчиков, ODBC это стандарт, поддерживая который можно создавать приложения способные работать со многими СУБД, при условии, что эти СУБД, в свою очередь, тоже поддерживают этот стандарт (поставляя драйвер, скрывающий от программиста подробности вызова native API). При этом, со стороны клиента совершенно не обязательно знать с какой СУБД он работает в данный момент: Oracle, MSSQL, Access. Если клиент использует ODBC, то он не замечает особой разницы.

Подробное описание ODBC API вы сможете найти в библиотеке MSDN в разделе Platform SDK Documentation -> Data Services -> Microsoft Data Access Components (MDAC) SDK -> Microsoft Open Database Connectivity (ODBC).

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

Мы разберём всю последовательность действий, которые необходимо выполнить любому приложению работающему с СУБД посредством ODBC.

Работа с СУБД через ODBC API

Сначала в общих чертах.

Теперь подробнее.

Шаг 1. Устанавливаем соединение с базой данных

Прежде чем начать работать, нам необходимо загрузить Driver Manager (Менеджер драйвера). Для этого вызовем функцию SQLAllocHandle, которая даст нам описатель (Handle) окружения. Необходимо сохранить его:

SQLHENV henv = NULL;
// Alloc env handle
if (::SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv) == SQL_ERROR)
{
    DumpError(_T("AllocHandle on ENV failed."));
    goto EXIT;
}

Затем нам необходимо зарегистрировать версию ODBC, с которой мы собираемся работать. Делается это с помощью функции SQLSetEnvAttr с флагом SQL_ATTR_APP_ODBC_VER. Эта функция, как видно из названия, изменяет атрибуты окружения Менеджера драйвера.

В нашем случае сообщаем, что работаем с 3-й версией ODBC (SQL_OV_ODBC3):

::SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, 
    (void*) SQL_OV_ODBC3, SQL_IS_INTEGER);

Далее переходим непосредственно к установлению соединения. Для этого мы должны инициализировать структуру, в которой Менеджер драйвера хранит всю информацию о соединении, и получить описатель этой структуры (Handle). Делается это с помощью всё той же функции SQLAllocHandle, но теперь в качестве флага мы указываем не SQL_HANDLE_ENV, а SQL_HANDLE_DBC:

SQLHDBC     hdbc = NULL;
if ( ::SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc) == SQL_ERROR)
{
        DumpError(_T("AllocHandle on DBC failed."));
        goto EXIT;
}

Теперь всё готово к установлению соединения с источником данных. Да, да, я не оговорился именно с источником данных (Data Source), хотя ODBC позволяет работать с СУБД напрямую, делать этого не рекомендуется, поэтому мы не будем рассматривать эту возможность в данной статье.

Итак, устанавливаем связь с помощью функции SQLConnect:

if ( ::SQLConnect(hdbc, (SQLTCHAR*)szDsn, SQL_NTS,
        (SQLTCHAR*)szUser, _tcslen(szUser), (SQLTCHAR*)szPass, 
         _tcslen(szPass) ) == SQL_ERROR )
{
        DumpError( _T("Connect to ODBC failed.") );
        goto EXIT;
}

Эта функция принимает 7 аргументов:

Ну, что ж теперь мы имеем соединение с СУБД и неплохо было бы воспользоваться этим обстоятельством для собственной выгоды :-))) - начинаем работать с базой данных.

Шаг 2. Выполняем запрос к СУБД

Перед тем как выполнять запрос к СУБД нам необходимо инициализировать структуру, в которой хранится вся информация о текущем запросе, делается это с помощью всё того же метода SQLAllocHandle, с флагом SQL_HANDLE_STMT.

// Get a statement handle 
if (::SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt) == SQL_ERROR)
{
        DumpError( _T("AllocHandle on STMT failed.") );
        goto EXIT;
}

Запросы к базе данных можно выполнять двумя способами.

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

Запрос сначала подготавливается с помощью SQLPrepare, затем если необходимо специальные символы в строке запроса, в ODBC это знак «?», можно связать с какими либо переменными, это делается с помощью SQLBindParameter, затем запрос отправляется в СУБД методом SQLExecute.

Поскольку с первым способом всё понятно и комментариев не требуется, то рассмотрим подробно лишь второй способ.

SQLREAL Price; 
SQLUINTEGER PartID; 
SQLINTEGER PartIDInd = 0, PriceInd = 0; 

// Подготавливаем запрос, обратите внимание на знаки «?» вместо значений. 
SQLPrepare(hstmt, "UPDATE Parts SET Price = ? WHERE PartID = ?", SQL_NTS); 

// Связываем 1-й и 2-й параметры с переменными.
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_FLOAT, 
    SQL_REAL, 7, 0, &Price, 0, &PriceInd); 

SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_ULONG,   
    SQL_INTEGER, 10, 0, &PartID, 0, &PartIDInd); 

// Запрос готов.
// Инициализируем связанные переменные значениями и выполняем запрос. 
while (GetPrice(&PartID, &Price)) 
{ 
    SQLExecute(hstmt); 
}

Как видите, здесь тоже нет ничего сложного.

Шаг 3. Получаем данные из СУБД

Чтобы получить данные из СУБД, нам необходимо просто подготовить переменные, в которые мы будем записывать получаемые значения, и связать эти переменные с соответствующими столбцами результата запроса. Делается это с помощью метода SQLBindCol.

SQLRETURN ret = ::SQLBindCol( hstmt, nCol, FldBindType, 
    ColData.DataPtr, BuffLen, &ColData.StrLen_or_Ind );

Коротко опишем эту функцию. Она принимает на вход 6 параметров:

Всё понятно если мы знаем, сколько столбцов в результате запроса и тип каждого из них, а если нет, если заранее мы не знаем ни количество столбцов, ни их тип? На этот случай ODBC API имеет один метод, чтобы узнать количество столбцов в результате запроса

ret = SQLNumResultCols( hstmt, &nCols );

И два метода, что бы получить информацию о каждом столбце SQLDescribeCol и SQLColAttribute обе эти функции функционально почти идентичны, но SQLColAttribute возвращает расширенное описание, основанное на стандарте ANSI SQL-92 и DBMS, а SQLDescribeCol информацию основанную на стандарте ANSI-89 SQL.

После того как мы связали наши переменные с каждым из столбцов, всё, что осталось нам сделать это вызывать метод SQLFetch, пока он не вернёт нам значение равное SQL_NO_DATA говорящее нам, что была достигнута последняя строка в результате запроса.

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

Ну что же мы получили данные, теперь нам обязательно необходимо закрыть курсор, который открывается автоматически при выполнении запроса к СУБД (SQLExecDirect или SQLExecute), делаем это методом SQLCloseCursor

// Close result cursor
::SQLCloseCursor( hstmt );

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

Шаг 4. Завершение работы

if (hstmt != NULL)
{
    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}

if (hdbc != NULL)
{
    SQLDisconnect(hdbc);
    SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
}

// Здесь можно перейти к шагу номер 2

if (henv != NULL)
{
    SQLFreeHandle(SQL_HANDLE_ENV, henv);
}

Вот так вкратце работают приложения на базе ODBC API. Возможности ODBC API намного шире, чем показано в данной статье, за дополнительной информацией обращайтесь в MSDN.


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