|
撰/ahliu
1:甚麼是ODBC?
ODBC的全寫為Open DataBase Connectivity,是當年業界根據X/Open規格開發的SQL程式元件,當中包括完整的API及SQL支援來連接、查詢及刪除資料庫內的數據,程式人員可將SQL送到ODBC的介面,再由相應的ODBC drivers處理操作。
使用了ODBC的程式不需直接連接數據庫(Excel、Access、dBase……),而是經由該數據庫的對應Driver執行所有操作,即是上至Oracle數據庫伺服器,下至Excel的檔案式數據庫,都能用同一套API去處理數據。
(由於本文之目的在於探討Windows SQL Server程式編寫,故往後並不會談及非Windows之數據庫編程)
2:為何要使用Microsoft ODBC?
大家也許聽過微軟還有很多方法去連接數據庫,ADO(ActiveX Data Objects)及OLE DB(Object Linking and Embedding DataBase),連同ODBC是屬於MDAC(Microsoft Data Access Components),甚至是利用DOM來處理XML文件。當然,每種方法都有不同目的及價值,人們選擇ODBC是用於開發支援多種數據庫系統的程式,只需更改驅動程式的名稱,不用更改API及SQL,就可連接到新的系統。
3:API+ODBC
其實這樣說起上來有點誤導,我們實際上可用Console程式連接ODBC,並不需要「WinMain」、「WindowProc」來配合視窗的訊息迴圈,只需要加入以下header files:
<sql.h>
<sqlext.h>
就能使用ODBC API。如果你沒有這兩個header files,即是你可能沒有安裝Platform SDK了,請到http://www.microsoft.com/msdownload/platformsdk/sdkupdate/去下載Platform SDK。
以下來的介紹,都使用了Windows API來處理程式的圖像,讀者參考程式碼時,可不必深究與ODBC無關的API碼,更鼓勵讀者以Console程式自行學習ODBC。
4:ODBC的進入點:SQL Handle
視窗程式有時候可不是和PHP、ASP等網頁程式一樣,一開始用「SQLConnect」就能連接數據庫系統,所有準備功夫都要由你去完成。
和絕大部份的視窗程式一樣,一切介面都有一個Handle,ODBC當然也少不免,而且一來便有三個Handles,包括Environment Handle(用來載入數據庫驅動程式管理程序,不同的Windows版本有不同的影響)、Connection handle(真正連接數據庫的介面)及Statement handle(查詢數據庫時的I/O介面),用以下方法宣告及建立Environment handle:
SQLHENV henv;
SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&henv);
當中,SQLAllocHandle的函數被宣告為:
SQLRETURN SQLAllocHandle(
SQLSMALLINT HandleType,
SQLHANDLE InputHandle,
SQLHANDLE *OutputHandlePtr);
根據Platform SDK ODBC Programmer Reference所述,第一個參數就是你想使用的handle,如果是Env. handle,就使用「SQL_HANDLE_ENV」;如果是Conn. handle,就使用「SQL_HANDLE_DBC」;如果是Stmt. handle,就使用「SQL_HANDLE_STMT」。第二個參數是parent handle,如果要使用的handle是Env. handle時,該parent handle就是「SQL_NULL_HANDLE」;如果是Conn. handle時,就使用剛才的Env. handle變數;如果是Stmt. handle時,就使用剛才的Conn. handle變數。簡單使用如下:
(1) 宣告變數
SQLHENV SQLHenv;
SQLHDBC SQLHdbc;
SQLHSTMT SQLHstmt;
(2) 分配記憶體予Env. handle
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &SQLHenv);
(3) 分配記憶體予Conn. handle
SQLAllocHandle(SQL_HANDLE_DBC, SQLHenv, &SQLHdbc);
(4) 分配記憶體予Stmt. handle
SQLAllocHandle(SQL_HANDLE_DBC, SQLHdbc, &SQLHstmt);
*注:Env. handle和Conn. handle是連接數據庫時必需執行的指令,而Stmt. handle則在執行數據庫查詢時才會使用。
若果你已急不及待地在你的程式中加入以上的程式碼,請停下來再看看以下的介紹。
5:載入數據庫驅動程式
這也是為何ODBC可以支援各式各類數據庫的關鍵所在,因為程式人員需要自行載入適當的驅動程式(Database driver),ODBC才會根據該數據庫驅動程式的定義,作查詢及寫入指令。在Windows環境下,常用的驅動程式有Microsoft Access Driver、Microsoft Excel Driver、SQL Server、Microsoft FoxPro Driver等(在Unix環境下有Oracle的Thin driver和OCI driver,在此不作討論),而載入數據庫驅動程式要做兩個步驟:設定ODBC版本及驅動程式名稱。
要設定ODBC版本,需要使用到剛才的Env. handle,及執行SQLSetEnvAttr函數,SQLSetEnvAttr宣告原型如下:
SQLRETURN SQLSetEnvAttr(SQLHENV SQLHenv, SQLINTEGER Attr, SQLPOINTER ValuePtr, SQLINTEGER strlength)
第一個參數就是Env. handle變數,第二個參數是SQL_ATTR_ODBC_VERSION,第三個數是ODBC版本的代號,第四個參數我們可設為零。例如我要使用ODBC 3.0:
SQLSetEnvAttr(SQLHenv, SQL_ATTR_ODBC_VERSION, (SQLINTEGER*) SQL_OV_ODBC3, 0);
*注:SQLSetEnvAttr必需在分配記憶體予Conn. handle前使用,更改Environmental Attribute只有在沒有任何Conn. handle底下才會生效
除了設定ODBC版本外,你還可以插入不同的屬性常數於第二參數,來設定ODBC的執行環境,但大都是不常用的(若讀者有意編寫支援網絡的小型或中型數據庫伺服器,可參考SQL_ATTR_CONNECTION_POOLING的用法)。
設定ODBC版本後,就可執行SQLDriverConnect,該函數原型如下:
SQLRETURN SQLDriverConnect(SQLHDBC Conn_handle, SQLHWND Win_handle, SQLCHAR *InConnectionString,
SQLSMALLINT StringLength1, SQLCHAR *OutConnectionString, SQLSMALLINT BufLen,
SQLSMALLINT *StrLen2Ptr, SQLUSMALLINT DriverCompletion)
相當複雜但卻合乎情理,要透過數據庫驅動程式來連接數據庫,先要有Conn. handle(第一參數),如果在child window中執行SQLDriverConnect當然亦都要有parent window的handle(如果你就是parent window就可將第二參數設為NULL),然後就要輸入將驅動程式的名稱及數據庫的名稱(第三參數,整條字串的長度則為第四參數),然後ODBC就會傳回完整的Connection String至第五參數(i.e. Connection string是有特定格式的字串,包括驅動程式及數據庫名稱資料),由於ODBC所傳回的connection string有可能長過OutConnectionString的長度,因此你需於第六參數輸入OutConnectionString的長度,若真的出現過長的情況,ODBC會把剩餘的字串指標傳到第七參數。例子如下:
/* Variable declaration */
SQLTCHAR InConnectionString[128];
SQLTCHAR OutConnectionString[1024];
SQLSMALLINT StringLength2Ptr;
/* Initialize the connection string */
memset(InConnectionString,0,sizeof(InConnectionString));
/* We are going to connect to an Access database */
_mbscpy(InConnectionString,(const unsigned char *)"DRIVER={Microsoft Access Driver (*.mdb)};DBQ=");
_mbscat(InConnectionString,(unsigned char *)DataFile); /* The database file name is stored in DataFile */
SQLDriverConnect(*SQLHdbc,NULL,InConnectionString,sizeof(InConnectionString),
OutConnectionString,sizeof(OutConnectionString),
&StringLength2Ptr,SQL_DRIVER_NOPROMPT))
*注:根據協定,OutConnectionString的長度必需為1024 bytes或以上
SQLDriverConnect的第八個參數設定是否顯示Driver的連接資料,若設為SQL_DRIVER_PROMPT,每次連接數據庫時都會有以下Dialog box顯示:

綜合以上各項與ODBC有關的指令,以下是一簡單的數據庫連接程式:
...
#include <sql.h>
#include <sqlext.h>
#include <mbstring.h> // For 16-bit string manipulation (SQLCHAR is defined as 16-bit char)
...
/* Variable declarations */
SQLHENV SQLHenv;
SQLHDBC SQLHdbc;
SQLHSTMT SQLHstmt;
SQLTCHAR InConnectionString[128];
SQLTCHAR OutConnectionString[1024];
SQLSMALLINT StringLength2Ptr;
// Environment handle memory allocation
switch (SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &SQLHenv)) {
case SQL_INVALID_HANDLE:
case SQL_ERROR:
MessageBox(NULL, "Environment handle memory allocation error.", "Error", MB_OK);
default:
break;
};
// Configure environment handle attribute (set to ODBC 3.0)
switch (SQLSetEnvAttr(SQLHenv, SQL_ATTR_ODBC_VERSION, (SQLINTEGER*) SQL_OV_ODBC3, 0)) {
case SQL_INVALID_HANDLE:
case SQL_ERROR:
MessageBox(NULL, "Environmental attributes setting error.", "Error", MB_OK);
return DATABASE_ERROR;
default:
break;
};
// Connection handle memory allocation
switch (SQLAllocHandle(SQL_HANDLE_DBC, SQLHenv, &SQLHdbc)) {
case SQL_INVALID_HANDLE:
case SQL_ERROR:
MessageBox(NULL, "Connection handle memory allocation error.", "Error", MB_OK);
return DATABASE_ERROR;
default:
break;
};
// Connect to data file (in this example it is an Access database file)
memset(InConnectionString,0,sizeof(InConnectionString));
_mbscpy(InConnectionString,(const unsigned char *)"DRIVER={Microsoft Access Driver (*.mdb)};DBQ=");
_mbscat(InConnectionString,(unsigned char *) "your_database.mdb");
switch (SQLDriverConnect(SQLHdbc, NULL, InConnectionString, sizeof(InConnectionString),
OutConnectionString, sizeof(OutConnectionString),
&StringLength2Ptr, SQL_DRIVER_PROMPT)) {
case SQL_INVALID_HANDLE:
case SQL_ERROR:
MessageBox(NULL, "Error connecting to data file.", "Error", MB_OK);
return DATABASE_ERROR;
default:
break;
};
...
相關連結:
- Platform SDK
相關文件:
- figure5_1
發表日期:2004-06-21
|