ODBC API 實現數據庫操作的手段是句柄。在ODBC中,使用不同的句柄(HANDLE)來標志環境(environment)、連接(Connection)、語句(statement)、描述符(description)等。句柄是一個應用程序變量,系統用它來存儲關於應用程序的上下文信息和應用程序所用到的一些對象。
1.1.1 環境句柄
環境是存取數據的全局性背景,與環境相關的是全局的所有信息。例如:環境狀態、當前環境狀態診斷、當前在環境上分配的連接句柄、每個環境屬性的當前設置。
在實現ODBC(Driver Manager或驅動程序)的代碼中,環境句柄標識包含這個信息的結構。環境句柄在ODBC應用程序中經常用來調用函數SQLAllocHandle、SQLEndTran、SQLFreeHandle、SQLGetDiagField和SQLGetDiagReg。
環境句柄是ODBC中整個上下文的句柄,使用ODBC的每個程序從創建環境句柄開始,以釋放環境句柄結束。所有其他的句柄都由環境句柄的上下文來管理,環境句柄在每個應用程序中只能創建一個。
1.1.2 連接句柄
一個連接包含一個驅動程序和一個數據源。連接句柄標識每個連接。連接句柄定義使用哪個驅動程序和該驅動程序使用的數據源。在執行ODBC(DriverManager或者驅動程序)的代碼中,連接句柄標志一個包含連接信息的結構。比如:連接狀態、當前連接層診斷、語句句柄和當前連接上分配的描述符、每個連接屬性的當前設置。
如果驅動程序支持多個同時連接,ODBC並不阻止多個同時的連接。因此,在特定的ODBC環境中,多個連接句柄可能指向不同的驅動程序和數據源、相同的驅動程序和不同的數據源甚至是與相同的驅動程序和數據源的多個連接。
與數據源進行連接(SQLConnect、SQLDriverConnect或SQLBrowseConnect)、從數據源上斷開(SQLDisconnect)、獲取驅動程序及數據源信息(SQLGetInfo)、檢索診斷(SQLGetDiagField和SQLGetDiagRec)和執行事務(SQLEndTran)時,都需要使用連接句柄。當設置和獲取連接屬性(SQLSetConnectAttr)時,也使用它們。
在應用程序中,可在任何適當的時候連接或脫離數據源,但不要輕易的建立或脫離連接。
1.1.3 語句句柄
每個語句由語句句柄標識。一個語句與單個的連接相關,並且在那個連接上可能有多個語句。在實現ODBC(DriverManager或驅動程序)的代碼中,語句句柄標識一個包含語句信息的結構,如:語句狀態、當前語句層診斷、應用程序變量綁定到語句參數和結果集列的地址、每個語句屬性的當前設置。
語句句柄在大多數ODBC函數中使用,它們用於函數綁定參數及結果集列(SQLBindParameter和SQLBindCol)、准備執行語句(SQLPrepare、SQLExecute和SQLExecDirect)、檢索元數據(SQLColAttribute和SQLDescribeCol)、取結果(SQLFetch)和檢索診斷(SQLGetDiagField和SQLGetDiagRec)。語句句柄使用SQLAllocHandle分配,使用SQLFreeHandle釋放。
1.2ODBC數據類型
在使用ODBC開發時一個重要的問題就是數據轉換的問題,在ODBC中存在下面的幾類數據:
(1)數據庫中SQL語言表達數據的類型
(2)ODBC中表達數據的類型
(3)C語言中表達數據的類型
在程序運行過程中數據需要經歷兩次轉換:C語言的數據或結構類型與ODBC的數據類型的轉換,ODBC與SQL間數據類型的轉換。所以ODBC所定義的數據類型起到了中間橋梁的作用,在ODBC的驅動程序調用自己的DBMS數據庫訪問接口時就需要對數據類型進行轉換。我們所需要關注的是C語言的數據類型和ODBC數據類型間的轉換關系。
1.2.1 SQL數據類型
SQL數據類型是在數據源中保存的數據類型。每個數據源都定義了自己的SQL數據類型,此處為SWIFTSQL數據類型。在基本數據源中,每一種數據類型如何映射為ODBC的SQL類型標識符是由驅動程序制定的。下表為對SWIFT SQL數據類型的描述及其與ODBC SQL類型的映射關系。
表1-1SWIFT SQL數據類型
序號
SWIFT
SQL類型
ODBC SQL類型標識
描述
有效范圍
1
smallint
SQL_SMALLINT
整型數據
[-32768, 32767]
2
int
SQL_INTEGER
整型數據
[-2147483648, 2147483647]
3
bigint
SQL_BIGINT
整型數據
[-9223372036854775808, 9223372036854775807]
4
double
precision
SQL_DOUBLE
浮點雙精度數
[2.2250738585072014e-308,1.7976931348623158e+308]
5
real
SQL_REAL
浮點精度數字
[-1E+308, 1E+308]
6
text
SQL_LONGVARCHAR
可變長度的字符數據
最大長度為1G個字符
7
char(n)
SQL_CHAR
定長字符串
長度為n
8
varchar(n)
SQL_VARCHAR
變長字符串
最大長度為n
9
bytea
SQL_VARBINARY
變長的二進制字串
理論上沒有限制,可以達到4個G
10
boolean
SQL_BIT
布爾類型
true/false
11
timestamp
SQL_TYPE_TIMESTAMP
時間戳
‘2013-01-01 00:00:00’
1.2.2 C數據類型
使用C語言開發,必定與ODBC語言之間存在數據轉換的問題,因為ODBC所存在的一些數據類型在C語言中是不存在的。ODBC定義了應用程序變量及其相應的數據標識符所使用的C數據類型。它們也用於綁定至結果集列和語句參數的緩沖區。ODBC定義了從每個數據類型到一個C數據類型的默認映射,如下表所示:
表1-2 ODBC 數據類型和C數據類型的映射
C語言數據類型名稱
ODBC 數據類型定義
C語言實際類型
SQL_C_SSHORT
SQLSMALLINT
short int
SQL_C_SLONG
SQLINTEGER
long int
SQL_C_FLOAT
SQLREAL
Float
SQL_C_DOUBLE
SQLDOUBLE, SQLFLOAT
Double
SQL_C_BIT
SQLCHAR
unsigned char
SQL_C_CHAR
SQLCHAR *
unsigned char *
SQL_C_SBIGINT
SQLBIGINT
_int64
SQL_C_BINARY
SQLCHAR *
unsigned char *
SQL_C_TYPE_TIMESTAMP
SQL_TIMESTAMP_STRUCT
struct tagTIMESTAMP_STRUCT {
SQLSMALLINT year;
SQLUSMALLINT month;
SQLUSMALLINT day;
SQLUSMALLINT hour;
SQLUSMALLINT minute;
SQLUSMALLINT second;
SQLUINTEGER fraction;
} TIMESTAMP_STRUCT;
所以在ODBC的開發過程中不要使用int,float 之類的C語言的實際類型來定義變量而應該使用ODBC定義的數據類型來定義變量,如:SQLINTEGER,SQLFLOAT。
表1-3 ODBC SQL 與 ODBC C 數據類型的轉換
ODBC SQL數據類型
ODBC C 數據類型
SQL_SMALLINT
SQL_C_SSHORT
SQL_INTEGER
SQL_C_SLONG
SQL_BIGINT
SQL_C_SBIGINT
SQL_DOUBLE
SQL_C_DOUBLE
SQL_REAL
SQL_C_FLOAT
SQL_CHAR
SQL_C_CHAR
SQL_VARCHAR
SQL_C_CHAR
SQL_LONGVARCHAR
SQL_C_CHAR
SQL_BIT
SQL_C_BIT
SQL_VARBINARY
SQL_C_BINARY
SQL_TYPE_TIMESTAMP
SQL_C_TYPE_TIMESTAMP
1.3 ODBC診斷
為了在程序開發過程中調試程序,發現程序錯誤,ODBC API通過兩種方式返回有關ODBC API函數執行的信息:返回碼和診斷記錄。返回碼返回函數執行的返回值,說明函數執行成功與否;診斷記錄說明函數執行的詳細信息。
1.3.1 返回碼
每一個ODBC API函數都返回一個返回碼,指示函數執行的成功與否。如果函數調用成功,返回碼為SQL_SUCCESS或SQL_SUCCESS_WITH_INFO。如果函數調用失敗,返回碼為SQL_ERROR。獲得結果集數據沒有更多的數據時,返回碼為SQL_NO_DATA。如果程序執行錯誤,返回碼為SQL_INVALID_HANDLE,程序無法執行,而其他的返回碼都帶有程序執行信息。幾個常見的返回碼及其說明,如下表所示:
返回碼
說明
SQL_SUCCESS
無錯誤。
SQL_SUCCESS_WITH_INFO
該函數完成,但是對 SQLError 的調用將顯示警告。這種狀態最常見的情況是:返回的值太長,應用程序提供的緩沖區不夠用。
SQL_ERROR
函數未完成,因為出現了錯誤。調用 SQLError 可獲取有關此問題的詳細信息。
SQL_INVALID_HANDLE
作為參數傳遞的環境、連接或語句句柄無效。
如果在釋放句柄后再使用該句柄,或者句柄為空值指針,則通常會發生這種情況。
SQL_NO_DATA
沒有可用信息。
使用這種狀態的最常見情況是在從游標進行讀取時,這種狀態表示游標中沒有更多行。
下面一段代碼根據函數SQLFetch執行的返回碼,判斷函數執行的成功與否,從而據此進行相應的處理:
SQLRETURNretcode;
SQLHSTMThstmt;
while(retcode= SQLFetch(hstmt) != SQL_NO_DATA)
{
if(retcode == SQL_SUCCESS_WITH_INFO)
{
//顯示警告信息
}
elseif (retcode == SQL_ERROR)
{
//顯示出錯信息
break;
}
//函數調用成功
}
1.3.2 診斷記錄
每個ODBC API函數都能產生一系列的反應操作信息的診斷記錄,這些診斷記錄都放在相關聯的ODBC句柄中,直到下一個使用同一個句柄的函數調用,該診斷記錄一直存在。診斷記錄的大小沒有限制。
診斷記錄有兩種:頭記錄(HeadRecord)和狀態記錄(Status Record)。頭記錄是第一版權記錄(Record0),后面的記錄為狀態記錄。診斷記錄有許多的域組成,這些域在頭記錄和狀態記錄中是不同的。
可以用SQLGetDiagField函數獲取診斷記錄中的特定的域,另外,可以使用SQLGetDiagRec函數獲取診斷記錄中一些常用的域,如SQLSTATE、原始錯誤號等。
(1) 頭記錄
頭記錄的各個域中包含了一個函數執行的通用信息,無論函數執行成功與否,只要不返回SQL_INVALID_HANDLE,都會生成頭記錄。
(2) 狀態記錄
狀態記錄中的每個域包含了驅動管理器、ODBC驅動程序或數據源返回的特定的錯誤或警告信息,包括SQLSTATE、原始錯誤碼、診斷信息、列號和行號等。只有函數執行返回SQL_ERROR、SQL_STILL_EXEUTING、SQL_SUCCESS_WITH_INFO、SQL_NEED_DATA或SQL_NO_DATA時,才會生成診斷記錄。
(3) 使用SQLGetDiagRec和SQLGetDiagField函數
應用程序可以調用SQLGetDiagRec和SQLGetDiagField函數獲取診斷信息。對於給定的句柄,這兩個函數返回最近使用句柄的函數的診斷信息。當使用該句柄的函數執行時,句柄記錄的原有的診斷信息被覆蓋。如果函數執行后產生多個狀態記錄,程序必須多次調用這兩個函數以獲取信息。
例如,下面的代碼示例用戶使用並執行SQL語句。如果返回一段消息,就可以調用SQLGetDiagField函數來取得狀態記錄並調用SQLGetDiagRec函數來從這些記錄中取得SQLSTATE、本地錯誤代碼、診斷消息等信息:
SQLCHARsqlState[6], SQLStmt[100], Msg[SQL_MAX_MESSAGE_LENGTH];
SQLINTRGERNativeError;
SQLSMALLINTI,MsgLen;
SQLRETURNrc1, rc2;
SQLHSTMEHstmt;
//用戶輸入一條SQL語句
GetSQLStmt(SQLStmt);
//執行SQL語句並返回錯誤結果
rc1= SQLExecDirect(hstmt, SQLStmt, SQL_NTS);
if((rc1== SQL_SUCCESS_WITH_INFO) || (rc1 == SQL_ERROR))
{
//得到狀態記錄
i = 1;
while((rc2 =SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState,
&NativeError, Msg,sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
{
DisplayError(SqlState, NativeError, Msg, MsgLen);
i++;
}
}
if((rc1== SQL_SUCCESS) || (rc1 == SQL_SUCCESS_WITH_INFO))
{
//如果必要就處理結果
}
第2章 ODBC API編程模型概述
2.1 ODBC訪問模式
查詢引擎使用一種客戶端/服務器的模式。一次查詢引擎會話由下列相關的進程(程序)組成:
1、一個服務器進程,它管理各個節點,接受來自客戶端應用與節點的連接,並且代表客戶端在節點上執行操作。
2、那些需要執行節點操作的用戶的客戶端(前端)應用。客戶端應用可能本身就是多種多樣的:它們可以是一個字符界面的工具,也可以是一個圖形界面的應用,或者是一個通過訪問查詢引擎來顯示網頁的 web 服務器,或者是一個特殊的管理工具。一些客戶端應用是和查詢引擎發布一起提供的,但絕大部分是用戶開發的。
和典型的客戶端/服務器應用(C/S應用)一樣,這些客戶端和服務器可以在不同的主機上。這時它們通過 TCP/IP 網絡連接通訊。查詢引擎可以處理來自客戶端的多個並發請求。因此,它為每個請求啟動一個新的進程。從這個時候開始,客戶端和新服務器進程就不再經過最初的進程進行通訊。因此,主服務器總是在運行,等待連接,而客戶端及其相關聯的服務器進程則是起起停停。當然,用戶是肯定看不到這些事情的。我們在這兒談這些主要是為了完整。
2.2ODBC API編程步驟
通常使用ODBC API開發數據庫應用程序需要經過如下步驟:
· 連接到數據源。
· 准備並執行SQL語句。
· 獲取結果集。
· 提交事務。
· 斷開數據源連接並釋放環境句柄。
為了更清楚的闡述ODBC API的調用步驟以及涉及到的重要函數,總結了一個表來說明
ODBC API應用的結構:
2.2.1 連接到數據源
下面的函數用於連接到數據源:
(1)SQLAllocHandle:分配環境、連接、語句或者描述符句柄。
(2)SQLConnect:建立與驅動程序或者數據源的連接。訪問數據源的連接句柄包含了包括狀態、事務申明和錯誤信息的所有連接信息。
(3)SQLDriverConnect:與SQLConnect相似,用來連接到驅動程序或者數據源。但它比SQLConnect支持數據源更多的連接信息,它提 供了一個對話框來提示用戶設置所有的連接信息以及系統信息表沒有定義的數據源。
(4)SQLBrowseConnect:支持一種交互方法來檢索或者列出連接數據源所需要的屬性和屬性值。每次調用函數可以獲取一個連接屬性字符串,當檢索完所有的屬性值,就建立起與數據源的連接,並且返回完整的連接字符串,否則提示缺少的連接屬性信息,用戶根據此信息重新輸入連接屬性值再次調用此函數進行連接。
#include<sql.h>
#include<sqltypes.h>
#include<sqlext.h>
SQLHENV henv; /*環境句柄*/
SQLHDBC hdbc; /*連接句柄*/
RETCODE retcode; /*返回碼 */
voidmain(void)
{
/* 分配一個環境句柄 */
SQLAllocHandle(SQL_HANDLE_ENV,NULL, &henv);
/* 設置環境句柄的ODBC版本 */
SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3,
SQL_IS_INTEGER);
/* 分配一個連接句柄 */
SQLAllocHandle(SQL_HANDLE_DBC,henv, &hdbc);
/* 連接數據源 */
retcode= SQLConnect(hdbc, (UCHAR)pszSourceName, SQL_NTS,
(UCHAR)pszUserId, wLengthUID,
(UCHAR)pszPassword, wLengthPSW );
if(retcode== SQL_SUCCESS) // 數據源連接成功
{
// 執行其它操作
…………
}
/* 釋放連接句柄*/
SQLFreeHandle(SQL_HANDLE_DBC,hdbc);
/* 釋放環境句柄 */
SQLFreeHandle(SQL_HANDLE_ENV,henv);
}
2.2.2 准備並執行SQL語句
調用函數SQLAllocHandle分配語句句柄,通過語句句柄應用程序可以執行SQL語句進行相關的SQL操作。調用函數SQLPrepare對SQL語句和操作進行准備,然后調用SQLExecute執行SQL語句,實現相關的SQL操作。應用程序也可以調用函數SQLExecDirect直接執行SQL語句進行相關的SQL操作。
示例如下:
SQLHSTMT hstmt;
RETCODE retcode;
/* 分配一個語句句柄 */
retcode= SQLAllocHandle (SQL_HANDLE_ENV, hdbc, &hstmt );
if(retcode== SQL_SUCCESS) // 連接句柄創建成功
{
// 執行其它操作
…………
}
/*執行一個查詢sql句柄 */
LPCSTRpszSQL;
strcpy(pszSQL,“SELECT * FROM EMPLOYEES”);
retcode=SQLExecDirect(hstmt, (unsigned char *)pszSQL, SQL_NTS );
if(retcode== SQL_SUCCESS) // SQL語句執行成功
{
// 執行其它操作
…………
}
調用SQLPrepare和SQLExecute函數的語法示例如下:
LPCSTRpszSQL;
strcpy(pszSQL,“SELECT * FROM EMPLOYEES”);
retcode=SQLPrepare( hstmt, (unsigned char *)pszSQL, SQL_NTS );
if(retcode== SQL_SUCCESS) // SQL語句准備成功
{
// 執行其它操作
…………
}
retcode= SQLExecute (hstmt, (unsigned char *)pszSQL, SQL_NTS );
if(retcode== SQL_SUCCESS) // SQL語句執行成功
{
// 執行其它操作
…………
}
2.2.3 獲取結果集
應用程序首先調用SQLNumResultCols函數,獲知每個記錄里有多少列,調用SQLDescribeCol函數取得每列的屬性,然后調用SQLBindCol函數將列數據綁定到指定的變量里,最后調用SQLFetch函數或者SQLGetData函數獲取數據。
對於其它的SQL語句,應用程序重復這個過程。這個過程代碼如下:
retcode=SQLNumResultCols( m_hstmt, &wColumnCount );
if(retcode!= SQL_SUCCESS ) // 列舉結果集列的個數不成功
{
// 釋放操作
…………
return;
}
LPSTR pszName;
retcode=SQLDescribeCol( m_hstmt,
wColumnIndex, // 列的索引
pszName, // 列的名稱
256, // 存放列名稱的緩沖區大小
& nRealLength, // 實際得到列名稱的長度
&wColumnType, // 列的數據類型
&dwPrecision, // 精度
&wScale, // 小數點位數
&wNullable ); // 是否允許空值
if(retcode!= SQL_SUCCESS ) // 執行不成功
{
// 釋放操作
…………
return;
}
retcode= SQLBindCol( hstmt,
uCounter, // 列索引
wColumnType, // 列數據類型
FieldValue, // 綁定的變量
dwBufferSize, // 變量內存大小
&BytesInBuffer); // 存放將來返回數據的大小的變量
if(retcode!= SQL_SUCCESS ) // 執行不成功
{
// 釋放操作
…………
return;
}
SQLFetch(hstmt);
// 此后可以從綁定的變量里讀取列的值。
…………
下面是通過SQLFetch和SQLGetData得到記錄集的例子:
//假設 SQL = SELECT CUSTID,NAME, PHONE FROM CUSTOMERS
SQLINTEGER sCustID
SQLCHAR szName[50], szPhone[50];
SQLINTEGERcbName, cbAge, cbBirthday;//用來保存得到的數據的長度
while(TRUE) {//循環得到所有行
retcode =SQLFetch(hstmt);//移動光標
if (retcode == SQL_ERROR || retcode ==SQL_SUCCESS_WITH_INFO) {
printf(“error SQLFetch\n”);
}
if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
SQLGetData(hstmt, 1, SQL_C_ULONG,&sCustID, 0, &cbCustID);
//此處並沒有指明BufferLength參數的值,是因為數據類型是定長的LONG型
SQLGetData(hstmt, 2, SQL_C_CHAR, szName, 50,&cbName);
SQLGetData(hstmt, 3, SQL_C_CHAR,szPhone, 50,&cbPhone);
printf(out, "%5d %s%s", sCustID, szName, szPhone);
}else {
break;
}
}
2.2.4 提交事務
當所有的SQL語句都被執行並接收了所有的數據以后,應用程序需要調用SQLEndTran()函數提交或者回退事務。如果提交方式為手工(應用程序設置)方式,則需要應用程序執行這個語句以提交或者回退事務,如果是自動方式,當SQL語句執行后,該命令自動執行。
調用SQLEndTran函數的語法如下:
SQLEndTran(SQL_HANDLE_DBC,hdbc, SQL_COMMIT); //提交事務
SQLEndTran(SQL_HANDLE_DBC,hdbc, SQL_ROLLBACK); //回滾事務
2.2.5 斷開數據源連接並釋放環境句柄
當應用程序使用完ODBC以后,需要使用SQLFreeHandle()函數釋放所有語句句柄、連接句柄、環境句柄。這里需要注意操作的順序,應該是先釋放所有語句句柄,調用SQLDisconnect()函數解除與數據源的連接,然后釋放所有連接句柄,最后釋放環境句柄,使應用程序同ODBC管理器的連接徹底解除。
第3章 連接到數據源
連接到數據源包括初始化環境句柄、初始化連接句柄以及連接數據源等幾個步驟。
3.1 初始化環境句柄
3.1.1 分配環境句柄
SQLAllocHandle函數的定義如下:
SQLRETURNSQLAllocHandle(
SQLSMALLINT HandleType,
SQLHANDLE InputHandle,
SQLHANDLE * OutputHandlePtr
);
第一個參數HandleType的取值可以為:SQL_HANDLE_ENV:申請環境句柄。SQL_HANDLE_DBC:申請數據庫連接句柄。SQL_HANDLE_STMT:申請SQL語句句柄,每次執行SQL語句都申請語句句柄,並且在執行完成后釋放。
第二個參數為輸入句柄,第三個參數為輸出句柄,也就是你在第一參數指定的需要申請的句柄。首先聲明一個SQLHENV類型的變量,然后調用SQLAllocHandle函數,向其中傳遞分配的SQLHENV類型的變量地址和SQL_HANDLE_ENV選項。
分配環境句柄示例代碼如下:
SQLHENV henv; /* 環境句柄 */
SQLHDBC hdbc; /* 連接句柄 */
RETCODE retcode; /* 返回碼*/
retcode= SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
if((retcode!= SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
{
AfxMessageBox(“分配環境句柄失敗”);
return FALSE;
}
執行該調用語句后,驅動程序分配一個結構,該結構中存放環境變量,然后返回對應於該環境的環境句柄。
一個應用程序對應於一個環境,但是同一個環境可以用於不同數據源的連接,可以使用多個線程,應用程序完成數據訪問任務,應調用函數SQLFreeHandle函數釋放當前分配的環境。
/* 釋放環境句柄 */
SQLFreeHandle(SQL_HANDLE_ENV,henv);
3.1.2 設置環境屬性
應用程序在完成環境的分配后,接着調用函數SQLSetEnvAttr設置環境屬性,注冊ODBC的版本號,說明應用程序所遵從的標准是ODBC 2.x還是ODBC 3.x,相關代碼如下所示。使用不同的版本,相同的參數作用會不相同,具體的說明參見MSDN。
SQLSetEnvAttr的函數定義如下:
SQLRETURNSQLSetEnvAttr(
SQLHENV EnvironmentHandle,
SQLINTEGER Attribute,
SQLPOINTER ValuePtr,
SQLINTEGER StringLength
);
ConnectionHandle:提供環境句柄。
Attribute:指定需要設置的屬性類型,這里設置為SQL_ATTR_ODBC_VERSION,其他屬性類型請查看MSDN。
ValuePtr:提供參數值。
StringLength:指定參數的長度,當參數為字符串時設置為字符串長度或者為SQL_NTS,而對於普通的變量如SQLINTEGER,SQLFLOAT等設置為0就可以了。
設置ODBC版本的環境屬性示例代碼如下:
retcode=SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3,0);
if((retcode!= SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
{
ReportError(m_henv, SQL_HANDLE_ENV, “設置ODBC版本號時失敗”);
return FALSE;
}
3.2 初始化連接句柄
3.2.1 分配連接句柄
分配環境句柄之后,在建立至數據源的連接之前,必須分配一個連接句柄,每一個到數據源的連接對應於一個連接句柄。
首先,程序定義一個SQLHDBC類型的變量,用於存放連接句柄,然后調用SQLAllocHandle函數分配句柄。
分配連接句柄示例代碼如下:
SQLHENV henv; /* 環境句柄 */
SQLHDBC hdbc; /* 連接句柄 */
RETCODE retcode; /* 返回碼 */
retcode= SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
if((retcode!= SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
{
ReportError(m_henv, SQL_HANDLE_ENV, “分配連接句柄失敗!”);
return FALSE;
}
3.2.2 設置連接屬性
連接屬性表示了一個連接的特性,如登錄等待時間等。所有的連接屬性都有默認值,也可以通過調用函數SQLSetConnectAttr設置。
SQLSetConnectAttr的函數定義如下:
SQLRETURNSQLSetConnectAttr(
SQLHDBC ConnectionHandle,
SQLINTEGER Attribute,
SQLPOINTER ValuePtr,
SQLINTEGER StringLength);
ConnectionHandle:提供DBC連接句柄。
Attribute:指定需要設置的屬性類型,在這里設置為值SQL_ATTR_AUTOCOMMIT,需要強制轉換位指針類型。其他屬性類型請查看MSDN。
ValuePtr:提供參數值。
StringLength:指定參數的長度,當參數為字符串時設置為字符串長度或者為SQL_NTS,而對於普通的變量如SQLINTEGER,SQLFLOAT等設置為0就可以了。
示例代碼如下:
retcode= SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
//設置登錄等待時間為5秒
SQLSetConnectAttr(hdbc,SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
// 連接到數據源
retcode= SQLConnect(hdbc, (SQLCHAR*) "SQL SWIFT", SQL_NTS,
(SQLCHAR*)NULL,0, NULL, 0);
}
3.3 連接數據源
完成連接屬性的設置之后,就可以建立到數據源的連接了。對於不同的程序和用戶接口,可以用不同的函數建立連接:SQLConnect、SQLDriverConnect、SQLBrowseConnect。
3.3.1 使用SQLConnect()函數
該函數提供了最為直接的程序控制方式,只要提供數據源名稱、用戶ID和口令,就可以進行連接了。其定義如下:
SQLRETURNSQLConnect(
SQLHDBC ConnectionHandle, //連接句柄
SQLCHAR *ServerName, //數據源名稱
SQLSMALLINT NameLength1, //數據源名稱長度
SQLCHAR *UserName, //用戶ID
SQLSMALLINT NameLength2, //用戶ID長度
SQLCHAR *Authentication, //用戶口令
SQLSMALLINT NameLength3 //用戶口令長度
);
使用SQLConnect函數連接數據源示例如下:
retcode=SQLConnect(hdbc, (SQLCHAR *)cpServerName, SQL_NTS,
(SQLCHAR*)cpUserName, SQL_NTS,(SQLCHAR *)cpPassword, SQL_NTS);
if((retcode!=SQL_SUCCESS) && (retcode!= SQL_SUCCESS_WITH_INFO))
{
ReportError(hdbc, SQL_HANDLE_DBC, “連接數據庫失敗!”);
return FALSE;
}
3.3.2 使用SQLDriverConnect()函數
該函數用一個連接字符串建立到數據源的連接,他提供比SQLConnect函數的參數更多的信息,可以讓用戶輸入必要的連接信息,使用系統中還沒有定義的數據源。
如果連接建立,該函數會返回完整的連接字符串,應用程序可以使用連接字符串建立額外的連接,其定義如下:
SQLRETURNSQLDriverConnect(
SQLHDBC ConnectionHandle; //連接句柄
SQLHWND WindowHandle; //窗口句柄,程序可以用父窗口的句柄
//或用NULL指針
SQLCHAR * InConnectionString; //一個指向連接字符串的指針
SQLSMALLINT StringLength1, //連接字符串長度
SQLCHAR * OutConnectionString; //一個指向連接字符串的指針
SQLSMALLINT BufferLength; //存放連接字符串的緩沖區的長度
SQLSMALLINT * StringLength2Ptr; //返回的連接字符串中的字符數
SQLSMALLINT DriverCompletion//額外的連接信息,可能的取值:
//SQL_DRIVER_PROMPT、
//SQL_DRIVER_COMPLETE、
//SQL_DRIVER_COMPLETE_REQUIRED、
//SQL_DRIVER_NOPROMPT
);
示例代碼如下:
//分配連接句柄
if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
retcode = SQLAllocHandle(SQL_HANDLE_DBC,henv, &hdbc);
// 設置登陸等待5秒
if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
SQLSetConnectAttr(hdbc,SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
retcode = SQLDriverConnect(hdbc,desktopHandle,
(SQLCHAR*)"driver=SQL SWIFT",_countof("driver=SQLSWIFT "),
OutConnStr,255,&OutConnStrLen,SQL_DRIVER_PROMPT );
if (retcode == SQL_SUCCESS || retcode ==SQL_SUCCESS_WITH_INFO){
//分配語句句柄
retcode = SQLAllocHandle(SQL_HANDLE_STMT,hdbc, &hstmt);
// 操作數據
}
3.3.3 使用SQLBrowseConnect()函數
函數SQLBrowseConnect支持以一種迭代的方式獲取到數據源的連接,直到最后建立連接。它基於客戶機/服務器體系結構。
一般,只提供部分連接信息,如果足以建立到數據源的連接,則成功建立連接,否則返回SQL_NEED_DATA,並在OutConnectionString參數中返回所需要的信息。其定義如下:
SQLRETURNSQLBrowseConnect(
SQLHDBC ConnectionHandle, //連接句柄
SQLCHAR *InConnectionString, //指向輸入字符串的指針
SQLSMALLINT StringLength1, //輸入字符串的指針長度
SQLCHAR *OutConnectionString, //指向輸出字符串的指針
SQLSMALLINT BufferLength, //存放輸出字符串的緩沖區的長度
SQLSMALLINT *StringLength2Ptr //實際返回的字符串的長度
);
使用SQLBrowseConnect函數連接數據源的示例代碼如下:
retcode= SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
strcpy_s((char*)szConnStrIn,_countof(szConnStrIn), "DSN=Sales");
do {
retcode= SQLBrowseConnect(hdbc, szConnStrIn,SQL_NTS,szConnStrOut,
BRWS_LEN,&cbConnStrOut);
if (retcode == SQL_NEED_DATA)
GetUserInput(szConnStrOut,szConnStrIn);
}while (retcode == SQL_NEED_DATA);
if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
//分配語句句柄
retcode= SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
if (retcode ==SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
//連接成功后處理數據
SQLFreeHandle(SQL_HANDLE_STMT,hstmt);
SQLDisconnect(hdbc);
}
}
第4章 准備並執行SQL語句
4.1 構造SQL語句
可以通過3種方式構造SQL語句:
l 在程序開發階段確定:具有易於實現且可在程序編碼時進行測試的優點。
l 在運行時確定:提供了極大靈活性,但給程序帶來了困難,卻需要更多的處理時間。
l 由用戶輸入SQL語句:極大的增強了程序的功能,但是程序必須提供有好的程序界面,且對用戶輸入的語句執行一定程序的語法檢查,能夠報告用戶錯誤。
4.2 執行SQL語句
4.2.1 分配語句句柄
語句句柄為ODBC驅動程序管理器提供數據結構,並跟蹤SQL語句的執行機器返回的結果,語句句柄是通過調用SQLAllocHandle函數分配的。下面的代碼聲明了一個存放語句句柄的變量hstmt,在函數中提供該變量的地址指針、所使用的連接句柄hdbc以及選項SQL_HANDLE_STMT:
retcode=SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
if((retcode!=SQL_SUCCESS) && (retcode!= SQL_SUCCESS_WITH_INFO))
{
ReportError(hdbc, SQL_HANDLE_DBC, “分配語句句柄失敗,不能執行”);
return FALSE;
}
當一個語句句柄使用完成后,調用函數SQLFreeHandle釋放句柄。
/* 釋放語句句柄*/
retcode=SQLFreeHandle(SQL_HANDLE_ENV, hstmt);
4.2.2 使用SQLExecDirect()函數
使用SQLExecDirect函數直接執行SQL語句,對於只執行一次的SQL語句來說,該函數是執行最快的方法,其定義如下:
SQLRETURNSQLExecDirect(SQLHSTMT StatementHandle, //語句句柄
SQLCHAR*StatementText, //要執行的SQL語句
SQLINTEGER TextLength //SQL語句的長度
);
使用該函數執行SQL語句的代碼:
retcode= SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
retcode= SQLExecDirect(hstmt, (SQLCHAR *) "SELECT CustomerID, ContactName,
PhoneFROM CUSTOMERS ORDER BY 2, 1, 3", SQL_NTS);
4.2.3 使用SQLPrepare()和SQLExecute()函數
對於需要多次執行的SQL語句來說,除了使用SQLExecDirect函數之外,也可以在執行SQL語句之前,先調用函數SQLPrepare准備SQL語句的執行。對於使用參數的語句,這可大大提高程序的執行速度。而函數SQLExecute用來執行一個准備好的語句,當語句中有參數時,用當前綁定的參數變量的值。其定義如下:
SQLRETURNSQLPrepare(SQLHSTMT StatementHandle, //語句句柄
SQLCHAR*StatementText, //要執行的SQL語句
SQLINTEGERTextLength //SQL語句的長度
);
SQLRETURNSQLExecute(SQLHSTMT StatementHandle); //語句句柄
示例代碼1:
retcode= SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
retcode= SQLPrepare(hstmt, (SQLCHAR*)"{call SQLBindParameter(?)}",
SQL_NTS);
retcode= SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR,50, 0, szQuote, 0, &cbValue);
retcode= SQLGetStmtAttr(hstmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0);
retcode= SQLSetDescField(hIpd, 1, SQL_DESC_NAME, "@quote", SQL_NTS);
retcode= SQLExecute(hstmt);
示例代碼2:
retcode= SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
retcode= SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR,EMPLOYEE_ID_LEN, 0, szEmployeeID, 0, &cbEmployeeID);
retcode= SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_SSHORT,
SQL_INTEGER,0, 0, &sCustID, 0, &cbCustID);
retcode= SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_TYPE_DATE,
SQL_TIMESTAMP,sizeof(dsOrderDate), 0, &dsOrderDate,
0,&cbOrderDate);
retcode= SQLPrepare(hstmt, (SQLCHAR*)"INSERT INTO Orders(CustomerID,
EmployeeID,OrderDate) VALUES (?, ?, ?)", SQL_NTS);
strcpy_s((char*)szEmployeeID,_countof(szEmployeeID), "BERGS");
sCustID= 5;
dsOrderDate.year= 2006;
dsOrderDate.month= 3;
dsOrderDate.day= 17;
retcode= SQLExecute(hstmt);
4.2.4 使用參數
使用參數可以使一條SQL語句多次執行,得到不同的結果,如下面的一條SQL語句:
INSERTINTO EMP(職員號,職工名稱,工作) VALUES(?,?,?)
該語句將向表emp中加入一條記錄,通過參數替換(將句中問號出現的地方以相應的參數值替換),可以在程序運行的時候動態的添加多條記錄。
SQL語句執行時,參數標識符(“?”)綁定程序中的變量,通過參數就可以在程序執行時動態的將值傳入SQL語句。
函數SQLBindParameter負責為參數定義變量,將一段SQL語句中的一個參數標識符(“?”)綁定在一起,實現參數值的傳遞,其定義如下:
SQLRETURNSQLBindParameter(
SQLHSTMTStatementHandle, //語句句柄
SQLUSMALLINTParameterNumber, //綁定的參數在SQL語句中的序號,在SQL中,
//所有參數從左到右依次編號(從1開始)。
//SQL語句執行之前,應該為每個參數調用函數
//SQLBindParameter綁定到某個程序變量。
SQLSMALLINTInputOutputType, //參數類型,可為SQL_PARA_INPUT、
//SQL_PARAM_INPUT_OUTPUT、
//SQL_PARAM_OUTPUT
SQLSMALLINTValueType, //參數的C數據類型
SQLSMALLINTParameterType, //參數的SQL數據類型
SQLUINTEGERColumnSize, //參數的大小
SQLSMALLINTDecimalDigits, //參數的精度
SQLPOINTERParameterValuePtr, //指向程序中存放參數值的緩沖區的指針
SQLINTEGERBufferLength, //程序中存放參數值的緩沖區的字節數
SQLINTEGER*StrLen_or_IndPtr //指向存放參數ParameterValuePtr的緩沖區指針
);
下面是一個實際應用的例子
#include<sqltypes.h>
#include<sqlext.h>
#defineEMPLOYEE_ID_LEN 10
SQLHENVhenv = NULL;
SQLHDBChdbc = NULL;
SQLRETURNretcode;
SQLHSTMThstmt = NULL;
SQLSMALLINTsCustID;
SQLCHARszEmployeeID[EMPLOYEE_ID_LEN];
SQL_DATE_STRUCTdsOrderDate;
SQLINTEGERcbCustID = 0, cbOrderDate = 0, cbEmployeeID = SQL_NTS;
intmain() {
retcode =SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
retcode =SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER*)SQL_OVODBC3,0);
retcode= SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
retcode = SQLSetConnectAttr(hdbc,SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
retcode = SQLConnect(hdbc, (SQLCHAR*)"Northwind", SQL_NTS,
(SQLCHAR*) NULL, 0, NULL,0);
retcode = SQLAllocHandle(SQL_HANDLE_STMT,hdbc, &hstmt);
retcode = SQLBindParameter(hstmt, 1,SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR,EMPLOYEE_ID_LEN, 0, szEmployeeID,0, &cbEmployeeID);
retcode = SQLBindParameter(hstmt, 2,SQL_PARAM_INPUT, SQL_C_SSHORT,
SQL_INTEGER,0, 0, &sCustID, 0, &cbCustID);
retcode = SQLBindParameter(hstmt, 3,SQL_PARAM_INPUT,
SQL_C_TYPE_DATE,SQL_TIMESTAMP,sizeof(dsOrderDate), 0,&dsOrderDate, 0,
&cbOrderDate);
retcode = SQLPrepare(hstmt,(SQLCHAR*)"INSERT INTO Orders(CustomerID,
EmployeeID,OrderDate) VALUES (?, ?, ?)", SQL_NTS);
strcpy_s((char*)szEmployeeID,_countof(szEmployeeID), "BERGS");
sCustID = 5;
dsOrderDate.year = 2006;
dsOrderDate.month = 3;
dsOrderDate.day = 17;
retcode = SQLExecute(hstmt);
}
第5章 獲取結果集
查詢結果集是數據源上滿足一定條件的數據記錄的集合,在概念上,可以看成是一個臨時存放返回結果的表。結果集可以是空。在用SQLExecDirect等函數執行SQL語句后,就可以從數據源中獲取結果集。
5.1 使用SQLNumResultCols()函數
調用SQLNumResultCols函數,獲知每個記錄里有多少列,函數的定義如下:
SQLRETURNSQLNumResultCols(
SQLHSTMT StatementHandle, // 語句句柄
SQLSMALLINT * ColumnCountPtr //返回列數
);
5.2 使用SQLDescribeCol()函數
調用SQLDescribeCol函數取得每列的屬性,函數定義如下:
SQLRETURNSQLDescribeCol(
SQLHSTMT StatementHandle,
SQLSMALLINT ColumnNumber,
SQLCHAR * ColumnName,
SQLSMALLINT BufferLength,
SQLSMALLINT * NameLengthPtr,
SQLSMALLINT * DataTypePtr,
SQLINTEGER * ColumnSizePtr,
SQLSMALLINT * DecimalDigitsPtr,
SQLSMALLINT * NullablePtr
);
StatementHandle:語句句柄。
ColumnNumber:需要得到的列的序號,從1開始計算。
ColumnName:得到列的名稱。
BufferLength:指明ColumnName參數的最大長度。
NameLengthPtr:返回列名稱的長度。
DataTypePtr:得到列的ODBC數據類型。
ColumnSizePtr:得到列的長度。
DecimalDigitsPtr:當該列為數字類型時返回小數點后數據的位數。
NullablePtr:指明該列是否允許為空值。
5.3 綁定列
從數據源取回的數據存放在應用程序定義的變量中,因此,必須首先分配與結果集中字段相對應的變量,然后通過函數SQLBindCol將記錄字段同程序變量綁定在一起。對於長記錄字段,可以通過調用函數SQLGetData直接取回數據。
綁定字段可以根據自己的需要全部綁定,也可以綁定其中的某幾個字段。
通過調用函數SQLBindCol將其變量地址賦值為NULL,可以結束對一個記錄字段的綁定;通過調用函數SQLFreeStmt,將其中的選項設為SQL_UNBIND,或者是直接釋放句柄,都會結束所有記錄字段的綁定。函數SQLBindCol的定義如下:
SQLRETURNSQLBindCol(
SQLHSTMT StatementHandle, //語句句柄
SQLSMALLINTColumnNumber, //標識要綁定的列號。數據列號從0開始升序排
//列,其中第0列用作書簽,則列號從1開始
SQLSMALLINT TargetType, //數據類型
SQLPOINTER TargetValuePtr, //綁定到數據字段的緩沖區的地址
SQLINTEGER BufferLength, //緩沖區長度
SQLINTEGER *StrLen_or_IndPtr //指向綁定數據列使用的長度的指針
);
示例代碼如下:
retcode= SQLExecDirect(hstmt, (SQLCHAR *) "SELECT CustomerID, ContactName,
PhoneFROM CUSTOMERS ORDER BY 2, 1, 3", SQL_NTS);
if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
// 綁定1、2、3列
retcode= SQLBindCol(hstmt, 1, SQL_C_CHAR, &sCustID, 100, &cbCustID);
retcode= SQLBindCol(hstmt, 2, SQL_C_CHAR, szName, NAME_LEN, &cbName);
retcode= SQLBindCol(hstmt, 3, SQL_C_CHAR, szPhone, PHONE_LEN, &cbPhone);
// 獲得並打印每一列的信息,如果有錯誤打印錯誤並推出
……
}
5.4 使用SQLFetch()函數
函數SQLFetch用於將記錄集中的下一行變為當前行,並把所有捆綁過的數據字段的數據拷貝到相應的緩沖區,其定義如下:
SQLRETURNSQLFetch(SQLHSTMT StatementHandle); //StatementHandle:語句句柄
使用SQLFetch函數的示例代碼如下:
//獲得並打印每一列的信息,如果有錯誤打印錯誤並推出
for(i ; ; i++) {
retcode =SQLFetch(hstmt);
if (retcode ==SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
show_error();
if (retcode ==SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
wprintf(L"%d: %S%S %S\n", i + 1, sCustID, szName, szPhone);
else
break;
}
總結一下,要獲取結果集一般先用函數SQLExecDirect執行SQL語句,接着用函數SQLBindCol綁定列,然后用函數SQLFetch遍歷記錄,獲取記錄的示例代碼如下:
#defineNAME_LEN 50
#definePHONE_LEN 10
SQLCHARszName[NAME_LEN], szPhone[PHONE_LEN];
SQLINTEGERsCustID, cbName, cbCustID, cbPhone;
SQLHSTMThstmt;
SQLRETURNretcode;
//執行SQL語句
retcode= SQLExecDirect(hstmt,
“SELECTCUSTID, NAME, PHONE FROM CUSTOMERS ORDER BY 2,1,3”,
SQL_NTS);
if((retcode!= SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)){
//執行成功
//綁定1,2,3列
SQLBindCol(hstmt, 1, SQL_C_ULONG,&sCustID, 0, &cbCustID);
SQLBindCol(hstmt, 2, SQL_C_CHAR, szName,NAME_LEN, &cbName);
SQLBindCol(hstmt, 3, SQL_C_CHAR, szPhone,PHONE_LEN, &cbPhone);
//遍歷記錄集,輸出結果
while(TRUE){
retcode = SQLFetch(hstmt);
if(retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO){
//出錯,輸出錯誤,返回
show_error();
}
if(retcode== SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
fprintf(out, “%-*s%-5d %*s”,NAME_LEN-1, szName, sCustID,
PHONE_LEN-1,szPhone);
}else{
break;
}
}
}
5.5 使用SQLGetData()函數
SQLGetData可用於檢索結果集數據,而無需綁定列值。可以對同一列連續調用SQLGetData函數以從text、ntext或image數據類型的列中檢索大量數據。
SQLGetData函數的定義如下:
SQLRETURNSQLGetData(
SQLHSTMT StatementHandlem,// 語句句柄
SQLUSMALLINT ColumnNumber, // 列號,以1開始
SQLSMALLINT TargetType, // 數據緩沖區(TargetValuePtr)的C語言類
SQLPOINTER TargetValuePtr, // 數據緩沖區
SQLINTEGER BufferLength, // 數據緩沖區(TargetValuePtr)的長度
SQLINTEGER* StrLen_or_IndPtr // 返回當前字段得到的字節長度
);
使用SQLGetData函數的示例代碼如下:
#defineNAME_LEN 50
#definePHONE_LEN 50
SQLCHAR szName[NAME_LEN], szPhone[PHONE_LEN];
SQLINTEGER sCustID, cbName, cbAge, cbBirthday;
SQLRETURN retcode;
SQLHSTMT hstmt;
retcode= SQLExecDirect(hstmt,
"SELECTCUSTID, NAME, PHONE FROM CUSTOMERS ORDER BY 2, 1, 3"
SQL_NTS);
if(retcode == SQL_SUCCESS) {
while(TRUE) {
retcode= SQLFetch(hstmt);
if (retcode == SQL_ERROR) {
show_error();
}
if (retcode == SQL_SUCCESS || retcode ==SQL_SUCCESS_WITH_INFO){
/*直接得到1,2,3列數據*/
SQLGetData(hstmt, 1, SQL_C_LONG,&sCustID, 0, &cbCustID);
SQLGetData(hstmt, 2, SQL_C_CHAR, szName,NAME_LEN,
&cbName);
SQLGetData(hstmt, 3, SQL_C_CHAR, szPhone,PHONE_LEN,
&cbPhone);
/* 打印每一列的數據 */
fprintf(out, "%-5d %-*s %*s",sCustID, NAME_LEN-1, szName,
PHONE_LEN-1, szPhone);
}else{
break;
}
}
}
5.6游標
應用程序獲取數據是通過游標(Cursor)來實現的,執行語句和操縱結果集的 ODBC 函數使用游標執行它們的任務。應用程序每次執行SQLExecute 或 SQLExecDirect 函數時,都會隱式打開游標。
如果游標在結果集中只正向移動而不更新結果集,對於這種應用情況,游標行為相對比較簡單。缺省情況下,ODBC 應用程序會請求此行為。ODBC 定義一個只讀的單向游標。
單向游標只能向前移動,要返回記錄集的開始位置,必須先關閉游標,再打開游標,它對與只需要瀏覽一次的應用非常有用,而且效率很高。有關單向游標的簡單示例,請參見數據檢索。
SQLRETURNSQLFetch(SQLHSTMT);
作用:在綁定好結果列后就可以獲取結果集了,判斷是否讀取到結尾的方法有判斷返回結果是否是正確值,或是否等於SQL_NO_DATA。
5.7 數據檢索
若要從數據庫檢索行,可使用SQLExecute或 SQLExecDirect執行 SELECT 語句。這將為該語句打開一個游標。然后,使用 SQLFetch讀取行,使用SQLBindCol 或 SQLGetData讀取值。如果使用 SQLBindCol,每次讀取時自動檢索值。如果使用SQLGetData,必須在每次讀取后為每一列調用它。最后使用 SQLFreeHandle 釋放語句后,它將關閉游標。
為了使示例更容易讀,這里省略了錯誤檢查操作。
SQLINTEGERcbDeptID = 0, cbDeptName = SQL_NTS, cbManagerID = 0;
SQLCHARdeptName[ DEPT_NAME_LEN + 1 ];
SQLSMALLINTdeptID, managerID;
SQLHENV env;
SQLHDBC dbc;
SQLHSTMTstmt;
SQLRETURNretcode;
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env );
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3,0);
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc );
SQLConnect(dbc,(SQLCHAR*) "SQL SWIFT Demo", SQL_NTS,
(SQLCHAR*)"DBA", SQL_NTS, (SQLCHAR*) "sql", SQL_NTS );
SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt );
SQLBindCol(stmt, 1, SQL_C_SSHORT, &deptID, 0, &cbDeptID);
SQLBindCol(stmt, 2, SQL_C_CHAR, deptName, sizeof(deptName),&cbDeptName);
SQLBindCol(stmt, 3, SQL_C_SSHORT, &managerID, 0, &cbManagerID);
SQLExecDirect(stmt, (SQLCHAR * )
"SELECTDepartmentID, DepartmentName, DepartmentHeadID FROM
DepartmentsORDER BY DepartmentID", SQL_NTS );
while(( retcode = SQLFetch( stmt ) ) != SQL_NO_DATA ){
printf("%d %20s %d\n", deptID, deptName, managerID );
}
SQLFreeHandle(SQL_HANDLE_STMT, stmt );
SQLDisconnect(dbc );
SQLFreeHandle(SQL_HANDLE_DBC, dbc );
SQLFreeHandle(SQL_HANDLE_ENV, env);
第6章 錯誤處理
每個ODBC API函數都能產生一系列的反應操作信息的診斷記錄。可以用SQLGetDiagField函數獲取診斷記錄中的特定的域,另外,可以使用SQLGetDiagRec函數獲取診斷記錄中的一些常用的域,如SQLSTATE、原始錯誤號等。
SQLGetDiagRec函數的定義如下:
SQLRETURNSQLGetDiagRec(
SQLSMALLINT HandleType,
SQLHANDLE Handle,
SQLSMALLINT RecNumber,
SQLCHAR * Sqlstate,
SQLINTEGER * NativeErrorPtr,
SQLCHAR * MessageText,
SQLSMALLINT BufferLength,
SQLSMALLINT * TextLengthPtr
);
RecNumber:指明需要得到的錯誤狀態行,從1開始逐次增大。
Sqlstate,NativeErrorPtr,MessageText:返回錯誤狀態,錯誤代碼和錯誤描述。
BufferLength:指定MessageText的最大長度。
TextLengthPtr:指定返回的MessageText中有效的字符數。
函數的返回值可能為:SQL_SUCCESS,SQL_SUCCESS_WITH_INFO,SQL_ERROR,SQL_INVALID_HANDLE,SQL_NO_DATA。在沒有返回錯誤的情況下你需要反復調用此函數,並順次增大RecNumber參數的值,直到函數返回SQL_NO_DATA,以得到所有的錯誤描述。
得到STMT句柄上的錯誤信息,示例代碼如下:
SQLCHARSqlState[6],SQLStmt[100],Msg[SQL_MAX_MESSAGE_LENGTH];
SQLINTEGER NativeError;
SQLSMALLINT i, MsgLen;
inti = 1;
while((retcode = SQLGetDiagRec(SQL_HANDLE_STMT,hstmt, i, SqlState,
&NativeError, Msg, sizeof(Msg),&MsgLen)) != SQL_NO_DATA)
{
//顯示錯誤的代碼
i++;
}
第7章 事務處理
ODBC中實現對支持事務,在ODBC中支持自動提交模式和手工提交模式,當使用自動提交模式時每執行一次SQL語句ODBC會自動提交本次所進行的修改;當使用手工提交模式時,你必須顯示的提交或回滾當前的操作才能夠使修改生效。在ODBC 2.X中使用SQLTransact來開始和結束事務,但是在 3.X中不再使用,而使用SQLSetConnectAttr 來設置ODBC調用的事務特性,並使用SQLEndTran結束事務。
如果要設置為自動提交模式使用下面的方式調用:
SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT,
(SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_POINTER);
如果要設置為手工提交模式使用下面的方式調用:
SQLSetConnectAttr(hdbc,SQL_ATTR_AUTOCOMMIT,
(SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_POINTER);
在手工提交模式下,需要使用SQLEndTran來提交或回滾當前事務。
SQLRETURNSQLEndTran(
SQLSMALLINT HandleType,
SQLHANDLE Handle,
SQLSMALLINT CompletionType);
HandleType:指定句柄的類型,設置為值:SQL_HANDLE_DBC。
Handle:提供DBC連接句柄。
CompletionType:設置為SQL_COMMIT或者SQL_ROLLBACK表明提交或者回滾。
示例代碼如下:
SQLEndTran(SQL_HANDLE_DBC,hdbc, SQL_COMMIT); // 提交事務
SQLEndTran(SQL_HANDLE_DBC,hdbc, SQL_ROLLBACK); // 回滾事務
第8章 斷開數據源連接並釋放環境句柄
當應用程序使用完ODBC以后,需調用SQLFreeHandle函數釋放所有語句句柄、連接句柄、環境句柄(注意釋放順序為逆序)。先釋放語句句柄、再調用SQLDisconnect函數解除與數據源的連接、再次調用SQLFreeHandle函數釋放連接句柄、最后釋放環境句柄。
SQLDisconnect函數定義:
SQLRETURNSQLDisconnect(
SQLHDBC ConnectionHandle //連接句柄
);
斷開數據源連接並釋放句柄示例代碼如下:
intmain()
{
SQLHENV henv;
SQLHDBC hdbc;
SQLHSTMT hstmt;
SQLRETURN retcode;
SQLCHAR * OutConnStr = (SQLCHAR *)malloc(255);
SQLSMALLINT * OutConnStrLen = (SQLSMALLINT*)malloc(255);
// 分配環境句柄
retcode = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE, &henv);
// 設置ODBC版本環境屬性
if (retcode == SQL_SUCCESS || retcode ==SQL_SUCCESS_WITH_INFO)
{
retcode = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,
(void*)SQL_OV_ODBC3,0);
// 分配連接句柄
if (retcode == SQL_SUCCESS || retcode ==SQL_SUCCESS_WITH_INFO)
{
retcode =SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
// 設置登陸等待5秒
if (retcode == SQL_SUCCESS || retcode== SQL_SUCCESS_WITH_INFO)
{
SQLSetConnectAttr(hdbc,SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
// 連接到數據源
retcode = SQLConnect(hdbc,(SQLCHAR*) "NorthWind", SQL_NTS,
(SQLCHAR*) NULL, 0,NULL, 0);
// 分配語句句柄
if (retcode == SQL_SUCCESS || retcode==
SQL_SUCCESS_WITH_INFO
{
retcode =SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
// 處理數據
if (retcode == SQL_SUCCESS ||retcode ==
SQL_SUCCESS_WITH_INFO)
{
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}
SQLDisconnect(hdbc);
}
SQLFreeHandle(SQL_HANDLE_DBC,hdbc);
}
}
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}
}
關於更詳細的ODBC API的信息,可以參考MSDN ODBC Reference。可以在http://msdn.microsoft.com/en-us/library/ms713607(v=vs.85).aspx獲得相關文檔。
另外編寫本文還參考了以下兩個文檔:
http://msdn.microsoft.com/zh-cn/library/1dwe8111.aspx
http://www.cnblogs.com/kzloser/archive/2012/11/29/2794815.html
