Access и ODBC (часть 2)Источник: Mccinet
Уровни функциональных возможностей ODBC APIВ ODBC существует несколько уровней функциональных возможностей, которые обеспечивают различные уровни взаимодействия с прикладными программами. Например, функция SQLConnect, относящаяся к базовому уровню, загружает драйвер и устанавливает соединение с источником данных на основе лишь идентификатора соединения и строки подключения, в которой, как правило, указываются имя источника данных, имя пользователя и пароль. Первый уровень расширения функциональных возможностей ODBC включает в себя функцию SQLDriverConnect (она будет рассмотрена далее), которая позволяет запрашивать у пользователя более подробную информацию о подготавливаемом соединении, а также задавать степень обязательности этого запроса. Второй уровень ODBC API включает в себя функцию SQLBrowseConnect, поддерживающую пошаговый метод ввода данных, которые необходимы для установления соединения с источником данных. В реальности, соответствие ODBC драйвера некоторому уровню означает то, что он поддерживает все функции данного уровня. При этом в нём, как правило, реализованы ещё и некоторые из функций более высокого уровня. Существуют специальные функции, которые позволяют точно определить возможности драйвера. Уровни соответствия SQLУровень соответствия SQL - это показатель того, какие возможности языка SQL и типы данных поддерживает драйвер. Грамматика ODBC SQL может быть базовой (она соответствует стандартным требованиям), минимальной (более низкий уроверь по сравнению с базовым) и расширенной (обеспечивает некоторые общепринятые расширения SQL). Как и в случае уровней ODBC API, существуют функции, позволяющие определить, какие операторы и типы данных поддерживает драйвер. Более сложная задачаВ предыдущей части я описал лишь основы использования интерфейса доступа к данным ODBC. Создание полнофункциональных приложений, основанных непосредственно на этом API, по сравнению с разработкой на MS Access, представляет собой исключительно трудную задачу. Чтобы не быть голословным, я приведу здесь небольшой пример, который иллюстрирует, насколько много нужно написать кода, для того, чтобы всего лишь получить список пользовательских таблиц с выбранного сервера БД. Перво-наперво, откажемся от использования классов для «обёртывания» функций, отвечающих за выделение и возврат ресурсов. Это не прибавит нашей программе надёжности, но зато значительно сократит её объём и улучшит читаемость кода. А функций таких будет много - полный комплект для всех трёх уровней структуры API (окружение-соединение-оператор). Далее, дополним модуль ODBC_API новыми объявлениями констант и функций: 'Константы ODBC 'Константы для SQLDriverConnect Global Const SQL_DRIVER_NOPROMPT = 0 Global Const SQL_DRIVER_COMPLETE = 1 Global Const SQL_DRIVER_PROMPT = 2 Global Const SQL_DRIVER_COMPLETE_REQUIRED = 3 'Константы для FreeStmt Global Const SQL_CLOSE = 0 Global Const SQL_DROP = 1 Global Const SQL_UNBIND = 2 Global Const SQL_RESET_PARAMS = 3 'Типы данных Global Const SQL_C_CHAR = 1 'Декларации функций ODBC Declare Function SQLDriverConnect Lib "odbc32.dll" (ByVal HDBC As Long, _ ByVal hWnd As Long, _ ByVal ConStrIn As String, _ ByVal ConStrInMax As Integer, _ ByVal ConStrOut As String, _ ByVal ConStrOutMax As Integer, _ ByRef ConStrOutLen As Long, _ ByVal DriverCompleation As Integer) As Integer Declare Function SQLDisconnect Lib "odbc32.dll" (ByVal HDBC As Long) As Integer Declare Function SQLTables Lib "odbc32.dll" (ByVal HSTMT As Long, _ ByVal TableQualifier As String, _ ByVal TableQualifierLen As Integer, _ ByVal TableOwner As String, _ ByVal TableOwnerLen As Integer, _ ByVal TableName As String, _ ByVal TableNameLen As Integer, _ ByVal TableType As String, _ ByVal TableTypeLen As Integer) As Integer Declare Function SQLFetch Lib "odbc32.dll" (ByVal HSTMT As Long) As Integer Declare Function SQLGetData Lib "odbc32.dll" (ByVal HSTMT As Long, _ ByVal ColNumber As Long, _ ByVal DataType As Integer, _ ByVal DataValue As String, _ ByVal DataValueMax As Integer, _ ByRef DataValueLen As Long) As Integer Для непосредственной работы создадим форму, в которой разместим список драйверов ODBC, поля для ввода параметров подключения к серверу (имя сервера, имя базы данных на нем, логин и пароль пользователя), а также список таблиц, который и будем пытаться заполнить. Ещё предусмотрим пару кнопок для придания большей наглядности нашим опытам (хотя, можно обойтись и обработкой событий списков). Список драйверов заполним по аналогии с предыдущим примером, только не будем создавать и наполнять таблицу. Вместо этого, в качестве типа источника строк выберем список значений, а при загрузке формы запишем в переменную наименования всех найденных драйверов и присвоим её свойству «Источник строк» списка. Следующий важный шаг - установить соединение с сервером. Информация, которая должна понадобиться - это, кроме имени драйвера, имена сервера и БД, а также логин и пароль пользователя, от лица которого будет осуществляться подключение. Вся вместе она образует так называемую строку подключения (Connection String), где перечисляются соответствующие ключи и их значения: strConStr = "DRIVER=" & Me.lbxDrivers & _ ";SERVER=" & Me.txtServer & _ ";DATABASE=" & Me.txtDataBase & _ ";UID=" & Me.txtLogin & _ ";PWD=" & Me.txtPassword & ";" Нетрудно заметить, что в качестве значений мы указываем как раз содержимое элементов управления нашей тестовой формы. Теперь передадим эту строку вместе с уже имеющимся идентификатором окружения в предварительно заготовленную функцию, которая выполнит всю работу и, в случае успеха, вернёт ненулевой идентификатор соединения. Или нулевой - в случае ошибки. Вот она: Public Function OpenConnection(HENV As Long, ConnectionString As String) As Long Dim intStatus As Integer 'Для кодов возврата функций ODBC. Dim lngHDBC As Long 'Идентификатор соединения. Dim strConStrIn As String * MAX_DATA_BUFFER 'Строковый буфер для передачи данных. Dim strConStrOut As String * MAX_DATA_BUFFER 'Строковый буфер для получения данных. Dim lngConStrOutLen As Long 'Реальная длина данных в буфере. OpenConnection = 0 If Len(ConnectionString) < MAX_DATA_BUFFER Then 'Строка не должна быть слишком длинной. strConStrIn = ConnectionString & Chr(0) 'Добавим нулевой символ как знак завершения строки. intStatus = SQLAllocConnect(HENV, lngHDBC) 'Получим идентификатор соединения. If intStatus = SQL_SUCCESS Then 'Если соединение успешно создано, пытаемся открыть его. intStatus = SQLDriverConnect(lngHDBC, _ 0, _ strConStrIn, _ MAX_DATA_BUFFER, _ strConStrOut, _ MAX_DATA_BUFFER, _ lngConStrOutLen, _ SQL_DRIVER_NOPROMPT) If intStatus = SQL_SUCCESS Then 'Если соединение успешно открыто, возвращаем его идентификатор. OpenConnection = lngHDBC Else 'Иначе - освободим ресурсы, выделенные соединению, а функция вернёт 0. intStatus = SQLFreeConnect(lngHDBC) End If End If End If End Function Основа нашей функции - функция ODBC API SQLDriverConnect. Первым параметром в ней идёт уже знакомый нам идентификатор соединения, который надо предварительно получить с помощью функции SQLAllocConnect. Второй параметр - идентификатор окна, которое будет являться родительским для окна диалога ввода параметров соединения. Этот диалог обеспечивается используемым драйвером ODBC, если мы того захотим и укажем в последнем параметре вызова функции соответствующий режим. В нашем случае диалог не требуется и поэтому мы передаём нулевой идентификатор окна (в противном случае можно передать, например, hWndAccessApp) и SQL_DRIVER_NOPROMPT соответственно. Третьим и четвёртым парамертами идут строка подключения и размер текстового буфера, отведенного этой строке. Последующие три параметра функции предназначены для возврата строки подключения, сформированной самой функцией и, в нашем случае, не используются. После того, как соединение установлено, подготовим идентификатор оператора с помощью функции SQLAllocStmt и вызовем функцию SQLTables. Эта функция возвращает набор сведений о таблицах (TABLE), представлениях (VIEW) и других аналогичных объектах базы данных. Использовать её нужно также, как и все функции, работающие с результирующим множеством, каким является, например, результат выполнения запроса на выборку. В двух словах, порядок действий такой: сначала нужно определить количество столбцов в результирующем множестве (SQLNumResultCols), затем их имена (SQLColumns) и типы данных (SQLDescribeCol). Для нашего примера всё это не нужно, так как точно известно, что имена таблиц содержатся в третьем столбце, имеют символьный тип данных, а название столбца не имеет значения. Теперь можно выделить буферы для данных каждого из столбцов с помощью функции SQLBindCol. Мы этого делать не будем, а воспользуемся другим способом извлечения данных (SQLGetData). Далее необходимо, устанавливая указатель на нужную запись с помощью функции SQLFetch или SQLExtendedFetch (последняя поддерживается не всеми драйверами), считывать данные столбца в подготовленную для них переменную. Как видите, всё это очень сложно и громоздко. Поэтому, многими фирмами разработаны различные программные компоненты более высокого уровня, существенно облегчающие использование таких интерфейсов, как ODBC. В MS Access для этого можно использовать механизмы присоединённых таблиц и запросов к серверу. В программах на VBA удобно использовать объекты доступа к данным DAO. Все перечисленные механизмы имеют ряд особенностей, которые необходимо знать, учитывать и использовать при работе с данными через интерфейс ODBC. Однако, это уже тема для отдельного разговора. |