目錄
ODBC 概述
ODBC 數據類型 API 函數
ODBC 編程
(本章節中例子都是用 VS2005 編譯調試的,數據庫為 SQL Server 2005)
ODBC 概述
ODBC 概述
ODBC(Open Database Connectivity),開放數據庫互連.ODBC 是上個世紀八十年代末九十年代初出現的技術,它為編寫關系數據庫的客戶軟件提供了一種統一的接口.ODBC 提供一個單一的 API,可用於處理不同數據庫的客戶應用程序.使用 ODBC API 的應用程序可以與任何具有ODBC驅動程序的關系數據庫進行通信
ODBC 是為客戶應用程序訪問關系數據庫時提供的一個標准的接口,對於不同的數據庫,ODBC 提供了統一的 API,使應用程序鎖提供的 API 來訪問任何提供了 ODBC 驅動程序的數據庫.
-
- 應用程序(Application,即對應下圖的客戶程序): 應用程序本身不直接與數據庫打交道,主要負責處理並調用ODBC函數,發送對數據庫的SQL請求及取得結果.
- 驅動程序管理器(Driver Manager,即對應下圖的 ODBC 驅動管理程序): 驅動程序管理器是一個帶有輸入程序的動態鏈接庫(DLL),主要目的是加載驅動程序,處理ODBC調用的初始化調用,提供ODBC調用的參數有效性和序列有效性.
- 驅動程序(Driver,即對應下圖的 ODBC 驅動程序): 驅動程序是一個完成ODBC函數調用並與數據庫相互影響的DLL,這些驅動程序可以處理對於特定的數據的數據庫訪問請求.對於應用驅動程序管理器送來的命令,驅動程序再進行解釋形成自己的數據庫所能理解的命令.驅動程序將處理所有的數據庫訪問請求,對於應用程序來講不需要關注所使用的是本地數據庫還上網絡數據庫.
三者的交互過程如下圖所示:
優點
由於ODBC是一種底層訪問技術,因為ODBC API可以使客戶應用程序能夠從底層設置和控制數據庫,完成一些高層數據庫技術無法完成的功能
缺點
由於ODBC只能用於關系數據庫,使得利用ODBC很難訪問對象數據庫及其他非關系數據庫.另外,直接使用ODBC API編寫應用程序要編制大量的代碼,增加了程序編制的復雜程度
相關文件及概述:
- sql.h: 包含有基本的ODBC API的定義
- sqlext.h: 包含有擴展的ODBC的定義
- sqltypes.h: 包含有SQL的類型的定義
- odbc32.lib: 庫文件
ODBC API執行SQL語句方式
直接執行
- 特點: 直接執行是最快捷的執行方式,一次准備,一次提交並執行
准備執行
- 特點: 准備執行是逐步設定執行需要的參數,最后來執行,也就是經歷了一個執行准備和執行提交的過程
- 階段:
- 執行准備階段: 應用程序通過調用SQLPrepare函數來實現,每次准備的SQL語句先被預編譯一遍,以等待語句的執行,這樣多次執行的語句值須編譯一次,從而加快SQL語句的多次執行,另外准備執行也可以減少網絡負載,因為在每次執行語句時,驅動程序可以只想,數據源發送一個語句標識符,而不是整個SQL語句
- 執行提交階段: 應用程序使用SQLExecute函數來實現
- 步驟
- 調用SQLPrepare函數並向其傳遞一個含有SQL語句的字符串
- 設置參數值用SQLBindParameter來綁定參數
- 調用SQLExecute函數執行SQL語句
使用編碼函數
在應用程序中獲取數據源的內部系統信息,如數據庫中包含的所有表信息,表中包含所有列信息
ODBC 光標類型
- 向前光標: SQL_CURSOR_FORWARD_ONLY,光標僅僅向前滾動
- 靜態光標: QL_CURSOR_STATIC,結果集的數據是靜態的,這就是說明在執行查詢后,返回的結果集的數據不會再改變,即使是有其他程序更新了數據庫中的記錄,結果集中的記錄也不會發生改變
- 動態光標: SQL_CURSOR_DYNAMIC,在光標打開以后,當結果集中的行所對應的數據值發生變化時,其變化能夠能夠反映到光標所對應的結果集上,這些變化包括:字段的修改,添加,結果集中行的順序變化.但是請注意如果行別刪除則無法在當前結果集中反映出,因為被刪除的行不再出現在當前的結果集中.動態光標所對應的結果集在數據發生變化時會被重建.例如,假設動態光標已獲取到了兩行,然后,另一應用程序更新了這兩行中的一行,並刪除了另一行,如果動態游標再試圖獲取那些行,它將不能檢測已刪除的行(因為當前結果集中只有一行,但是不要利用這個辦法去檢測被刪除的行,因為出現這種情況還可能是因為行的數據被改變后不能再滿足查詢條件),而是返回已更新行的新值
- 鍵集光標: SQL_CURSOR_KEYSET_DRIVEN,和上面的動態光標所不同的是鍵集光標能夠檢測到行的刪除和修改,但是無法檢測到檢測到行的添加和結果集順序變化.因為在光標創建時就創建了整個結果集,結果集合中記錄和順序已經被固定,這一點和靜態光標一樣.所以鍵集光標可以說是一種介於靜態光標和動態光標之間的光標類型
ODBC 編程
操作步驟:
建立 ODBC DSN:
說明:
DSN(Data Source Name)是用於指定ODBC與相關的驅動程序相對應的一個入口,所有DSN的信息由系統進行管理,一般來講當應用程序要使用ODBC訪問數據庫時,就需要指定一個DSN以便於連接到一個指定的ODBC驅動程序.
DSN 共分為三類:
- 用戶 DSN: 對當前登錄用戶可見,只能夠用於當前計算機.
- 系統 DSN: 對當前系統上所有用戶可見,包括NT中的服務.
- 文件 DSN: DSN信息存放在文件中,對能夠訪問到該文件的用戶可見.
步驟:
菜單 -> 控制面板 -> 管理工具 -> 數據源 ODBC -> 添加數據源(操作過程如下面圖片所示,單擊可以看到大圖)
在這里我遇到過一個問題,當時我的系統是 Win7 ,然后裝 SQL Server 2005 時候是利用默認實例,結果我在添加數據源時候一直沒有找到服務器,然后我在晚上查原因時候,網上給我的解釋是默認實例時裝數據庫是不建立服務器的,所以我有重新裝了一遍 SQL Server 2005 然后采用的是命名實例,實例名為 SQL2005 然后便可以在數據源添加時候找到自己的服務器了.如果有遇到這類情況即找不到服務器時候可以使試試這個方法.但是在用 SQL Server Management Studio 登入數據庫時候服務器名稱就是 "本機名 + \ + 命名實例名" 例如: KANG-PC\SQL2005.
代碼樣例:
首先在 SQL Server 2005 中,先建立了一個數據庫,數據庫名為 MyTest ,數據庫中有一個表,表名為 T1,表項和數據類容如下:
然后為這個數據庫在數據源 ODBC 中添加了一個名為 SQL Server 的數據源驅動程序.接着開始數據庫編程:
直接執行:
查詢數據(這里注意下:用 SQLAllocHandle 分配的句柄用完后需要用 SQLFreeHandle 去釋放這個句柄)

#include"windows.h" #include"sql.h" #include"sqlext.h" #include"sqltypes.h" //以上四個頭文件是必須要的 #include<cstdlib> #include<iostream> using namespace std; void main() { SQLHENV serverhenv; SQLHDBC serverhdbc; SQLHSTMT serverhstmt; SQLRETURN ret; SQLCHAR name[20]={0}; SQLINTEGER id = 0,age=0,num=0,length; //分配環境句柄 ret = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&serverhenv); //設置環境屬性 ret = SQLSetEnvAttr(serverhenv,SQL_ATTR_ODBC_VERSION,(void*)SQL_OV_ODBC3,0); if(!SQL_SUCCEEDED(ret)) { cout<<"AllocEnvHandle error!"<<endl; system("pause"); return; } //分配連接句柄 ret = SQLAllocHandle(SQL_HANDLE_DBC,serverhenv,&serverhdbc); if(!SQL_SUCCEEDED(ret)) { cout<<"AllocDbcHandle error!"<<endl; system("pause"); return; } //數據庫連接 ret = SQLConnect(serverhdbc,(SQLWCHAR*)L"SQL Server",SQL_NTS,NULL,SQL_NTS,NULL,SQL_NTS); if(!SQL_SUCCEEDED(ret)) { cout<<"SQL_Connect error!"<<endl; system("pause"); return; } //分配執行語句句柄 ret = SQLAllocHandle(SQL_HANDLE_STMT,serverhdbc,&serverhstmt); //查詢數據 ret=SQLExecDirect(serverhstmt,(SQLWCHAR*)L"select * from T1",SQL_NTS); if(ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { //綁定數據 SQLBindCol(serverhstmt,1, SQL_C_CHAR, (void*)name,sizeof(name), &length); SQLBindCol(serverhstmt,2, SQL_C_ULONG, (void*)&id,sizeof(id), &length); SQLBindCol(serverhstmt,3, SQL_C_ULONG, (void*)&age,sizeof(age), &length); //將光標移動到下行,即獲得下行數據 while(SQL_NO_DATA != SQLFetch(serverhstmt)) { cout<<"name:"<<name<<" ID:"<<id<<" Age:"<<age<<" "; cout<<endl; } } //釋放語句句柄 ret=SQLFreeHandle(SQL_HANDLE_STMT,serverhstmt); if(SQL_SUCCESS!=ret && SQL_SUCCESS_WITH_INFO != ret) cout<<"free hstmt error!"<<endl; //斷開數據庫連接 ret=SQLDisconnect(serverhdbc); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"disconnected error!"<<endl; //釋放連接句柄 ret=SQLFreeHandle(SQL_HANDLE_DBC,serverhdbc); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"free hdbc error!"<<endl; //釋放環境句柄句柄 ret=SQLFreeHandle(SQL_HANDLE_ENV,serverhenv); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"free henv error!"<<endl; system("pause"); }
運行結果:
准備執行:
插入數據:

#include"windows.h" #include"sql.h" #include"sqlext.h" #include"sqltypes.h" //以上四個頭文件是必須要的 #include<cstdlib> #include<iostream> using namespace std; void main() { SQLHENV serverhenv; SQLHDBC serverhdbc; SQLHSTMT serverhstmt; SQLRETURN ret; SQLCHAR name[20]={0}; SQLINTEGER id = 0,age=0,num=0,length=20; SQLINTEGER pointer=sizeof(name); //分配環境句柄 ret = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&serverhenv); //設置環境屬性 ret = SQLSetEnvAttr(serverhenv,SQL_ATTR_ODBC_VERSION,(void*)SQL_OV_ODBC3,0); if(!SQL_SUCCEEDED(ret)) { cout<<"AllocEnvHandle error!"<<endl; system("pause"); return; } //分配連接句柄 ret = SQLAllocHandle(SQL_HANDLE_DBC,serverhenv,&serverhdbc); if(!SQL_SUCCEEDED(ret)) { cout<<"AllocDbcHandle error!"<<endl; system("pause"); return; } //數據庫連接 ret = SQLConnect(serverhdbc,(SQLWCHAR*)L"SQL Server",SQL_NTS,NULL,SQL_NTS,NULL,SQL_NTS); if(!SQL_SUCCEEDED(ret)) { cout<<"SQL_Connect error!"<<endl; system("pause"); return; } //分配執行語句句柄 ret = SQLAllocHandle(SQL_HANDLE_STMT,serverhdbc,&serverhstmt); //准備語句 ret=SQLPrepare(serverhstmt,(SQLWCHAR*)L"insert T1 values(?,?,?)",SQL_NTS); //參數綁定 ret=SQLBindParameter(serverhstmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,pointer,0,(void *)name,pointer, &pointer); if( ret != SQL_SUCCESS) cout<<"SQLBindParameter name error!"<<endl; ret=SQLBindParameter(serverhstmt,2,SQL_PARAM_INPUT,SQL_C_LONG,SQL_INTEGER,0,0,(void*)&id,0,0); if( ret != SQL_SUCCESS) cout<<"SQLBindParameter id error!"<<endl; ret=SQLBindParameter(serverhstmt,3,SQL_PARAM_INPUT,SQL_C_LONG,SQL_INTEGER,0,0,(void*)&age,0,0); if( ret != SQL_SUCCESS) cout<<"SQLBindParameter age error!"<<endl; //插入數據 strcpy((char*)name,(char *)"Mile"); id = 1223; age = 45; ret=SQLExecute(serverhstmt); if( ret != SQL_SUCCESS) cout<<"SQLExecute error!"<<endl; //插入數據 strcpy((char*)name,(char *)"Kee"); id = 1224; age = 16; ret=SQLExecute(serverhstmt); if( ret != SQL_SUCCESS) cout<<"SQLExecute error!"<<endl; //釋放語句句柄 ret=SQLFreeHandle(SQL_HANDLE_STMT,serverhstmt); if(SQL_SUCCESS!=ret && SQL_SUCCESS_WITH_INFO != ret) cout<<"free hstmt error!"<<endl; //斷開數據庫連接 ret=SQLDisconnect(serverhdbc); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"disconnected error!"<<endl; //釋放連接句柄 ret=SQLFreeHandle(SQL_HANDLE_DBC,serverhdbc); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"free hdbc error!"<<endl; //釋放環境句柄句柄 ret=SQLFreeHandle(SQL_HANDLE_ENV,serverhenv); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"free henv error!"<<endl; system("pause"); }
運行結果(數據庫內容如下):
獲得錯誤信息:
實現代碼:

#include"windows.h" #include"sql.h" #include"sqlext.h" #include"sqltypes.h" //以上四個頭文件是必須要的 #include<cstdlib> #include<iostream> using namespace std; void main() { SQLHENV serverhenv; SQLHDBC serverhdbc; SQLHSTMT serverhstmt; SQLRETURN ret; SQLWCHAR errorString[100]; SQLSMALLINT MsgLen; SQLWCHAR SqlState[6]; SQLINTEGER NativeError; //分配環境句柄 ret = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&serverhenv); //設置環境屬性 ret = SQLSetEnvAttr(serverhenv,SQL_ATTR_ODBC_VERSION,(void*)SQL_OV_ODBC3,0); if(!SQL_SUCCEEDED(ret)) { cout<<"AllocEnvHandle error!"<<endl; system("pause"); return; } //分配連接句柄 ret = SQLAllocHandle(SQL_HANDLE_DBC,serverhenv,&serverhdbc); if(!SQL_SUCCEEDED(ret)) { cout<<"AllocDbcHandle error!"<<endl; system("pause"); return; } //數據庫連接 ret = SQLConnect(serverhdbc,(SQLWCHAR*)L"SQL Server",SQL_NTS,NULL,SQL_NTS,NULL,SQL_NTS); if(!SQL_SUCCEEDED(ret)) { cout<<"SQL_Connect error!"<<endl; system("pause"); return; } //分配執行語句句柄 ret = SQLAllocHandle(SQL_HANDLE_STMT,serverhdbc,&serverhstmt); //執行語句,數據庫中已經存在這條數據,所以插入肯定失敗. ret=SQLExecDirect(serverhstmt,(SQLWCHAR*)L"insert T1 values('Jame',1221,21)",SQL_NTS); if(SQL_SUCCESS!=ret && SQL_SUCCESS_WITH_INFO != ret) cout<<"SQLExecDirect error!"<<endl; //獲取錯誤信息 int i=1; while(true) { if((ret = SQLGetDiagRecW(SQL_HANDLE_STMT,serverhstmt,i,SqlState,&NativeError,errorString,100,&MsgLen)) != SQL_NO_DATA) //wcout<<errorString<<endl; //這里有些字符輸不出來,所以我改為了消息框彈出顯示錯誤信息 MessageBox(NULL,errorString,L"",MB_OK); else break; i++; } //釋放語句句柄 ret=SQLFreeHandle(SQL_HANDLE_STMT,serverhstmt); if(SQL_SUCCESS!=ret && SQL_SUCCESS_WITH_INFO != ret) cout<<"free hstmt error!"<<endl; //斷開數據庫連接 ret=SQLDisconnect(serverhdbc); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"disconnected error!"<<endl; //釋放連接句柄 ret=SQLFreeHandle(SQL_HANDLE_DBC,serverhdbc); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"free hdbc error!"<<endl; //釋放環境句柄句柄 ret=SQLFreeHandle(SQL_HANDLE_ENV,serverhenv); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"free henv error!"<<endl; system("pause"); }
運行結果:
相關資料: