ODBC API 学习总结
生活随笔
收集整理的這篇文章主要介紹了
ODBC API 学习总结
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
ODBC 編程API
http://blog.csdn.net/bichenggui/article/details/5601381轉的ODBC API函數詳細說明。
看了一遍,沒有問題。
使用 ODBC API 之前要用到的頭文件和 LIB 庫?
#include "sql.h" ? ? ? ? ? ? ?// This is the the main include for ODBC Core functions.
#include "sqlext.h" ? ? ? ? // This is the include for applications using the Microsoft SQL Extensions
#include "sqltypes.h" ? ?// This file defines the types used in ODBC
#include "sqlucode.h" ? // This is the the unicode include for ODBC Core functions
#include "odbcss.h" ? ? ?// This is the application include file for the SQL Server driver specific defines.
#pragma coment(lib, "odbc32.lib")?
ODBC API 的返回值?
? ? ? ? ?ODBC API 的返回值定義為: SQLRETURN 。 在成功時返回值為: SQL_SUCCESS , SQL_SUCCESS_WITH_INFO ; 在失敗時返回錯誤代碼。一點需要注意的是如果 ODBC 返回值為: SQL_SUCCESS_WITH_INFO 并不表明執行完全成功,而是表明執行成功但是 帶有一定錯誤信息。當執行錯誤時 ODBC 返回的是一個錯誤信息的結果集,你需要遍歷結果集合中所有行,這點和后面講到的查詢 SQL 語句執行結果集的思路很 類似。
? ? ? ? SQL_SUCCESS的enum值為0, SQL_SUCCESS_WITH_INFO的enum值為1.所以你可以用返回值是否小于0來簡單判斷。
SQLAllocHandle 創建 ODBC 句柄?
? ? ? ? ?SQLRETURN SQLAllocHandle (
? ? ? ? ? ? ? SQL SMALLINT ? ? HandleType, ? ? ? ? ? ? ? // 需要申請的句柄類型?
? ? ? ? ? ? ? SQLHANDLE ? ? ? ? ?InputHandle, ? ? ? ? ? ? ? // 輸入句柄?
? ? ? ? ? ? ? SQLHANDLE ? ? * ? OutputHandlePtr); ? ? // 輸出句柄,即在第一參數指定需要申請的句柄?
? ? ? ? ?第一參數 HandleType 的取值可以為:?
? ? ? ? ? ? 1. SQL_HANDLE_ENV
? ? ? ? ? ? 2. SQL_HANDLE_DBC
? ? ? ? ? ? 3. SQL_HANDLE_STMT
? 注意判斷返回值,看創建是否成功。
SQLConnect 連接數據庫?
? ? ? ? SQLRETURN SQLConnect (
? ? ? ? ? ? ? SQLHDBC ?ConnectionHandle, ? ? ? // DBC 句柄, hdbc
? ? ? ? ? ? ? SQLCHAR * ServerName, ? ? ? ? ? ? ? ?// 為 ODBC 的 DSN 名稱?
? ? ? ? ? ? ? SQLSMALLINT NameLength1, ? ? ?// 指明參數 ServerName 的長度 ( 可以用 SQL_NTS)
? ? ? ? ? ? ? SQLCHAR * UserName, ? ? ? ? ? ? ? ? ? // 數據庫用戶名?
? ? ? ? ? ? ? SQLSMALLINT NameLength2, ? ? ?// 指明參數 UserName 的長度 ( 可以用 SQL_NTS)
? ? ? ? ? ? ? SQLCHAR * Authentication, ? ? ? ? ?// 數據庫用戶密碼?
? ? ? ? ? ? ? SQLSMALLINT NameLength3) ? ? ?// 指明參數 Authentication 的長度 ( 可以用 SQL_NTS)
? ? ?例如:?
? ? ? ? SQLConnect (
? ? ? ? ? ? ? ? hdbc,?
? ? ? ? ? ? ? ? (SQLTCHAR*)szDSN, SQL_NTS,
? ? ? ? ? ? ? ? (SQLTCHAR*)szUserId, SQL_NTS,
? ? ? ? ? ? ? ? (SQLTCHAR*)szPassword, SQL_NTS);
SQLExecDirect 直接執行 SQL 語句?
? ? ? ? SQLRETURN SQLExecDirect (
? ? ? ? ? ? SQLHSTMT ? ? ?StatementHandle, ? ? ?// STMT 句柄?
? ? ? ? ? ? SQLCHAR ? ? * ?StatementText, ? ? ? ? ?// SQL 語句?
? ? ? ? ? ? SQLINTEGER ? ?TextLength) ? ? ? ? ? ? ?// 參數 StatementText 的長度,可以用 SQL_NTS
? ? ? ? ?如果函數執行成功,你將會得到一個結果集,否則將返回錯誤信息。
獲取 SQL 語句執行的結果?
? ? ? ? ?對 于 SQL 查詢語句, ODBC 會返回一個光標,與光標對應的是一個結果集合(可以理解為一個表格)。開發人員利用光標來瀏覽所有的結果,你可以利用 ODBC API 函數移動光標,并且獲取當前光標指向的行的列字段的數值。此外還可以通過光標來對光標當前所指向的數據進行修改,而修改會直接反映到數據庫中。
SQLFetch 移動光標?
? ? ? ? SQLRETURN SQLFetch (SQLHSTMT StatementHandle);
? ? ? ? ?在你調用 SQLExecDirect 執行 SQL 語 句后,你需要遍歷結果集來得到數據。 StatementHandle 是 STMT 句柄,此句柄必須是被執行過。當調用 SQLFetch 函數后,光標會被移動到下一條記錄處,當光標移動到記錄集的最后一條,函數將會返回 SQL_NO_DATA 。
?SQLGetData 得到光標處的某列的值?
? ? ? ? SQLRETURN SQLGetData (
? ? ? ? ? ? SQLHSTMT ? ? ? ? ? ? ?StatementHandlem, ? ? ? ? // STMT 句柄?
? ? ? ? ? ? SQLUSMALLINT ? ? ColumnNumber, ? ? ? ? ? ? ?// 列號,以 1 開始?
? ? ? ? ? ? SQLSMALLINT ? ? ? ?TargetType, ? ? ? ? ? ? ? ? ? ?// 數據緩沖區( TargetValuePtr )的 C 語言類型?
? ? ? ? ? ? SQLPOINTER ? ? ? ? ? ?TargetValuePtr, ? ? ? ? ? ? ?// 數據緩沖區?
? ? ? ? ? ? SQLINTEGER ? ? ? ? ? ?BufferLength, ? ? ? ? ? ? ? ? // 數據緩沖區( TargetValuePtr )的長度?
? ? ? ? ? ? SQLINTEGER ? ? * ? ? StrLen_or_IndPtr); ? ? ? // 返回當前字段得到的字節長度
SQLBindCol 通過列綁定獲得字段數據?
? ? ? ? SQLRETURN SQLBindCol (
? ? ? ? ? ? SQLHSTMT ? ? ? ? ? ? ?StatementHandle, ? ? ? ? ?// STMT 語句?
? ? ? ? ? ? SQLUSMALLINT ? ? ColumnNumber, ? ? ? ? ? ?// 列號,以 1 開始?
? ? ? ? ? ? SQLSMALLINT ? ? ? ?TargetType, ? ? ? ? ? ? ? ? ? // 數據緩沖區( TargetValuePtr )的 C 語言類型?
? ? ? ? ? ? SQLPOINTER ? ? ? ? ? ?TargetValuePtr, ? ? ? ? ? ? // 數據緩沖區?
? ? ? ? ? ? SQLINTEGER ? ? ? ? ? ? BufferLength, ? ? ? ? ? ? ? // 數據緩沖區( TargetValuePtr )的字節長度?
? ? ? ? ? ? SQLINTEGER ? ? * ? ? StrLen_or_IndPtr); ? ? ?// 返回當前字段得到的字節長度?
? ? ? ? ?在從結果集中讀取字段值時可以利用 SQLGetData ,但為了速度可以利用列綁定 (SQLBindCol) 的方式,在每次移動光標后讓 ODBC 將數據傳送到指定的變量中
SQLNumResultCols 得到結果集中列數?
? ? ? ? SQLRETURN SQLNumResultCols (
? ? ? ? ? ? SQLHSTMT ? ? ? ? ? StatementHandle, ? // STMT 句柄?
? ? ? ? ? ? SQLSMALLINT * ColumnCountPtr); ?// 返回的列數
SQLRowCount 執行 SQL 語句后得到影響的行數?
? ? ? ? SQLRETURN SQLRowCount (
? ? ? ? ? ? SQLHSTMT ? ? ? StatementHandle, ?// STMT 句柄?
? ? ? ? ? ? SQLINTEGER * RowCountPtr); ? ? ? // 被影響的數據的行數?
? ? ? ? ?你 可以通過 SQLExecDirect 執行 SQL 語句來插入,修改和刪除數據,在執行插入,修改和刪除的 SQL 語句后就可以通過 SQLRowCount 函數 來得到被影響的數據的行數。
SQLDescribeCol 得到結果集中列的描述?
? ? ? ? SQLRETURN SQLDescribeCol (
? ? ? ? ? ? SQLHSTMT ? ? ? ? ? StatementHandle, ? ? ?// STMT 句柄?
? ? ? ? ? ? SQLSMALLINT ? ?ColumnNumber, ? ? ? ? // 需要得到的列的序號,從 1 開始計算?
? ? ? ? ? ? SQLCHAR ? ? ? ? ?* ColumnName, ? ? ? ? ? ? // 得到列的名稱?
? ? ? ? ? ? SQLSMALLINT ? ?BufferLength, ? ? ? ? ? ? // 指明 ColumnName 參數的最大長度?
? ? ? ? ? ? SQLSMALLINT * NameLengthPtr, ? ? ? ? // 返回列名稱的長度?
? ? ? ? ? ? SQLSMALLINT * DataTypePtr, ? ? ? ? ? ? ?// 返回列的 ODBC 數據類型,見表?
? ? ? ? ? ? SQLUINTEGER ?* ColumnSizePtr, ? ? ? ? ? // 返回列的長度?
? ? ? ? ? ? SQLSMALLINT * DecimalDigitsPtr, ? ? ? // 當列為數字類型時返回小數點后數據的位數?
? ? ? ? ? ? SQLSMALLINT * NullablePtr); ? ? ? ? ? ? ? // 指明該列是否允許空值
SQLSetStmtAttr 設置 ODBC 光標類 型?
? ? ? ? SQLRETURN SQLSetStmtAttr (
? ? ? ? ? ? SQLHSTMT ? ? StatementHandle, ? ? // STMT 句柄?
? ? ? ? ? ? SQLINTEGER ?Attribute, ? ? ? ? ? ? ? ? ? // 指定需要設置的屬性類型?
? ? ? ? ? ? SQLPOINTER ?ValuePtr, ? ? ? ? ? ? ? ? ? ?// 提供的參數值?
? ? ? ? ? ? SQLINTEGER ?StringLength); ? ? ? ? ?// 指定參數的長度,當參數是整數時設置為?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// SQL_IS_INTEGER, 當參數是字符串是設置?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 為字符串長度或者是 SQL_NTS
? ? ? ? ?函數 SQLSetStmtAttr 可以讓我們在 ODBC 中可以使用不同的光標類型
Attribute?
ValuePtr?
作用
SQL_ATTR_ASYNC_ENABLE
整數,取值為:?
SQL_ASYNC_ENABLE_OFF ,?
SQL_ASYNC_ENABLE_ON?
是否使用異步執行功能
SQL_ATTR_QUERY_TIMEOUT
設置一個合法的整數
SQL 語句執行時的超時秒數,設置為 0 表示無超時
SQL_ATTR_CURSOR_TYPE
整數,取值為:?
SQL_CURSOR_FORWARD_ONLY ,?
SQL_CURSOR_STATIC ,?
SQL_CURSOR_DYNAMIC , SQL_CURSOR_KEYSET_DRIVEN
設置光標的類型
? ? ? ? 1. ?向前光標 : SQL_CURSOR_FORWARD_ONLY ,光標僅僅向前滾動。?
? ? ? ? 2. ?靜態光標 : SQL_CURSOR_STATIC ,結果集的數據是靜態的,這就是說明在執行查詢后,返回 的結果集的數據不會再改變,即使是有其他程序更新了數據庫中的記錄,結果集中的記錄也不會發生改變。?
? ? ? ? 3. ?動態光標 : SQL_CURSOR_DYNAMIC ,在光標打開以后,當結果集中的行所對應的數據值發生變 化時,其變化能夠反映到光標所對應的結果集上,這些變化包括:字段的修改,添加,結果集中行的順序變化。但是請注意如果行被刪除則無法在當前結果集中反映 出,因為被刪除的行不再出現在當前的結果集中。動態光標所對應的結果集在數據發生變化時會被重建。例如,假設動態光標已獲取到了兩行,然后,另一應用程序 更新了這兩行中的一行,并刪除了另一行,如果動態游標再試圖獲取那些行,它將不能檢測已刪除的行(因為當前結果集中只有一行,但是不要利用這個辦法去檢測 被刪除的行,因為出現這種情況還可能是因為行的數據被改變后不能再滿足查詢條件),而是返回已更新行的新值。?
? ? ? ? 4. ?鍵集光標 : SQL_CURSOR_KEYSET_DRIVEN ,和上面的動態光標所不同的是鍵集光標能夠 檢測到行的刪除和修改,但是無法檢測到行的添加和結果集順序變化。因為在光標創建時就創建了整個結果集,結果集合中記錄和順序已經被固定,這一點和靜態光 標一樣。所以鍵集光標可以說是一種介于靜態光標和動態光標之間的光標類型。?
? ? ? ? ?如: SQLSetStmtAttr (hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER) SQL_CURSOR_KEYSET_DRIVEN, 0);
SQLFetchScroll 利用可滾動光標進行 查詢?
? ? ? ? SQLRETURN SQLFetchScroll (
? ? ? ? ? ? SQLHSTMT ? ? ? ? ?StatementHandle, ? ? // STMT 語句?
? ? ? ? ? ? SQLSMALLINT ? FetchOrientation, ? ? // 光標滾動的方式,見下表?
? ? ? ? ? ? SQLINTEGER ? ? ? FetchOffset); ? ? ? ? ? ? // 光標滾動的位置?
? ? ? ? ?前面介紹的 SQLFetch 函數只能夠讓光標向前移動,但在很多時候我們需要光標能夠前后移動。我們需要利用另 一個函數 SQLFetchScroll ,但是再這之前請利用 SQLSetStmtAttr 正確設置光標類型。
FetchOrientation
含 ? ? 義
SQL_FETCH_NEXT?
滾動到下一行,這時候調用相當與 SQLFetch ,參數 FetchOffset 將被忽略 ( 用 0 值 )
SQL_FETCH_PRIOR
滾動到上一行,參數 FetchOffset 將被忽略 ( 用 0 值 )
SQL_FETCH_FIRST?
滾動到第一行,參數 FetchOffset 將被忽略 ( 用 0 值 )
SQL_FETCH_LAST?
滾動到最后一行,參數 FetchOffset 將被忽略 ( 用 0 值 )
SQL_FETCH_ABSOLUTE?
滾動到參數 FetchOffset 指定的絕對行
SQL_FETCH_RELATIVE?
由當前位置滾動到參數 FetchOffset 指定的相對行, FetchOffset 大于 0 表示向前滾 動, FetchOffset 小于 0 表示向后滾動
用法示例會在接下來的文章里給出。
========
ODBC API Reference
https://msdn.microsoft.com/en-us/library/ms714562(VS.85).aspx?
The topics in this section describe each ODBC function in alphabetical order. Each function is defined as a C programming language function. Descriptions include the following:
Purpose
ODBC version
Standard CLI conformance level
Syntax
Arguments
Return values
Diagnostics
Comments about usage and implementation
Code example
References to related functions
The standard CLI conformance level can be one of the following: ISO 92, Open Group, ODBC, or Deprecated. A function tagged as ISO 92–conformant also appears in Open Group version 1, because Open Group is a pure superset of ISO 92. A function tagged as Open Group-compliant also appears in ODBC 3.x, because ODBC 3.x is a pure superset of Open Group version 1. A function tagged as ODBC-compliant appears in neither standard. A function tagged as deprecated has been deprecated in ODBC 3.x.
Handling of diagnostic information is described in the SQLGetDiagField function description. The text associated with SQLSTATE values is included to provide a description of the condition but is not intended to prescribe specific text.
System_CAPS_noteNote
For driver-specific information about ODBC functions, see the section for the driver.
This section contains topics for the following functions:
SQLAllocConnect Function
SQLAllocEnv Function
SQLAllocHandle Function
SQLAllocStmt Function
SQLBindCol Function
SQLBindParameter Function
SQLBrowseConnect Function
SQLBulkOperations Function
SQLCancel Function
SQLCancelHandle Function
SQLCloseCursor Function
SQLColAttribute Function
SQLColAttributes Function
SQLColumnPrivileges Function
SQLColumns Function
SQLCompleteAsync Function
SQLConnect Function
SQLCopyDesc Function
SQLDataSources Function
SQLDescribeCol Function
SQLDescribeParam Function
SQLDisconnect Function
SQLDriverConnect Function
SQLDrivers Function
SQLEndTran Function
SQLError Function
SQLExecDirect Function
SQLExecute Function
SQLExtendedFetch Function
SQLFetch Function
SQLFetchScroll Function
SQLForeignKeys Function
SQLFreeConnect Function
SQLFreeEnv Function
SQLFreeHandle Function
SQLFreeStmt Function
SQLGetConnectAttr Function
SQLGetConnectOption Function
SQLGetCursorName Function
SQLGetData Function
SQLGetDescField Function
SQLGetDescRec Function
SQLGetDiagField Function
SQLGetDiagRec Function
SQLGetEnvAttr Function
SQLGetFunctions Function
SQLGetInfo Function
SQLGetStmtAttr Function
SQLGetStmtOption Function
SQLGetTypeInfo Function
SQLMoreResults Function
SQLNativeSql Function
SQLNumParams Function
SQLNumResultCols Function
SQLParamData Function
SQLParamOptions Function
SQLPrepare Function
SQLPrimaryKeys Function
SQLProcedureColumns Function
SQLProcedures Function
SQLPutData Function
SQLRowCount Function
SQLSetConnectAttr Function
SQLSetConnectOption Function
SQLSetCursorName Function
SQLSetDescField Function
SQLSetDescRec Function
SQLSetEnvAttr Function
SQLSetParam Function
SQLSetPos Function
SQLSetScrollOptions Function
SQLSetStmtAttr Function
SQLSetStmtOption Function
SQLSpecialColumns Function
SQLStatistics Function
SQLTablePrivileges Function
SQLTables Function
SQLTransact Function
========
ODBC API簡介
http://www.cnblogs.com/bigbigtree/p/4286695.html1. ? ? ?數據類型:
通過SQLGetTypeInfo函數來獲取ODBC 3.0支持的數據類型信息。由SQLGetTypeInfo返回的數據類型是數據源所支持的數據類型。
SQLRETURN SQLGetTypeInfo(
? ? ?SQLHSTMT ? ? ?StatementHandle,
? ? ?SQLSMALLINT ? DataType);
其中DataType類型為SQL Data Types的一種,具體參見
https://msdn.microsoft.com/en-us/library/ms710150(v=vs.85).aspx
同時需要了解SQL數據類型與C數據類型的對應關系:
C type identifier
ODBC C typedef
C type
SQL_C_CHAR
SQLCHAR *
unsigned char *
SQL_C_SSHORT[j]
SQLSMALLINT
short int
SQL_C_USHORT[j]
SQLUSMALLINT
unsigned short int
SQL_C_SLONG[j]
SQLINTEGER
long int
SQL_C_ULONG[j]
SQLUINTEGER
unsigned long int
SQL_C_FLOAT
SQLREAL
float
SQL_C_DOUBLE
SQLDOUBLE, SQLFLOAT
double
SQL_C_BOOKMARK[i]
BOOKMARK
unsigned long int[d]
SQL_C_VARBOOKMARK
SQLCHAR *
unsigned char *
SQL_C_TYPE_DATE[c]
SQL_DATE_STRUCT
struct tagDATE_STRUCT {
? ?SQLSMALLINT year;
? ?SQLUSMALLINT month;
? ?SQLUSMALLINT day;?
} DATE_STRUCT;[a]
SQL_C_TYPE_TIME[c]
SQL_TIME_STRUCT
struct tagTIME_STRUCT {
? ?SQLUSMALLINT hour;
? ?SQLUSMALLINT minute;
? ?SQLUSMALLINT second;
} TIME_STRUCT;[a]
SQL_C_TYPE_TIMESTAMP[c]
SQL_TIMESTAMP_STRUCT
struct tagTIMESTAMP_STRUCT {
? ?SQLSMALLINT year;
? ?SQLUSMALLINT month;
? ?SQLUSMALLINT day;
? ?SQLUSMALLINT hour;
? ?SQLUSMALLINT minute;
? ?SQLUSMALLINT second;
? ?SQLUINTEGER fraction;[b]
} TIMESTAMP_STRUCT;[a]
2. ? ? ?常用接口:
連接到數據源
下面的函數用于連接到數據源:
(1)SQLAllocHandle:分配環境、連接、語句或者描述符句柄。
(2)SQLConnect:建立與驅動程序或者數據源的連接。訪問數據源的連接句柄包含了包括狀態、事務申明和錯誤信息的所有連接信息。
(3)SQLDriverConnect:與SQLConnect相似,用來連接到驅動程序或者數據源。但它比SQLConnect支持數據源更多的連接信息;
(4)SQLBrowseConnect:支持一種交互方法來檢索或者列出連接數據源所需要的屬性和屬性值。
獲取驅動程序和數據源信息
下面的函數用來獲取驅動程序和數據源信息:
(1)SQLDataSources:能夠被調用多次來獲取應用程序使用的所有數據源的名字。
(2)SQLDrivers:返回所有安裝過的驅動程序清單,包括對它們的描述以及屬性關鍵字。
(3)SQLGetInfo:返回連接的驅動程序和數據源的元信息。
(4)SQLGetFunctions:返回指定的驅動程序是否支持某個特定函數的信息。
(5)SQLGetTypeInfo:返回指定的數據源支持的數據類型的信息。
設置或者獲取驅動程序屬性
下面的函數用來設置或者獲取驅動程序屬性:
(1)SQLSetConnectAttr:設置連接屬性值。
(2)SQLGetConnectAttr:返回連接屬性值。
(3)SQLSetEnvAttr:設置環境屬性值。
(4)SQLGetEnvAttr:返回環境屬性值。
(5)SQLSetStmtAttr:設置語句屬性值。
(6)SQLGetStmtAttr:返回語句屬性值。
設置或者獲取描述符字段
下面的函數用來設置或者獲取描述符字段:
(1)SQLGetDescField:返回單個描述符字段的值。
(2)SQLGetDescRec:返回當前描述符記錄的多個字段的值。
(3)SQLSetDescField:設置單個描述符字段的值。
(4)SQLSetDescRec:設置描述符記錄的多個字段。
準備SQL語句
下面的函數用來準備SQL語句:
(1)SQLPrepare:準備要執行的SQL語句。
(2)SQLBindParameter:在SQL語句中分配參數的緩沖區。
(3)SQLGetCursorName:返回與語句句柄相關的游標名稱。
(4)SQLSetCursorName:設置與語句句柄相關的游標名稱。
(5)SQLSetScrollOptions:設置控制游標行為的選項,在ODBC 3.0 中被SQLGetInfo和SQLSetStmtAttr接口替代
提交SQL請求
下面的函數用來提交SQL請求:
(1)SQLExecute:與SQLPrepare共同使用,執行準備好的SQL語句。
(2)SQLExecDirect:執行一條SQL語句。
(3)SQLNativeSql:返回驅動程序對一條SQL語句的翻譯,并不執行sql語句。
(4)SQLDescribeParam:返回對SQL語句中指定參數的描述。
(5)SQLNumParams:返回SQL語句中參數的個數。
(6)SQLParamData:與SQLPutData聯合使用在運行時給參數賦值。
(7)SQLPutData:在SQL語句運行時給部分或者全部參數賦值。
檢索結果集及其相關信息
下面的函數用來檢索結果集及其相關信息:
(1)SQLRowCount:返回INSERT、UPDATE或者DELETE等語句影響的行數。
(2)SQLNumResultCols:返回結果集中列的數目。
(3)SQLDescribeCol:返回結果集中列的描述符記錄。
(4)SQLColAttribute:返回結果集中列的屬性。
(5)SQLBindCol:為結果集中的列分配緩沖區。
(6)SQLFetch:在結果集中檢索下一行元組。
(7)SQLFetchScroll:返回指定的結果行。
(8)SQLGetData:返回結果集中當前行某一列的值。
(9)SQLSetPos:在取到的數據集中設置游標的位置。這個記錄集中的數據能夠刷新、更新或者刪除。
(10)SQLBulkOperations:執行塊插入和塊書簽操作,其中包括根據書簽更新、刪除或者取數據。
(11)SQLMoreResults:確定是否能夠獲得更多的結果集,如果能就執行下一個結果集的初始化操作。
(12)SQLGetDiagField:返回一個字段值或者一個診斷數據記錄。
(13)SQLGetDiagRec:返回多個字段值或者一個診斷數據記錄。
取得數據源系統表的信息
下面的函數用來取得數據源系統表的信息:
(1)SQLColumnPrivileges:返回一個關于指定表的列的列表以及相關的權限信息。
(2)SQLColumns:返回指定表的列信息的列表。
(3)SQLForeignKeys:返回指定表的外鍵信息的列表。
(4)SQLPrimaryKeys:返回指定表的主鍵信息的列表。
(5)SQLProcedureColumns:返回指定存儲過程的參數信息的列表。
(6)SQLProcedures:返回指定數據源的存儲過程信息的列表。
(7)SQLSpecialColumns:返回唯一確定某一行的列的信息,或者當某一事務修改一行的時候自動更新各列的信息。
(8)SQLStatistics:返回一個單表的相關統計信息和索引信息。
(9)SQLTablePrivileges:返回相關各表的名稱以及相關的權限信息。
(10)SQLTables:返回指定數據源中表信息。
終止語句執行
下面的函數用來終止語句執行:
(1)SQLFreeStmt:終止語句執行,關閉所有相關的游標,放棄沒有提交的結果,選擇釋放與指定語句句柄相關的資源。
(2)SQLCloseCursor:關閉一個打開的游標,放棄沒有提交的結果。
(3)SQLCancel:放棄執行一條SQL語句。
(4)SQLEndTran:提交或者回滾事務。
中斷連接
下面的函數處理中斷連接的任務:
(1) ? ?SQLDisconnect:關閉指定連接。
(2) ? ?SQLFreeHandle:釋放環境、連接、語句或者描述符句柄。
參考:
https://msdn.microsoft.com/en-us/library/ms714562(v=vs.85).aspx
http://www.cnblogs.com/huzhongzhong/archive/2011/07/12/2104209.html
========
ODBC API數據庫編程
http://blog.chinaunix.net/uid-1857870-id-2823762.html
一、動態加載數據源
1、通過修改注冊表加載數據源:
·用戶數據源:HKEY_CURRENT_USER\SOFTWARE\ODBC\ODBC.INI
·系統數據源:HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.INI
對于不同類型的數據源,注冊表的修改也不同,但基本上要修改兩個地方,一個是在ODBC.INI子鍵下建立一個與數據源描述名同名的子鍵,并在該子鍵下建立與數據源配置相關的項;另一個是在\ODBC.INI\ODBC Data Sources子鍵下建立一個新項以便告訴驅動程序管理器ODBC數據源的類型。
2、通過ODBC API加載:Windows系統子目錄下的動態鏈接庫Odbcinst.dll提供了一個可以動態增加、修改和刪除數據源的函數SQLConfigDataSource,由于VC的默認庫文件中不包含此函數,因此使用前需將Odbcinst.h文件包含在工程的頭文件中,在工程的setting屬性框Link頁的Object/library module編輯框中增加Odbc32.lib,同時保證系統目錄system32下有文件Odbccp32.dll。
3、文件數據源的連接:除了ODBC管理器,還可以通過SQLDriverConnect來添加文件數據源。
二、ODBC ?API編程
如果一個ODBC API函數執行成功,則返回SQL_SUCCESS或SQL_SUCCESS_WITH_INFO,SQL_SUCCESS指示可通過診斷記錄獲取有關操作的詳細信息,SQL_SUCCESS_WITH_INFO指示應用程序執行結果帶有警告信息,可通過診斷記錄獲取詳細信息。如果函數調用失敗,返回碼為SQL_ERROR。
一般,編寫ODBC程序主要有一下幾個步驟:
1、分配環境句柄:聲明一個SQLHENV的變量,調用函數SQLAllocHandle。
設置環境屬性:完成環境分配后,用函數SQLSetEnvAttr設置環境屬性,注冊ODBC版本號。
釋放環境句柄:完成數據訪問任務時,應調用SQLFreeHandle釋放前面分配的環境。
2、分配連接句柄:聲明一個SQLHDBC類型的變量,調用SQLAllocHandle函數分配句柄。
設置連接屬性:所有連接屬性都可通過函數SQLSetConnectAttr設置,調用函數SQLGetConnectAttr可獲取這些連接屬性的當前設置值。
3、 ? 連接數據源:對于不同的程序和用戶接口,可以用不同的函數建立連接
SQLConnect:該函數只要提供數據源名稱、用戶ID和口令,就可以進行連接了。
SQLDriverConnect:該函數用一個連接字符串建立至數據源的連接,它可以讓用戶輸入必要的連接信息,使用系統中還沒定義的數據源。
SQLBrowseConnect:該函數支持以一種迭代的方式獲取到數據源的連接,直到最后建立連接,它基于客戶機/服務器體系結構,因此本地數據庫不支持該函數。
4、 ? 準備并執行SQL語句
A、 ?分配語句句柄:語句句柄是通過調用SQLAllocHandle函數分配的。
函數SQLGetStmrrAttr和SQLSetStmrrAttr用來獲取和設置一個語句句柄的選項,使用完,調用SQLFreeHandle釋放該句柄。
B、 ?執行SQL語句
SQLExecDirect:該函數直接執行SQL語句,對于只執行一次的SQL語句來說,該函數是執行最快的方法。
SQLPrepare和SQLExecute:對于需要多次執行的SQL語句來說,可先調用SQLPrepare準備SQL語句的執行,用SQLExecute執行準備好的語句。
C、 ?使用參數:使用參數可以使一條SQL語句多次執行,得到不同的結果。
函數SQLBindParameter負責為參數定義變量,將一段SQL語句中的一個參數標識符("?")捆綁在一起,實現參數值的傳遞。
5、 ? 獲取記錄集
A、 ? 綁定列:首先必須分配與記錄集中字段相對應的變量,然后通過函數SQLBindCol將記錄字段同程序變量綁定在一起,對于長記錄字段,可以通過調用函數SQLGetData直接取回數據。
綁定字段可以根據自己的需要全部綁定,也可以綁定其中的某幾個字段。
通過調用函數SQLBindCol將變量地址值賦為NULL,可以結束對一個記錄字段的綁定,通過調用函數SQLFreeStmt,將其中選項設為SQL_UNBIND,或者直接釋放句柄,都會結束所有記錄字段的綁定。
B、SQLFetch:該函數用于將記錄集的下一行變成當前行,并把所有捆綁過的數據字段的數據拷貝到相應的緩沖區。
C、 光標:應用程序獲取數據是通過光標(Cursor)來實現的,在ODBC中,主要有3種類型的光標:單向光標、可滾動光標和塊光標。
有些應用程序不支持可滾動光標和塊光標,ODBC SDK提供了一個光標庫(ODBCCR32.DLL),在應用程序中可通過設置連接屬性(SQL_STTR_ODBC_CURSOR)激活光標庫。
6、 ?記錄的添加、刪除和更新:數據源數據更新可通過3種方式:通過SQLExecDirect函數使用相應的SQL語句;調用SQLSetPos函數實現記錄集定義更新;調用SQLBulkOperations函數實現數據更新。
第一種方式適用于任何ODBC數據源,后兩種方式有的數據源不支持,可調用SQLGetInfo確定數據源。
SQLBulkOperations:該函數操作基于當前行集,調用前,須先調用SQLFetch或SQLFetchScroll獲取。
函數調用后,塊光標的位置變為未定義狀況,因此,應該先調用函數SQLFetchScroll設定光標位置。
7、錯誤處理:每個ODBC API函數都能產生一系列反映操作信息的診斷記錄,可以用SQLGetDiagField函數獲取診斷記錄中特定的域,另外,可以使用SQLGetDiagRec獲取診斷記錄中一些常用的域。
8、事務處理:事務提交有兩種方式:自動提交模式和手動提交模式。應用程序可通過調用函數SQLSetConnectAttr設定連接屬性SQL_ATTR_AUTOCOMMIT,自動提交模式是默認的連接屬性設置,對于所有的ODBC驅動程序都能適應這種模式下,所有語句都是作為一個獨立的事務進行處理的。
手動提交模式把一組SQL語句放入一個事務中,程序必須調用函數SQLEenTran明確地終止一個事務。若使用多個激活的事務,就必須建立多個連接,每一個連接包含一個事務。
9、斷開數據連接并釋放環境句柄:完成數據庫操作后,可調用SQLDisconnect函數關閉同數據庫的連接。
========
使用ODBC API訪問數據庫
http://blog.csdn.net/stavck/article/details/598888最近工作需要,研究了一下ODBC API,并對它進行了簡單的封裝,下面介紹一下:
假設有一個數據庫表為:
CREATE TABLE `testTable` ( ? ? ? ? ? ? ?
? ? ? ? ? ? ?`TestTEXT` text, ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ?`TestBigInt` bigint(20) default NULL ?
? ? ? ? ? ?)
使用我的封裝類的方法如下:
#include "query.h" #include <iostream> using namespace std;int main(int argc, char* argv[]) {int nConnectTimeOut = 5;int nLoginTimeOut = 3;int nQueryTimeOut = 3;CConnection * pCon = NULL;CDBAccess dbAccess;//初始化if(!dbAccess.Init(NULL, &pCon, nConnectTimeOut, nLoginTimeOut, nQueryTimeOut)){cout << dbAccess.GetLastError()<<endl;return -1;}//連接數據庫if(!dbAccess.Connect("DNSName", "UserName", "Password")){cout << dbAccess.GetLastError()<<endl;return -1; }//oK,連接成功了,可以進行你的sql操作了。char pszSQL[256];memset(pszSQL, 0x00, sizeof(pszSQL)); sprintf((char*)pszSQL, "SELECT * FROM %s", "testTable");if(dbAccess.ExecuteSQL(pszSQL)){ //執行查詢語句完畢,開始取數據if(dbAccess.FetchFirst()){do {long nRetLen = 0;char pszTest[300];memset((char*)pszTest, 0x00, sizeof(pszTest));if(!dbAccess.GetData("TestTEXT", pszTest, sizeof(pszTest), &nRetLen)){cout << "無法取到TestTEXT字段信息!"<<endl;break;}cout << "get TestTEXT data:" << pszTest << endl;_INT64 lTestBigInt = 0;if(!dbAccess.GetData("TestBigInt", &(lTestBigInt), sizeof(_INT64), &nRetLen, SQL_C_SBIGINT)){cout << "無法取到TestBigInt字段信息!"<<endl;break;} char pszTemp[30];memset(pszTemp, 0x00, 24);_i64toa(lTestBigInt, (char*)pszTemp, 10); cout << "get TestBigInt data:" << pszTemp <<endl; } while(dbAccess.Fetch());}else{cout <<dbAccess.GetLastError()<<endl;}}//下面關閉句柄,否則會出現訪問非法,非常重要!dbAccess.CloseStmt();//斷開到數據庫的連接if(!dbAccess.Disconnect()){cout <<"error disconnect" <<dbAccess.GetLastError()<<endl;return -1;}dbAccess.Close();return 0; }
下面是Query.h文件內容:
// Query.h: interface for the CQuery class.
//
//
#if !defined(AFX_QUERY_H__CAEAF203_40C0_4C32_BA76_9A4B0245984B__INCLUDED_)
#define AFX_QUERY_H__CAEAF203_40C0_4C32_BA76_9A4B0245984B__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#ifdef WIN32
?#include "windows.h"
#endif
//ODBC API
#include <sql.h>?
#include <sqlext.h>
#include <odbcinst.h>
//--
#pragma comment(lib,"odbc32.lib")
#pragma comment(lib,"OdbcCP32.Lib")
#ifdef WIN32
typedef __int64 _INT64;
#endif
#ifdef GCC
typedef long long int _INT64;
#endif
//--
/* SQLEndTran() options */
#define SQL_COMMIT ? ? ? ? ?0 //提交
#define SQL_ROLLBACK ? ? ? ?1 //回滾
//
//
//錯誤代碼定義
//錯誤代碼定義規則:ERROR_類名_函數名_函數內編號
//暫定四位錯誤編號
#define BASE_ERROR ? ? ?100000
//數據庫模塊錯誤代碼
#define DATABASE_BASE_ERROR ? ?BASE_ERROR + 0
//CConnection類中的錯誤代碼
#define CON_BASE_ERROR ? DATABASE_BASE_ERROR + 0
#define ERROR_CON_INIT_1 ?CON_BASE_ERROR + 1?
#define ERROR_CON_INIT_2 ?CON_BASE_ERROR + 2?
#define ERROR_CON_INIT_3 ?CON_BASE_ERROR + 3?
#define ERROR_CON_CONNECT_1 ?CON_BASE_ERROR + 4?
#define ERROR_CON_CONNECT_2 ?CON_BASE_ERROR + 5?
#define ERROR_CON_DISCONNECT_1 CON_BASE_ERROR + 6
#define ERROR_CON_DISCONNECT_2 CON_BASE_ERROR + 7
#define ERROR_CON_BEGINTRAN_1 CON_BASE_ERROR + 8
#define ERROR_CON_ENDTRAN_1 ?CON_BASE_ERROR + 9
#define ERROR_CON_ENDTRAN_2 ?CON_BASE_ERROR + 10
#define ERROR_CON_SETTIMEOUT_1 CON_BASE_ERROR + 11
#define ERROR_CON_SETTIMEOUT_2 CON_BASE_ERROR + 12
#define ERROR_CON_SETTIMEOUT_3 CON_BASE_ERROR + 13
#define ERROR_CON_SETTIMEOUT_4 CON_BASE_ERROR + 14
#define ERROR_CON_CONNECT_3 ?CON_BASE_ERROR + 15
#define ERROR_CON_DISCONNECT_3 CON_BASE_ERROR + 16
#define ERROR_CON_BEGINTRAN_2 CON_BASE_ERROR + 17
#define ERROR_CON_ENDTRAN_3 ?CON_BASE_ERROR + 18
#define ERROR_CON_SETTIMEOUT_5 CON_BASE_ERROR + 19
#define ERROR_CON_CONNECT_4 ?CON_BASE_ERROR + 20?
#define ERROR_CON_BEGINTRAN_3 CON_BASE_ERROR + 21
#define ERROR_CON_ENDTRAN_4 ?CON_BASE_ERROR + 22
#define ERROR_CON_ISCONNECT_1 CON_BASE_ERROR + 23
#define ERROR_CON_ISCONNECT_2 CON_BASE_ERROR + 24
//CQuery類中的錯誤代碼
#define QUERY_BASE_ERROR ?DATABASE_BASE_ERROR + 100
#define ERROR_QUERY_INIT_1 ? QUERY_BASE_ERROR + 1
#define ERROR_QUERY_INIT_2 ? QUERY_BASE_ERROR + 2
#define ERROR_QUERY_INIT_3 ? QUERY_BASE_ERROR + 3
#define ERROR_QUERY_GETCOLCOUNT_1 QUERY_BASE_ERROR + 4
#define ERROR_QUERY_GETCOLCOUNT_2 QUERY_BASE_ERROR + 5
#define ERROR_QUERY_GETCROWCOUNT_1 QUERY_BASE_ERROR + 6
#define ERROR_QUERY_GETCROWCOUNT_2 QUERY_BASE_ERROR + 7
#define ERROR_QUERY_EXECSQL_1 ?QUERY_BASE_ERROR + 8
#define ERROR_QUERY_EXECSQL_2 ?QUERY_BASE_ERROR + 9
#define ERROR_QUERY_FETCH_1 ? QUERY_BASE_ERROR + 10
#define ERROR_QUERY_FETCH_2 ? QUERY_BASE_ERROR + 11
#define ERROR_QUERY_FETCHNEXT_1 ?QUERY_BASE_ERROR + 12
#define ERROR_QUERY_FETCHNEXT_2 ?QUERY_BASE_ERROR + 13
#define ERROR_QUERY_FETCHPRE_1 ?QUERY_BASE_ERROR + 14
#define ERROR_QUERY_FETCHPRE_2 ?QUERY_BASE_ERROR + 15
#define ERROR_QUERY_FETCHFIRST_1 QUERY_BASE_ERROR + 16
#define ERROR_QUERY_FETCHFIRST_2 QUERY_BASE_ERROR + 17
#define ERROR_QUERY_FETCHLAST_1 ?QUERY_BASE_ERROR + 18
#define ERROR_QUERY_FETCHLAST_2 ?QUERY_BASE_ERROR + 19
#define ERROR_QUERY_FETCHROW_1 ?QUERY_BASE_ERROR + 20
#define ERROR_QUERY_FETCHROW_2 ?QUERY_BASE_ERROR + 21
#define ERROR_QUERY_CANCEL_1 ?QUERY_BASE_ERROR + 22
#define ERROR_QUERY_CANCEL_2 ?QUERY_BASE_ERROR + 23
#define ERROR_QUERY_GETDATA_1 ?QUERY_BASE_ERROR + 24
#define ERROR_QUERY_GETDATA_2 ?QUERY_BASE_ERROR + 25
#define ERROR_QUERY_GETDATA_3 ?QUERY_BASE_ERROR + 26
#define ERROR_QUERY_GETDATA_4 ?QUERY_BASE_ERROR + 27
#define ERROR_QUERY_GETCOLBYNAME_1 QUERY_BASE_ERROR + 28
#define ERROR_QUERY_GETCOLNAME_1 QUERY_BASE_ERROR + 29
#define ERROR_QUERY_GETCOLNAME_2 QUERY_BASE_ERROR + 30
#define ERROR_QUERY_INIT_4 ? QUERY_BASE_ERROR + 31
#define ERROR_QUERY_GETCOLCOUNT_3 QUERY_BASE_ERROR + 32
#define ERROR_QUERY_GETCROWCOUNT_3 QUERY_BASE_ERROR + 33
#define ERROR_QUERY_EXECSQL_3 ?QUERY_BASE_ERROR + 34
#define ERROR_QUERY_FETCH_3 ? QUERY_BASE_ERROR + 35
#define ERROR_QUERY_FETCHNEXT_3 ?QUERY_BASE_ERROR + 36
#define ERROR_QUERY_FETCHPRE_3 ?QUERY_BASE_ERROR + 37
#define ERROR_QUERY_FETCHFIRST_3 QUERY_BASE_ERROR + 38
#define ERROR_QUERY_FETCHLAST_3 ?QUERY_BASE_ERROR + 39
#define ERROR_QUERY_FETCHROW_3 ?QUERY_BASE_ERROR + 40
#define ERROR_QUERY_CANCEL_3 ?QUERY_BASE_ERROR + 41
#define ERROR_QUERY_GETDATA_5 ?QUERY_BASE_ERROR + 42
#define ERROR_QUERY_GETCOLNAME_3 QUERY_BASE_ERROR + 43
#define ERROR_QUERY_INSERTARRAY_1 QUERY_BASE_ERROR + 44
#define ERROR_QUERY_INSERTARRAY_2 QUERY_BASE_ERROR + 45
#define ERROR_QUERY_INSERTARRAY_3 QUERY_BASE_ERROR + 46
#define ERROR_QUERY_INSERTARRAY_4 QUERY_BASE_ERROR + 47
#define ERROR_QUERY_INSERTARRAY_5 QUERY_BASE_ERROR + 48
#define ERROR_QUERY_INSERTARRAY_6 QUERY_BASE_ERROR + 49
#define ERROR_QUERY_INIT_5 ? QUERY_BASE_ERROR + 50
//CDBAccess類中的錯誤代碼
#define DBACCESS_BASE_ERROR ?DATABASE_BASE_ERROR + 200
#define ERROR_DBACCESS_INIT_1 ? DBACCESS_BASE_ERROR + 1
#define ERROR_DBACCESS_INIT_2 ? DBACCESS_BASE_ERROR + 2
#define ERROR_DBACCESS_CONNECT_1 ?DBACCESS_BASE_ERROR + 3
#define ERROR_DBACCESS_CONNECT_2 ?DBACCESS_BASE_ERROR + 4
#define ERROR_DBACCESS_DISCONNECT_1 ?DBACCESS_BASE_ERROR + 5
#define ERROR_DBACCESS_DISCONNECT_2 ?DBACCESS_BASE_ERROR + 6
#define ERROR_DBACCESS_RECONNECT_1 ?DBACCESS_BASE_ERROR + 7
#define ERROR_DBACCESS_EXECUTESQL_1 ?DBACCESS_BASE_ERROR + 8
#define ERROR_DBACCESS_EXECUTESQL_2 ?DBACCESS_BASE_ERROR + 9
#define ERROR_DBACCESS_EXECUTESQL_3 ?DBACCESS_BASE_ERROR + 10
#define ERROR_DBACCESS_EXECUTESQL_4 ?DBACCESS_BASE_ERROR + 11
#define ERROR_DBACCESS_EXECUTESQL_5 ?DBACCESS_BASE_ERROR + 12
#define ERROR_DBACCESS_INIT_3 ? DBACCESS_BASE_ERROR + 13
#define ERROR_DBACCESS_INIT_4 ? DBACCESS_BASE_ERROR + 14
#define ERROR_DBACCESS_GETTASKDATA_1 DBACCESS_BASE_ERROR + 15
#define ERROR_DBACCESS_GETTASKDATA_2 DBACCESS_BASE_ERROR + 16
#define ERROR_DBACCESS_GETTASKDATA_3 DBACCESS_BASE_ERROR + 17
#define ERROR_DBACCESS_GETTASKDATA_4 DBACCESS_BASE_ERROR + 18
#define ERROR_DBACCESS_GETTASKDATA_5 DBACCESS_BASE_ERROR + 19
#define ERROR_DBACCESS_GETTASKDATA_6 DBACCESS_BASE_ERROR + 20
#define ERROR_DBACCESS_GETTASKDATA_7 DBACCESS_BASE_ERROR + 21
#define ERROR_DBACCESS_GETTASKDATA_8 DBACCESS_BASE_ERROR + 22
#define ERROR_DBACCESS_GETTASKDATA_9 DBACCESS_BASE_ERROR + 23
#define ERROR_DBACCESS_GETTASKDATA_10 DBACCESS_BASE_ERROR + 24
#define ERROR_DBACCESS_GETTASKDATA_11 DBACCESS_BASE_ERROR + 25
#define ERROR_DBACCESS_GETTASKDATA_12 DBACCESS_BASE_ERROR + 26
#define ERROR_DBACCESS_GETTASKDATA_13 DBACCESS_BASE_ERROR + 27
//
class CConnection
{
public:
?CConnection();
?virtual ~CConnection();
public:
?//提交事務處理.參數: SQL_COMMIT為提交,SQL_ROLLBACK為回滾.返回值:是否提交成功
?bool EndTran(short nAction);
?//開始事務處理.返回值:是否成功
?bool BeginTran();
?//斷開連接. 斷開連接前,要確定是否提交了事務。返回值:是否成功
?bool Disconnect();
?//連接到DNS,參數:DNS名,用戶名,口令, 返回值:是否連接成功
?bool Connect(const char* pszDNS, const char* pszUser, const char* pszPwd);
?//取錯誤信息,返回值:錯誤信息
?const char* GetLastError(){return (char*)m_pszLastError;}
?//是否連接到數據庫,返回值:是否連接到數據庫
?bool IsConnect();
?//設置超時,參數分別為連接超時,登錄超時.如果為-1,表示該參數不起作用,返回值:是否設置成功
?bool SetTimeOut(int nConnect = -1, int nLogin ?= -1);
?//是否正在進行事務處理,返回值:是否進行事務處理.
?bool IsInTran(){return m_bTraning;}
?//重新連接數據庫,返回值:是否重連成功
?bool ReConnect();?
?//初始化,參數:連接超時,登錄超時,返回值:是否初始化成功.
?bool Init(int nConnectTimeOut = -1, int nLoginTimeOut = -1);
?
?//可以方便的為CQuery類使用。
?operator SQLHANDLE(){return m_hCon;}
?
private:
?//設置錯誤信息,參數:錯誤信息,錯誤代碼
?void SetErrorInfo(const char* pszError, long lErrorCode);?
?//取得錯誤信息,參數:句柄類型,出錯的句柄,錯誤代碼,返回值:是否成功
?bool GetErrorInfo(SQLSMALLINT nHandleType, SQLHANDLE nHandle, long lErrorCode);
?//是否成功執行了,參數:需要判斷的返回值,句柄類型,需要判斷的句柄,錯誤代碼,返回值:是否成功
?bool IsSuccess(SQLRETURN nRet, SQLSMALLINT nHandleType, SQLHANDLE nHandle, long lErrorCode);
public:
?SQLHANDLE m_hCon; //數據庫連接句柄?
protected:
?int m_nLoginTimeOut;
?int m_nConnectTimeOut;
?SQLHANDLE m_hEnv; //環境句柄
?SQLRETURN m_nRet; //返回值
protected:
?bool m_bIsConnect; //是否連接數據庫
?SQLCHAR m_pszLastError[SQL_MAX_MESSAGE_LENGTH+100]; //錯誤信息
?bool m_bTraning; //事務處理是否進行中
?bool m_bInit; //初始化是否正常
?char m_pszDNS[255]; ? //ODBC DNS名
?char m_pszUser[255]; ?//ODBC 用戶名
?char m_pszPwd[255]; ? //ODBC 用戶口令
};
class CQuery ?
{
public:
?CQuery(CConnection** ppDBCon, int nQueryTimeOut = 3);
?virtual ~CQuery();
?//取得記錄集的列數.返回值:列數
?unsigned short GetColumnCount();
?//取得影響行數,返回值:影響行數
?long GetChangedRowCount(void);
?//執行指定的sql語句,參數:要執行的sql語句,返回值:是否成功
?bool ExecuteSQL(const char* pszSQL);
?//下一個記錄,返回值:是否成功
?bool Fetch();
?//前一個記錄,返回值:是否成功
?bool FetchPrevious();
?//下一個記錄,返回值:是否成功
?bool FecthNext();
?//當Absolute為true是,跳到nRow指定的絕對行,否則,由當前位置滾動到參數FetchOffset指定的相對行,nRow大于0表示向前滾動,nRow小于0表示向后滾動
?bool FetchRow(unsigned int nRow, bool bAbsolute = true);
?//跳到第一行,返回值:是否成功
?bool FetchFirst();
?//跳到最后一行,返回值:是否成功
?bool FetchLast();
?//取消,返回值:是否成功
?bool Cancel();
?//取得當前行的第nColumn列的值。參數:哪列,接收緩沖區,接收緩沖區大小,返回值大小,緩沖區的C語言類型。返回值:是否成功
?bool GetData(unsigned short nColumn, void* pBuffer,?
? ? ?unsigned long nBufLen,?
? ? ?long * nDataLen = NULL,?
? ? ?int nType=SQL_C_DEFAULT);
?//取得當前行的pszName字段的值。參數:哪列,接收緩沖區,接收緩沖區大小,返回值大小,緩沖區的C語言類型。返回值:是否成功
?bool GetData(const char* pszName, void* pBuffer,?
? ? ? ?unsigned long nBufLen,
? ? ? ?long * nDataLen = NULL,?
? ? ? ?int nType=SQL_C_DEFAULT);
?//關閉連接,重新執行sql語句時,必須先斷開連接,然后在分配句柄才行,否則會包非法的游標
?void Close();
?//取錯誤信息.返回值:錯誤信息
?const char* GetLastError(){return (char*)m_pszLastError;}
protected:
?//初始化,分配句柄.返回值:是否成功
?bool Init();
?//取得nColumn列的字段名.參數:哪列,字段名,字段名長度.返回值:是否成功
?bool GetColumnName(unsigned short nColumn, char* pszName, short nNameLen);
?//取得字段名為pszColumn所在的列,參數:字段名,返回值:字段名所在列
?unsigned short GetColumnByName(const char* pszColumn);
?//設置錯誤信息,參數:錯誤信息,錯誤代碼
?void SetErrorInfo(const char* pszError, long lErrorCode);
?
?//取得錯誤信息,參數:句柄類型,出錯的句柄,錯誤代碼.返回值:是否成功
?bool GetErrorInfo(SQLSMALLINT nHandleType, SQLHANDLE nHandle, long lErrorCode);
?//是否成功執行了,參數:需要判斷的返回值,句柄類型, 需要判斷的句柄,錯誤代碼,返回值:是否成功
?bool IsSuccess(SQLRETURN nRet, SQLSMALLINT nHandleType, SQLHANDLE nHandle, long lErrorCode);
?//是否可用.返回值:是否成功
?bool IsValid();
protected:
?SQLHSTMT m_hStmt; //STMT句柄
?SQLRETURN m_nRet; //返回值
?
private:
?CConnection** m_ppDBCon;
protected:
?SQLCHAR m_pszLastError[SQL_MAX_MESSAGE_LENGTH+100]; //錯誤信息
?int m_nQueryTimeOut; ?//查詢超時時間
};
class CDBAccess ?
{
public:
?//執行SQL語句,參數:要執行的SQL語句.返回值:是否執行成功
?bool ExecuteSQL(const char* pszSQL);
?//關閉到數據庫的連接,如果想繼續使用,必須先調用Init函數.
?void Close();
?//重新連接數據庫,返回值:是否重連成功
?bool ReConnect();
?//斷開到數據庫的連接.返回值:是否斷開連接
?bool Disconnect();
?//連接到數據庫.參數:DNS名,用戶名,口令.返回值:是否連接到數據庫.
?bool Connect(const char* pszDNS, const char* pszUser, const char* pszPwd);
?//初始化,參數:sql語句保存文件,連接超時,登錄超時.返回值:是否初始化成功.
?bool Init(const char* pszSaveSQLFile, CConnection** ppDBCon, int nConnectTimeOut = -1, int nLoginTimeOut = -1, int nQueryTimeOut = 3);
?//是否已經連到數據庫了,返回值,是否連接到數據庫
?bool IsConnect();
?//開始事務處理,返回值:是否成功
?bool BeginTran(){return (*m_ppDBCon)->BeginTran();}
?//提交事務處理,參數:提交類型,返回值:是否成功
?bool EndTran(short nAction){return (*m_ppDBCon)->EndTran(nAction);}
?//add by stavck at 20051031
?//取得當前行的cszName字段的值。參數:哪列,接收緩沖區,接收緩沖區大小,返回值大小,緩沖區的C語言類型。返回值:是否成功
?bool GetData(const char* pszName, void* pBuffer, unsigned long nBufLen
? , long * nDataLen = NULL, int nType=SQL_C_DEFAULT)
?{return m_pQuery->GetData(pszName, pBuffer, nBufLen, nDataLen, nType);}
?//下一個記錄,返回值:是否成功
?bool Fetch(){return m_pQuery->Fetch();}
?//end add
?//跳到第一行,返回值:是否成功
?bool FetchFirst(){return m_pQuery->FetchFirst();}
?//前一個記錄,返回值:是否成功
?bool FetchPrevious(){return m_pQuery->FetchPrevious();}
?//下一個記錄,返回值:是否成功
?bool FecthNext(){return m_pQuery->FecthNext();}
?//當Absolute為true是,跳到nRow指定的絕對行,否則,由當前位置滾動到參數FetchOffset指定的相對行,nRow大于0表示向前滾動,nRow小于0表示向后滾動
?bool FetchRow(unsigned int nRow, bool bAbsolute = true)
? {return m_pQuery->FetchRow(nRow, bAbsolute);}
?//跳到最后一行,返回值:是否成功
?bool FetchLast(){return m_pQuery->FetchLast();}?
?//取錯誤信息.返回值:錯誤信息
?const char* GetLastError(){return (char*)m_pszLastError;}
?
?CDBAccess();
?virtual ~CDBAccess();
?
?//add at 20051124
?//關閉語句句柄,因為退出時關閉會出錯。所以每次數據庫操作完畢要使用該函數來關閉
?void CloseStmt(){m_pQuery->Close();}
protected:?
?int m_nLoginTimeOut; ?//登錄超時時間
?int m_nConnectTimeOut; ?//連接超時時間
?int m_nQueryTimeOut; ?//查詢超時時間
?char m_pszLastError[255]; //最后誤操作的錯誤信息
?CConnection **m_ppDBCon; ?//數據庫連接
?CQuery * m_pQuery; ? //查詢測試任務時使用,記錄集
protected:
?//設置錯誤信息,參數:錯誤信息,發生錯誤的位置,是否添加到日志
?void SetErrorInfo(const char *pszError, long lErrorCode);?
protected:
?bool m_bCreateCon; //是否是自己創建的數據庫連接
?bool m_bEnd; //查詢測試任務時使用,是否到了最后一條記錄了.
};
#endif // !defined(AFX_QUERY_H__CAEAF203_40C0_4C32_BA76_9A4B0245984B__INCLUDED_)
下面是Query.cpp文件內容:
// Query.cpp: implementation of the CQuery class.
//
//
#include "stdafx.h"
#include "Query.h"
//
// Construction/Destruction
//
CConnection::CConnection()
{
?memset(m_pszLastError, 0x00, SQL_MAX_MESSAGE_LENGTH+100);
?memset(m_pszDNS, 0x00, sizeof(m_pszDNS));
?memset(m_pszUser, 0x00, sizeof(m_pszUser));
?memset(m_pszPwd, 0x00, sizeof(m_pszPwd));
?m_bIsConnect = false;
?m_hCon = INVALID_HANDLE_VALUE;
?m_hEnv = INVALID_HANDLE_VALUE;
?m_bTraning = false;
?m_nRet = SQL_SUCCESS;
?m_bInit = false;
}
CConnection::~CConnection()
{
?try
?{
? if(m_bTraning)
? {//如果沒有手工斷掉連接,這里會進行回滾
? ?EndTran(SQL_ROLLBACK);
? }
? if(IsConnect())
? {//如果連接還沒有斷開,那么這里斷開。
? ?Disconnect();
? }
? if(m_hEnv != INVALID_HANDLE_VALUE)
? {
? ?SQLFreeHandle(SQL_HANDLE_ENV, m_hEnv);
? ?m_hEnv = INVALID_HANDLE_VALUE;
? }
? if(m_hCon != INVALID_HANDLE_VALUE)
? {
? ?SQLFreeHandle(SQL_HANDLE_DBC, m_hCon);
? ?m_hCon = INVALID_HANDLE_VALUE;
? }
?}
?catch (...)
?{
? //
?}?
}
bool CConnection::GetErrorInfo(SQLSMALLINT nHandleType, SQLHANDLE nHandle, long lErrorCode)
{//還不完整,要進一步寫一下,否則,只能取到最后一行的錯誤信息,不過大部分情況時夠用了.
?bool bConnInd = nHandleType == SQL_HANDLE_DBC ? true :false;
?SQLRETURN nRet = SQL_SUCCESS;
?SQLCHAR pszSqlState[SQL_MAX_MESSAGE_LENGTH] = "";
? ? SQLCHAR pszErrorMsg[SQL_MAX_MESSAGE_LENGTH] = "";
?
?SQLINTEGER nNativeError = 0L;
?SQLSMALLINT nErrorMsg = 0;
?SQLSMALLINT nRecNmbr = 1;
?
?//執行錯誤時ODBC返回的是一個錯誤信息的結果集,需要遍歷結果集合中所有行
?memset(pszSqlState, 0x00, sizeof(pszSqlState));
?memset(pszErrorMsg, 0x00, sizeof(pszErrorMsg));
?nRet = SQLGetDiagRec(nHandleType, nHandle,
? nRecNmbr, pszSqlState, &nNativeError,
? pszErrorMsg, SQL_MAX_MESSAGE_LENGTH - 1,
? &nErrorMsg);
?SetErrorInfo((char*)pszErrorMsg, lErrorCode);
?return true;
}
bool CConnection::IsSuccess(SQLRETURN nRet, SQLSMALLINT nHandleType,
? ? ? ?SQLHANDLE nHandle, long lErrorCode)
{
?if(nRet == SQL_SUCCESS)
?{
? return true;
?}
?else if(nRet == SQL_SUCCESS_WITH_INFO)
?{
? return true;
?}
?else
?{
? GetErrorInfo(nHandleType, nHandle, lErrorCode);
? return false;
?}
?return false;?
}
bool CConnection::Init(int nConnectTimeOut /* = -1 */, int nLoginTimeOut /* = -1 */)
{?
?m_nRet = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_hEnv);
?if(IsSuccess(m_nRet, SQL_HANDLE_ENV, m_hEnv, ERROR_CON_INIT_1))
?{
? //將ODBC設置成為版本3,否則某些ODBC API 函數不能被支持。
? m_nRet = SQLSetEnvAttr(m_hEnv, SQL_ATTR_ODBC_VERSION,
? ? ? ?(SQLPOINTER)SQL_OV_ODBC3, SQL_IS_UINTEGER);
? if(!IsSuccess(m_nRet, SQL_HANDLE_ENV, m_hEnv, ERROR_CON_INIT_2))
? {
? ?//系統不支持ODBC3
? ?return false;
? }
? m_nRet = SQLAllocHandle(SQL_HANDLE_DBC, m_hEnv, &m_hCon);
? if(!IsSuccess(m_nRet, SQL_HANDLE_DBC, m_hCon, ERROR_CON_INIT_3))
? {
? ?//分配連接句柄不成功!
? ?return false;
? }
?}
?else
?{
? //分配環境句柄不成功!
? return false;
?}
?
?m_nConnectTimeOut = nConnectTimeOut;
?m_nLoginTimeOut = nLoginTimeOut;
?m_bInit = true;
?SetTimeOut(nConnectTimeOut, nLoginTimeOut);
?return true;
}
bool CConnection::Connect(const char *pszDNS, const char *pszUser, const char *pszPwd)
{
?if(!m_bInit)
?{//如果沒有初始化,是不能連接的!
? SetErrorInfo("沒有初始化,不能連接到數據庫!", ERROR_CON_CONNECT_4);
? return false;
?}
?if(pszDNS == NULL)
?{//DNS怎么也不能為空的啊
? SetErrorInfo("DNS名為空,無法連接數據庫!", ERROR_CON_CONNECT_1);
? return false;
?}
?else
?{
? strncpy((char*)m_pszDNS, pszDNS, sizeof(m_pszDNS));
?}
?if(pszUser != NULL)
?{
? strncpy((char*)m_pszUser, pszUser, sizeof(m_pszUser));
?}
?else
?{
? m_pszUser[0] = NULL;
?}
?if(pszPwd != NULL)
?{
? strncpy((char*)m_pszPwd, pszPwd, sizeof(m_pszPwd));
?}
?else
?{
? m_pszPwd[0] = NULL;
?}
?try
?{
? //開始連接
? m_nRet = SQLConnect(m_hCon,
? ? ? ?(SQLCHAR*)pszDNS, SQL_NTS,
? ? ? ?(SQLCHAR*)pszUser, SQL_NTS,
? ? ? ?(SQLCHAR*)pszPwd, SQL_NTS);
?}
?catch (...)
?{
? SetErrorInfo("連接數據庫時發生錯誤!", ERROR_CON_CONNECT_3);
? return false;
?}
?m_bIsConnect = IsSuccess(m_nRet, SQL_HANDLE_DBC, m_hCon, ERROR_CON_CONNECT_2);
?
?return m_bIsConnect;
}
void CConnection::SetErrorInfo(const char *pszError, long lErrorCode)
{//先把錯誤代碼填到錯誤信息中,接著填具體的錯誤信息
?memset(m_pszLastError, 0x00, sizeof(m_pszLastError));
?char pszErrorCode[20] = "";
?
?//先設置錯誤代碼
?sprintf((char*)pszErrorCode, "EC[%ld]", lErrorCode);
?int nLen = strlen((char*)pszErrorCode);
?
?strncpy((char*)m_pszLastError, (const char*)pszErrorCode, nLen);
?//不能超過了最大長度,要控制一下
?size_t nMaxSize = sizeof(m_pszLastError)-1-nLen;
?strncat((char*)m_pszLastError, pszError, nMaxSize>strlen(pszError)?strlen(pszError):nMaxSize);
}
bool CConnection::Disconnect()
{
?if(!IsConnect())
?{//如果沒有連接,只是簡單的返回
? return true;
?}
?
?if(m_bTraning)
?{//事務處理還沒有提交呢!
? SetErrorInfo("必須先提交事務,才能斷開!", ERROR_CON_DISCONNECT_1);
? return false;
?}
?try
?{?
? //斷開連接
? m_nRet = SQLDisconnect(m_hCon);
? if(m_hEnv != INVALID_HANDLE_VALUE)
? {
? ?SQLFreeHandle(SQL_HANDLE_ENV, m_hEnv);
? ?m_hEnv = INVALID_HANDLE_VALUE;
? }
? if(m_hCon != INVALID_HANDLE_VALUE)
? {
? ?SQLFreeHandle(SQL_HANDLE_DBC, m_hCon);
? ?m_hCon = INVALID_HANDLE_VALUE;
? } ?
? m_bInit = false;
?}
?catch (...)
?{
? SetErrorInfo("斷開數據庫連接時發生錯誤!", ERROR_CON_DISCONNECT_3);
? m_bIsConnect = false;
? return false;
?}
?
?//判斷是否成功斷開
?m_bIsConnect = !IsSuccess(m_nRet, SQL_HANDLE_DBC, m_hCon, ERROR_CON_DISCONNECT_2);
?
?return !m_bIsConnect;
}
bool CConnection::BeginTran()
{
?if(!IsConnect())
?{//如果沒有連接,怎么開始事務處理!
? SetErrorInfo("沒有連接,怎么開始事務處理?", ERROR_CON_BEGINTRAN_3);
? return false;
?}
?try
?{?
? //這里是設置手動提交.
? m_nRet= SQLSetConnectAttr(m_hCon,
? ? ? ?SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF,
? ? ? ?SQL_IS_POINTER);
?}
?catch (...)
?{
? SetErrorInfo("開始事務處理時發生錯誤!", ERROR_CON_BEGINTRAN_2);
? return false;
?}
?//判斷是否成功
?m_bTraning = IsSuccess(m_nRet, SQL_HANDLE_DBC, m_hCon, ERROR_CON_BEGINTRAN_1);
?return m_bTraning;
}
bool CConnection::EndTran(short nAction)
{
?if(!IsConnect() || !m_bTraning)
?{
? SetErrorInfo("沒有連接,或者還沒有開始事務處理!無法提交事務", ERROR_CON_ENDTRAN_4);
? return true;
?}
?if(nAction != SQL_COMMIT)
?{//如果不是提交,都當回滾處理
? nAction = SQL_ROLLBACK;
?}
?try
?{?
? //先結束事務處理
? m_nRet = SQLEndTran(SQL_HANDLE_DBC, m_hCon, nAction);
? //判斷是否成功
? m_bTraning = !IsSuccess(m_nRet, SQL_HANDLE_DBC, m_hCon, ERROR_CON_ENDTRAN_1);
? //再改成自動提交.
? m_nRet= SQLSetConnectAttr(m_hCon,
? ? ? ?SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON,
? ? ? ?SQL_IS_POINTER);
?}
?catch (...)?
?{
? SetErrorInfo("結束事務處理時發生錯誤!", ERROR_CON_ENDTRAN_3);
? return false;
?}
?
?//修改成功了沒
?IsSuccess(m_nRet, SQL_HANDLE_DBC, m_hCon, ERROR_CON_ENDTRAN_2);?
?return !m_bTraning;?
}
bool CConnection::SetTimeOut(int nConnect, int nLogin)
{
?bool bRet = false;
?if(!m_bInit)
?{//如果沒有初始化,不能使用該函數!
? SetErrorInfo("請在初始化后使用該函數", ERROR_CON_SETTIMEOUT_1);
? return bRet;
?}
?try
?{?
? if(nLogin >= 0)
? {//如果是負數,就不用管啦!
? ?if(m_bIsConnect)
? ?{//連接后,不能設置登錄超時了.
? ? SetErrorInfo("設置登錄超時必須在連接前使用該函數", ERROR_CON_SETTIMEOUT_2);
? ?}
? ?else
? ?{
? ? m_nRet = SQLSetConnectAttr(m_hCon, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)nLogin, SQL_IS_INTEGER);
? ? bRet = IsSuccess(m_nRet, SQL_HANDLE_DBC, m_hCon, ERROR_CON_SETTIMEOUT_3);
? ?}
? }
??
? if(nConnect >= 0)
? {//如果是負數,就不用管啦!
? ?m_nRet = SQLSetConnectAttr(m_hCon, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER)nConnect, SQL_IS_INTEGER);
? ?bRet = IsSuccess(m_nRet, SQL_HANDLE_DBC, m_hCon, ERROR_CON_SETTIMEOUT_4) || bRet;
? }
? else
? {
? ?return bRet;
? }
?}
?catch (...)
?{
? SetErrorInfo("設置系統超時時發生錯誤!", ERROR_CON_SETTIMEOUT_5);
? return false;
?}
?return bRet;
}
bool CConnection::ReConnect()
{
?try
?{
? //斷開連接
? SQLDisconnect(m_hCon);
? m_bIsConnect = false;
??
? if(m_hEnv != INVALID_HANDLE_VALUE)
? {
? ?SQLFreeHandle(SQL_HANDLE_ENV, m_hEnv);
? ?m_hEnv = INVALID_HANDLE_VALUE;
? }
? if(m_hCon != INVALID_HANDLE_VALUE)
? {
? ?SQLFreeHandle(SQL_HANDLE_DBC, m_hCon);
? ?m_hCon = INVALID_HANDLE_VALUE;
? } ?
? m_bInit = false;
? if(!Init(m_nConnectTimeOut, m_nLoginTimeOut))
? {
? ?return false;
? }
? m_bIsConnect = false;
?}
?catch (...)
?{
? m_bIsConnect = false;
? m_bInit = false;
? return false;
?}
?return Connect(m_pszDNS, m_pszUser, m_pszPwd);
}
//add by stavck at 20051116
bool CConnection::IsConnect()
{?
?//如果連接已經手工斷開了,這里就沒有必要再檢查了。
?if(!m_bIsConnect)
?{
? return false;
?}
?else
?{
? return true;
?}
?//下面代碼會在數據庫重連后產生錯誤,刪除。
?SQLINTEGER lRet = SQL_CD_TRUE;
?SQLINTEGER lRetSize = 0;
?try
?{
? //判斷連接是否活著,不過斷開時一定要等到對數據庫有SQL請求后才有可以。
? m_nRet = SQLGetConnectAttr(m_hCon, SQL_ATTR_CONNECTION_DEAD, (SQLPOINTER)&lRet, SQL_IS_INTEGER, &lRetSize);
? if(!IsSuccess(m_nRet, SQL_HANDLE_DBC, m_hCon, ERROR_CON_ISCONNECT_1))
? {
? ?return false;
? }
?}
?catch (...)
?{
? SetErrorInfo("查詢系統連接時發生錯誤!", ERROR_CON_ISCONNECT_2);
? return false;?
?}
?
?//連接仍然是活動的
?if(lRet == SQL_CD_FALSE)
?{
? return true;
?}
?
?//連接已經斷掉了
?return false;
}
//
CQuery::CQuery(CConnection** ppDBCon, int nQueryTimeOut /* = 3 */)
{
?memset(m_pszLastError, 0x00, SQL_MAX_MESSAGE_LENGTH+100);
?m_nQueryTimeOut = 3;
?m_ppDBCon = ppDBCon;?
?m_nRet = SQL_SUCCESS;
?m_hStmt = INVALID_HANDLE_VALUE;?
?m_nQueryTimeOut = nQueryTimeOut;
}
CQuery::~CQuery()
{
?Close();
}
bool CQuery::Init()
{
?try
?{?
? if (m_ppDBCon == NULL || *m_ppDBCon == NULL ||!(*m_ppDBCon)->IsConnect())
? {
? ?return false;
? }
??
? //分配SQL語句句柄
? m_nRet = SQLAllocHandle( SQL_HANDLE_STMT,
? ?(*m_ppDBCon)->m_hCon, &m_hStmt );
? if(!IsSuccess(m_nRet, SQL_HANDLE_DBC,?
? ?(*m_ppDBCon)->m_hCon, ERROR_QUERY_INIT_1))
? {
? ?m_hStmt = INVALID_HANDLE_VALUE;?
? ?return false;
? }?
? //指定要使用的游標并發級別
? m_nRet = SQLSetStmtAttr(m_hStmt, SQL_ATTR_CONCURRENCY,?
? ? ? ? ?(SQLPOINTER) SQL_CONCUR_ROWVER, 0);
? if(!IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_INIT_2))
? {
? ?Close();?
? ?return false;
? }
??
? //設置光標類型為鍵集光標,
? //鍵集光標能夠檢測到行的刪除和修改,但是無法檢測到檢測到行的添加和結果集順序變化。
? //因為在光標創建時就創建了整個結果集,結果集合中記錄和順序已經被固定,
? //這一點和靜態光標一樣。所以鍵集光標可以說是一種介于靜態光標和動態光標之間的光標類型。
? m_nRet = SQLSetStmtAttr(m_hStmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)?
? ? ? ? SQL_CURSOR_DYNAMIC /* SQL_CURSOR_KEYSET_DRIVEN*/, 0);
? if(!IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_INIT_3))
? {
? ?Close();?
? ?return false;
? }
? m_nRet = SQLSetStmtAttr(m_hStmt, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)m_nQueryTimeOut, SQL_IS_UINTEGER);
? if(!IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_INIT_5))
? {
? ?Close();?
? ?return false;
? }
/*#ifdef _DEBUG
? SQLINTEGER dummy;
? m_nRet = SQLGetStmtAttr(m_hStmt, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)&lTimeOut, SQL_IS_UINTEGER, &dummy);
? if(IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_INIT_5))
? {
? ?printf("query time out is: %ld/n/r", lTimeOut);
? }
??
#endif*/
?}
?catch (...)
?{
? SetErrorInfo("初始化時發生錯誤!", ERROR_QUERY_INIT_4);
? Close();
? return false;
?}
?return true;
}
void CQuery::Close()
{
?try
?{
? if(m_hStmt != INVALID_HANDLE_VALUE)
? {
? ?//釋放句柄
? ?//SQLFreeStmt(m_hStmt, SQL_DROP);
? ?SQLFreeHandle(SQL_HANDLE_STMT,m_hStmt);
? ?m_hStmt = INVALID_HANDLE_VALUE;?
? }
?}
?catch (...)?
?{
? m_hStmt = INVALID_HANDLE_VALUE;
?}
}
bool CQuery::GetErrorInfo(SQLSMALLINT nHandleType, SQLHANDLE nHandle,?
? ? ? ? ?long lErrorCode)
{//還不完整,要進一步寫一下,否則,只能取到最后一行的錯誤信息。
?bool bConnInd = nHandleType == SQL_HANDLE_DBC ? true :false;
?SQLRETURN nRet = SQL_SUCCESS;
?SQLCHAR pszSqlState[SQL_MAX_MESSAGE_LENGTH] = "";
? ? SQLCHAR pszErrorMsg[SQL_MAX_MESSAGE_LENGTH] = "";
?SQLINTEGER nNativeError = 0L;
?SQLSMALLINT nErrorMsg = 0;
?SQLSMALLINT nRecNmbr = 1;
?
?//執行錯誤時ODBC返回的是一個錯誤信息的結果集,需要遍歷結果集合中所有行
?memset(pszSqlState, 0x00, sizeof(pszSqlState));
?memset(pszErrorMsg, 0x00, sizeof(pszErrorMsg));
?
?nRet = SQLGetDiagRec(nHandleType, nHandle,
? nRecNmbr, pszSqlState, &nNativeError,
? pszErrorMsg, SQL_MAX_MESSAGE_LENGTH - 1,
? &nErrorMsg);
?SetErrorInfo((char*)pszErrorMsg, lErrorCode);
?return true;
}
bool CQuery::IsSuccess(SQLRETURN nRet, SQLSMALLINT nHandleType,
? ? ? ? ?SQLHANDLE nHandle, long lErrorCode)
{
?if(nRet == SQL_SUCCESS)
?{
? return true;
?}
?else if(nRet == SQL_SUCCESS_WITH_INFO)
?{//表明執行成功但是帶有一定錯誤信息,此時不應該記錄到log中,否則log會與日俱增
? //GetErrorInfo(nHandleType, nHandle, lErrorCode, false);
? return true;
?}
?else
?{
? GetErrorInfo(nHandleType, nHandle, lErrorCode);
? return false;
?}
?return false;?
}
void CQuery::SetErrorInfo(const char *pszError, long lErrorCode)
{//先把錯誤代碼填到錯誤信息中,接著填具體的錯誤信息
?memset(m_pszLastError, 0x00, sizeof(m_pszLastError));
?char pszErrorCode[20] = "";
?//先設置錯誤代碼
?sprintf((char*)pszErrorCode, "EC[%ld]", lErrorCode);
?int nLen = strlen((char*)pszErrorCode);
?strncpy((char*)m_pszLastError, (const char*)pszErrorCode, nLen);
?//不能超過了最大長度,要控制一下
?size_t nMaxSize = sizeof(m_pszLastError)-1-nLen;
?strncat((char*)m_pszLastError, pszError,?
? nMaxSize>strlen(pszError)?strlen(pszError):nMaxSize);
}
bool CQuery::IsValid()
{
?return m_hStmt != INVALID_HANDLE_VALUE;
}
unsigned short CQuery::GetColumnCount()
{
?if(!IsValid())
?{
? SetErrorInfo("STMT句柄不可用!", ERROR_QUERY_GETCOLCOUNT_1);
? return 0;
?}
?
?short nCols=0;
?try
?{
? if(!IsSuccess(m_nRet = SQLNumResultCols(m_hStmt, &nCols),?
? ?SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_GETCOLCOUNT_2))
? {
? ?//如果不成功,返回0個
? ? ?return 0;
? }
?}
?catch (...)?
?{
? SetErrorInfo("取得列個數時發生錯誤!", ERROR_QUERY_GETCOLCOUNT_3);
? Close();
? return 0;
?}
?return nCols;?
}
long CQuery::GetChangedRowCount()
{//對select語句是無效的,請選擇使用
?if(!IsValid())
?{
? SetErrorInfo("STMT句柄不可用!", ERROR_QUERY_GETCROWCOUNT_1);
? return 0;
?}
?
?long nRows=0;
?
?try
?{
? if(!IsSuccess(m_nRet = SQLRowCount(m_hStmt,&nRows),?
? ?SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_GETCROWCOUNT_2))
? {
? ?return 0;
? }
?}
?catch (...)?
?{
? SetErrorInfo("取得影響記錄集的個數數時發生錯誤!", ERROR_QUERY_GETCROWCOUNT_3);
? Close();
? return 0;
?}
?return nRows;?
}
bool CQuery::ExecuteSQL(const char* pszSQL)
{
?//因為一個語句句柄只能執行一次sql語句,所有,得先釋放才能執行
?if(IsValid())
?{//如果有效,先關閉
? Close();
?}
?if(!Init())
?{//再初始化
? return false;
?}
?if(!IsValid())
?{//這時不能用?
? SetErrorInfo("STMT句柄不可用!", ERROR_QUERY_EXECSQL_1);
? return false;
?}
?try
?{
? //執行相應的sql語句
? m_nRet = SQLExecDirect(m_hStmt, (SQLTCHAR *)pszSQL, SQL_NTS);
?}
?catch (...)?
?{
? SetErrorInfo("執行SQL語句出錯!", ERROR_QUERY_EXECSQL_3);
? Close();
? return false;
?}
?
?return IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_EXECSQL_2);?
}
bool CQuery::Fetch()
{
?if(!IsValid())
?{
? SetErrorInfo("STMT句柄不可用!", ERROR_QUERY_FETCH_1);
? return false;
?}
?try
?{
? m_nRet = SQLFetch(m_hStmt);
?}
?catch (...)?
?{
? SetErrorInfo("Fetch出錯!", ERROR_QUERY_FETCH_3);
? Close();
? return false;
?}?
?return IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_FETCH_2);
}
bool CQuery::FecthNext()
{
?if(!IsValid())
?{
? SetErrorInfo("STMT句柄不可用!", ERROR_QUERY_FETCHNEXT_1);
? return false;
?}
?
?try
?{
? m_nRet = SQLFetchScroll(m_hStmt, SQL_FETCH_NEXT, 0);
?}
?catch (...)?
?{
? SetErrorInfo("FecthNext出錯!", ERROR_QUERY_FETCHNEXT_3);
? Close();
? return false;?
?}?
?
?return IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_FETCHNEXT_2);?
}
bool CQuery::FetchPrevious()
{
?if(!IsValid())
?{
? SetErrorInfo("STMT句柄不可用!", ERROR_QUERY_FETCHPRE_1);
? return false;
?}
?try
?{
? m_nRet = SQLFetchScroll(m_hStmt, SQL_FETCH_PRIOR, 0);
?}
?catch (...)?
?{
? SetErrorInfo("FetchPrevious出錯!", ERROR_QUERY_FETCHPRE_3);
? Close();
? return false;
?}?
?return IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_FETCHPRE_2);?
}
bool CQuery::FetchFirst()
{
?if(!IsValid())
?{
? SetErrorInfo("STMT句柄不可用!", ERROR_QUERY_FETCHFIRST_1);
? return false;
?}
?try
?{
? m_nRet = SQLFetchScroll(m_hStmt, SQL_FETCH_FIRST, 0);
?}
?catch (...)?
?{
? SetErrorInfo("FetchFirst出錯!", ERROR_QUERY_FETCHFIRST_3);
? Close();
? return false;
?}?
?
?return IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_FETCHFIRST_2);?
}
bool CQuery::FetchLast()
{
?if(!IsValid())
?{
? SetErrorInfo("STMT句柄不可用!", ERROR_QUERY_FETCHLAST_1);
? return false;
?}
?try
?{
? m_nRet = SQLFetchScroll(m_hStmt,SQL_FETCH_LAST,0);
?}
?catch (...)?
?{
? SetErrorInfo("FetchLast出錯!", ERROR_QUERY_FETCHLAST_3);
? Close();
? return false;
?}?
?return IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_FETCHLAST_2);?
}
bool CQuery::FetchRow(unsigned int nRow, bool bAbsolute /* = true */)
{
?if(!IsValid())
?{
? SetErrorInfo("STMT句柄不可用!", ERROR_QUERY_FETCHROW_1);
? return false;
?}
?try
?{
? m_nRet = SQLFetchScroll(m_hStmt,
? ? ? ?(bAbsolute ? SQL_FETCH_ABSOLUTE : SQL_FETCH_RELATIVE),?
? ? ? ?nRow);
?}
?catch (...)?
?{
? SetErrorInfo("FetchRow出錯!", ERROR_QUERY_FETCHROW_3);
? Close();
? return false;
?}
?
?return IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_FETCHROW_2);
}
bool CQuery::Cancel()
{
?if(!IsValid())
?{
? SetErrorInfo("STMT句柄不可用!", ERROR_QUERY_CANCEL_1);
? return true;
?}
?try
?{
? m_nRet = SQLCancel(m_hStmt);
?}
?catch (...)
?{
? SetErrorInfo("Cancel出錯!", ERROR_QUERY_CANCEL_3);
? Close();
? return false;
?}
?return IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_CANCEL_2);
}
bool CQuery::GetData(unsigned short nColumn, void* pBuffer,?
? ? ? ?unsigned long nBufLen,
? ? ? ?long * nDataLen /* = NULL */,
? ? ? ?int nType/* =SQL_C_DEFAULT */)
{
?if(nColumn <= 0 || nColumn > GetColumnCount() || pBuffer == NULL)
?{
? SetErrorInfo("列范圍不對,或者pBuffer為空!", ERROR_QUERY_GETDATA_1);
? return false;
?}
?if(!IsValid())
?{
? SetErrorInfo("STMT句柄不可用!", ERROR_QUERY_GETDATA_2);
? return true;
?}
?SQLINTEGER nOutLen = 0;
?
?try
?{
? m_nRet = SQLGetData(m_hStmt, nColumn, nType, pBuffer, nBufLen, &nOutLen);
?}
?catch (...)
?{
? SetErrorInfo("GetData出錯!", ERROR_QUERY_GETDATA_5);
? Close();
? return false;
?}
?if(!IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_GETDATA_3))
?{?
? return false;
?}
?if(nDataLen)
?{
? *nDataLen=nOutLen;
?}
?return true;?
}
bool CQuery::GetData(const char* pszName, void* pBuffer,?
? ? ? ? unsigned long nBufLen,?
? ? ? ?long * nDataLen /* = NULL */,?
? ? ? ?int nType/* =SQL_C_DEFAULT */)
{
?if(pszName == NULL || pBuffer == NULL)
?{
? SetErrorInfo("PszName or pBuffer 不能為空!", ERROR_QUERY_GETDATA_4);
? return false;
?}
?unsigned short nColumn = GetColumnByName(pszName);
?//有效性判斷讓GetData自己判斷
?return GetData(nColumn, pBuffer, nBufLen, nDataLen, nType);
}
unsigned short CQuery::GetColumnByName(const char *pszColumn)
{
?if(!IsValid())
?{
? SetErrorInfo("STMT句柄不可用!", ERROR_QUERY_GETCOLBYNAME_1);
? return true;
?}
?unsigned short nCols = GetColumnCount();
?
?for(unsigned short i = 1; i < (nCols+1) ; i++)
?{//依次得到每個列的字段名,然后比較。
? TCHAR pszName[256] = "";
? if(GetColumnName(i, pszName, sizeof(pszName)))
? {
? ?if(stricmp(pszName, pszColumn) == 0)
? ?{
? ? return i;
? ?}
? }
?}
?return 0;
}
bool CQuery::GetColumnName(unsigned short nColumn, char *pszName, short nNameLen)
{
?if(!IsValid())
?{
? SetErrorInfo("STMT句柄不可用!", ERROR_QUERY_GETCOLNAME_1);
? return true;
?}
?int nType = SQL_C_DEFAULT;
?SQLSMALLINT nSwCol=0, nSwType=0, nSwScale=0, nSwNull=0; //這些數據,在這個函數中,我們不用關心
?SQLUINTEGER pcbColDef=0;
?try
?{
? m_nRet = SQLDescribeCol( m_hStmt, nColumn, ?
? ? ? ?(SQLTCHAR*)pszName, nNameLen,
? ? ? ?&nSwCol, &nSwType, &pcbColDef,?
? ? ? ?&nSwScale, &nSwNull);?
?}
?catch (...)?
?{
? SetErrorInfo("取字段名稱時出錯!", ERROR_QUERY_GETCOLNAME_3);
? Close();
? return false;
?}
?
?return ?IsSuccess(m_nRet, SQL_HANDLE_STMT, m_hStmt, ERROR_QUERY_GETCOLNAME_2);
}
//
CDBAccess::CDBAccess()
{
?m_ppDBCon = NULL;
?memset(m_pszLastError, 0x00, sizeof(m_pszLastError));
?m_nConnectTimeOut = -1;
?m_nLoginTimeOut = -1;
?m_pQuery = NULL;
?m_bEnd = false;
?m_bCreateCon = false;
}
CDBAccess::~CDBAccess()
{
?if(m_pQuery != NULL)
?{
? delete m_pQuery;
? m_pQuery = NULL;
?}?
}
void CDBAccess::SetErrorInfo(const char *pszError, long lErrorCode)
{//先把錯誤代碼填到錯誤信息中,接著填具體的錯誤信息
?memset(m_pszLastError, 0x00, sizeof(m_pszLastError));
?char pszErrorCode[20] = "";
?//先設置錯誤代碼
?sprintf((char*)pszErrorCode, "EC[%ld]", lErrorCode);
?int nLen = strlen((char*)pszErrorCode);
?strncpy((char*)m_pszLastError, (const char*)pszErrorCode, nLen);
?//不能超過了最大長度,要控制一下
?size_t nMaxSize = sizeof(m_pszLastError)-1-nLen;
?strncat((char*)m_pszLastError, pszError, nMaxSize>strlen(pszError)?strlen(pszError):nMaxSize);
}
bool CDBAccess::Init(const char* pszSaveSQLFile, CConnection** ppDBCon,
? ? ? int nConnectTimeOut /* = -1 */, int nLoginTimeOut /* = -1 */,
? ? ? int nQueryTimeOut /* = 3 */)
{
?m_nLoginTimeOut = nLoginTimeOut;
?m_nConnectTimeOut = nConnectTimeOut;
?m_nQueryTimeOut = nQueryTimeOut;
?
?m_ppDBCon = ppDBCon;
?
?if((*ppDBCon) != NULL)
?{
? return true;
?}
?
?try
?{
? (*m_ppDBCon) = new CConnection;
? if ((*m_ppDBCon) == NULL)
? {
? ?SetErrorInfo("沒有足夠的內存!", ERROR_DBACCESS_INIT_1);
? ?return false;
? }
? m_bCreateCon = true;
? if(!(*m_ppDBCon)->Init(nConnectTimeOut, nLoginTimeOut))
? {
? ?delete (*m_ppDBCon);
? ?(*m_ppDBCon) = NULL;
? ?
? ?SetErrorInfo("數據庫連接初始化失敗!", ERROR_DBACCESS_INIT_2);
?
? ?m_bCreateCon = false;
? ?return false;
? }
?}
?catch (...)?
?{
? SetErrorInfo("初始化時出錯!", ERROR_DBACCESS_INIT_3);
? return false;?
?}
?m_pQuery = new CQuery(m_ppDBCon, m_nQueryTimeOut);
?
?if(m_pQuery == NULL)
?{
? SetErrorInfo("沒有足夠的內存!", ERROR_DBACCESS_INIT_4);
? return false;
?}
?return true;
}
bool CDBAccess::Disconnect()
{
?if((*m_ppDBCon) == NULL)
?{
? SetErrorInfo("重新連接數據庫時出錯!,請稍后再試!", ERROR_DBACCESS_DISCONNECT_1);
? return false;
?}
?//注意,這里試著回滾了,請確保事務已經被提交.
?if((*m_ppDBCon)->IsInTran())
?{
? (*m_ppDBCon)->EndTran(SQL_ROLLBACK);
?}
?return (*m_ppDBCon)->Disconnect();
}
bool CDBAccess::Connect(const char *pszDNS, const char *pszUser, const char *pszPwd)
{
?if((*m_ppDBCon) == NULL)
?{
? SetErrorInfo("還沒有初始化,請重新初始化后再試!", ERROR_DBACCESS_CONNECT_1);
? return false;
?}
?
?if(pszDNS == NULL)
?{
? SetErrorInfo("DNS不能為空!", ERROR_DBACCESS_CONNECT_2);
? return false;
?}
?return (*m_ppDBCon)->Connect(pszDNS, pszUser, pszPwd);
}
bool CDBAccess::ReConnect()
{
?//add at 20051124 for 關閉前,先試著關閉語句句柄,以免數據庫關閉后,語句句柄無效!
?m_pQuery->Close();
?return (*m_ppDBCon)->ReConnect();
}
bool CDBAccess::IsConnect()
{
?if((*m_ppDBCon) == NULL)
?{
? return false;
?}
?else
?{
? return (*m_ppDBCon)->IsConnect();
?}
}
void CDBAccess::Close()
{
?try
?{?
? if(m_pQuery != NULL)
? {
? ?delete m_pQuery;
? ?m_pQuery = NULL;
? }?
?
? if(m_bCreateCon)
? {
? ?if((*m_ppDBCon) != NULL)
? ?{
? ? //強制斷開,很霸道的,一定要保證所有的事務都提交在斷開!
? ? delete (*m_ppDBCon);
? ? (*m_ppDBCon) = NULL;
? ?}
? ?m_bCreateCon = false;
? }
?}
?catch (...)?
?{
? //
?}
}
bool CDBAccess::ExecuteSQL(const char *pszSQL)
{
?if(!IsConnect())
?{
? SetErrorInfo("沒有連接到數據庫,無法執行SQL語句!", ERROR_DBACCESS_EXECUTESQL_1);
? return false;
?}
?bool bRet = false;
?try
?{ if(m_pQuery == NULL)
? {
? ?m_pQuery = new CQuery((m_ppDBCon), m_nQueryTimeOut);
? ?if(m_pQuery == NULL)
? ?{
? ? SetErrorInfo("沒有足夠的內存!", ERROR_DBACCESS_EXECUTESQL_2);
? ? return false;
? ?}
? }
? if (!m_pQuery->ExecuteSQL(pszSQL))
? {
? ?SetErrorInfo(m_pQuery->GetLastError(), ERROR_DBACCESS_EXECUTESQL_3);
? ?bRet = false;
? }
? else
? {
? ?bRet = true;
? }
?}
?catch (...)?
?{
? SetErrorInfo("執行指定的SQL語句時出錯!", ERROR_DBACCESS_EXECUTESQL_5);
? return false;?
?}
?return bRet;
}
========
學會使用ODBC API
http://www.cnblogs.com/geminiv/archive/2012/09/18/2691117.html數據庫方式
雖說以前也接觸過一些數據庫的應用,但是由于當時只是抱著非常淺顯的數據庫知識,做了一些SQL語句上的操作,安裝-配置-連接這一系列的流程全是自己照著例子實現的,大概記住的也就是ODBC、ADO之類,也簡單的理解了一下,當共享使用數據庫時最好使用ODBC的方式,而本地的數據庫就可以使用ADO。
具體區別這些連接方式倒是就可以好好學習一下了:
MDAC:http://zh.wikipedia.org/wiki/MDAC
ODBC:http://zh.wikipedia.org/zh-cn/ODBC
ADO:http://zh.wikipedia.org/wiki/ADO
簡單的來說,ODBC和ADO都是微軟提供的數據庫訪問方式,ADO是一個COM組件,而ODBC則是一種標準的訪問方式,結束了數據庫訪問和數據庫相關的局面,只不過兩者實現的不同,所以在不同應用的表象上不同罷了(不對的話請指出,只看了Wiki上的一點介紹,沒有系統學習)。
使用
通過分析考慮到將來可能會采用共享數據庫的方式,此外對于COM自己也不是很熟悉,所以還是使用ODBC的方式比較好,使用ODBC的方式就很多了,但是由于我并不是程序開發人員,現在負責維護,不大敢做大手術,所以就選擇了ODBC API,這就相當于使用WINDOS API和MFC一樣,雖然API復雜,但是相對自由很多。
首先要在程序中添加幾個依賴項
復制代碼
#include <sql.h>
#include <sqlext.h>
#include <odbcinst.h>
#pragma comment(lib,"odbc32.lib")
#pragma comment(lib,"odbccp32.lib")
復制代碼
前兩個頭文件分別代表基本的ODBC API和高級的ODBC API,最后一個好像是包含了更高級的API(暫時這么理解吧),一般用到前兩個即可,odbc32.lib對應sql.h和sqlext.h,odbccp32.lib對應odbcinst.h。
所有使用數據庫的過程無非是這樣一個流程:
1. 想辦法建立數據庫的連接
2. 想辦法執行SQL語句,并使用結果
3. 不用時關閉連接,以免下次訪問報錯
對于像我這樣不常用數據庫的菜鳥來說,這樣的方式足夠完成我所面臨的問題了,諸如互斥訪問、分布式之類的問題還是暫時留給高手們考慮吧。
建立數據庫連接
在我們要連接數據庫的類中,或者全局變量下建立相關的變量
? ? SQLHENV m_hEnviroment;//數據庫環境句柄,屬于老大級別的
? ? SQLHDBC m_hDatabaseConnection;//數據庫連接句柄,老大以后就是他了,有了他數據庫就連接上了
? ? SQLHSTMT m_hStatement;//執行語句句柄,最終執行SQL于句的句柄
使用ODBC API建立數據庫連接分為3部分:申請環境句柄,使用環境句柄申請連接句柄、使用連接句柄連接數據庫。
復制代碼
/* 申請環境變量 */
? ? SQLRETURN l_uiReturn = SQLAllocHandle(SQL_HANDLE_ENV,NULL,&m_hEnviroment);
//申請各種句柄都靠這個函數,參數1是要申請句柄的類型,參數2為申請該句柄依靠的句柄(老大沒依靠,所以是NULL),申請結果在參數3中保存
//返回值代表著執行的意義,如下面判斷,SUCCESS_WITH_INFO相當于是警告,雖然成功了,但是可能有問題
if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
? ? {
? ? ? ? return false;
? ? }
? ??
? ? SQLSetEnvAttr(m_hEnviroment,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3,SQL_IS_INTEGER);
? ? l_uiReturn = SQLAllocHandle(SQL_HANDLE_DBC,m_hEnviroment,&m_hDatabaseConnection);
? ? if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
? ? {
? ? ? ? return false;
? ? }
? ? SQLWCHAR * l_swcaDsnName = _T("");//數據源名稱
? ? SQLWCHAR * l_swcaUserName = _T("");//用戶名稱
? ? SQLWCHAR * l_swcaPassWord = _T("");//密碼
? ? l_uiReturn = SQLConnect(m_hDatabaseConnection,l_swcaDsnName,SQL_NTS
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ,l_swcaUserName,SQL_NTS
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ,l_swcaPassWord,SQL_NTS);
? ? ? ?if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
? ? {
? ? ? ? return false;
? ? }
復制代碼
使用SQL語句
復制代碼
? ? /* 申請于句句柄 */
? ? SQLRETURN l_uiReturn = SQLAllocHandle(SQL_HANDLE_STMT,m_hDatabaseConnection,&m_hStatement);
? ? if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
? ? {
? ? ? ? return 0;
? ? }
? ??
? ? /* 構造SQL語句 */
? ? CString l_cstrSql;
? ? l_cstrSql.Format(_T("SELECT * FROM 數據表 "));
? ? /* 執行SQL語句 */
? ? l_uiReturn = SQLExecDirect(m_hStatement,l_cstrSql.GetBuffer(),SQL_NTS);
? ? if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
? ? {
? ? ? ? return 0;
? ? }
? ? /* 獲得返回結果的行數 */
? ? SQLINTEGER l_siIdCount = 0;
? ? l_uiReturn = SQLRowCount(m_hStatement,&l_siIdCount);
? ? /* 開始讀取結果 *///讀取第一行時要調用,以后依次調用就可以下移行數,直到不返回SQL_SUCCESS
? ? l_uiReturn = SQLFetch(m_hStatement);
? ? if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
? ? {
? ? ? ? return 0;
? ? }
? ? SQLINTEGER l_siID;
? ? SQLINTEGER l_siIDLength = 0;
? ? /* 獲得數據 */
? ? SQLGetData(m_hStatement,1,SQL_C_ULONG,&l_siID,0,&l_siIDLength);
? ? //參數1為執行語句的句柄,參數2為所要得到的數據位于的列數(SQL語句中),參數3為數據類型,這個比較多,需要看一下MSDN
? ? //參數4為保存的位置(地址),參數5為參數4可用的位置,既然參數3已設定為長整型,所以這里可使用0
? ? //參數6為實際返回的長度
? ? /* 釋放語句句柄 */
? ? SQLFreeHandle(SQL_HANDLE_STMT,m_hStatement);
復制代碼
斷開連接
? ? SQLFreeHandle(SQL_HANDLE_STMT,m_hStatement);
? ? SQLFreeHandle(SQL_HANDLE_DBC,m_hDatabaseConnection);
? ? SQLFreeHandle(SQL_HANDLE_ENV,m_hEnviroment);
后記:客戶需求與實現方式的思考
客戶來了新的需求-交換:當時的原型為,他們那里有一套嵌入式的系統負責從傳感器上獲得數據,他們也有一套相應的管理程序,但是但是那套管理程序缺乏顯示效果,所以需要利用我們所做的系統進行顯示。
根據這個需求,我們(其實只有我一個人)進行了分析,當時客戶提出的最好使用SOCKET變成來實現,按說這也是一個不錯的主意,自由且不是很復雜,但是帶來的后果就可能是以后如果各種交互多了以后,極難維護。所以覺得使用一個共同的數據庫比較好,他們負責生成數據,我們負責讀取并顯示,這種好處是可以最大的運用程序的效率,不用總是考慮SOCKET能不能及時的接收到,而且責任分明,也方便維護,但是現在考慮這種方式的缺點就是,我們的代碼有一定的重復,他們的客戶端就有一些根據數據條件進行“分類”的意思,我們這邊則要根據數據進行不同的顯示效果。
========
使用ODBC API讀取Decimal或者Numeric
http://www.cnblogs.com/geminiv/archive/2012/09/21/2697264.html前言
前些天成功的連接并簡單操作了一下數據庫,誰知在第二天做些小動作的時候卻碰到了不小的麻煩,就是數據庫中標記為Decimal或者Numeric的數據都讀取不了,這可就麻煩大了,先后在SQLGetData中換了SQL_C_DOUBLE、SQL_C_FLOAT兩種種類型都無功而返,直到后來發現了,還有SQL_C_NUMERIC這個東西,一瞬間就很興奮,結果照著MSDN的介紹使用了,還是不大行。
改變
后來就繼續查資料了,結果在stackflow上面找到了一篇很好的文章(牛人果然多呀),正好是介紹這一點的,MSDN也還是非常強大的:
http://support.microsoft.com/kb/222831
簡單來說,核心的地方就是在執行完SQL語句后,要設置數據的屬性,具體的代碼如下:
復制代碼
/* 這一部分和上面一樣 */
SQLRETURN l_uiReturn = SQLAllocHandle(SQL_HANDLE_STMT,m_hDatabaseConnection,&m_hStatement);
? ? if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
? ? {
? ? ? ? return l_oRetval;
? ? }
? ? CString l_cstrSql;
? ? l_cstrSql.Format(_T("SELECT [float] FROM [數據源名稱].[所屬者].[data] ORDER BY [float] DESC"));
? ? l_uiReturn = SQLExecDirect(m_hStatement,l_cstrSql.GetBuffer(),SQL_NTS);
? ? if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
? ? {
? ? ? ? return l_oRetval;
? ? }
? ? /* 該代碼段用于配置當前語句相關數據列的數據格式 */ //這里開始進入重點?
? ? #pragma region
? ? SQLHDESC l_hDesc; //第四種句柄出現了
? ? l_uiReturn = SQLGetStmtAttr(m_hStatement, SQL_ATTR_APP_ROW_DESC,&l_hDesc, 0, NULL);//獲得SQL語句的屬性
? ? if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
? ? {
? ? ? ? return l_oRetval;
? ? }
? ? /* Float數據格式,對應數據庫中設置格式為DECIMAL(5,2) */
? ? l_uiReturn = SQLSetDescField (l_hDesc,1,SQL_DESC_TYPE,(VOID*)SQL_C_NUMERIC,0);
? ? l_uiReturn = SQLSetDescField (l_hDesc,1,SQL_DESC_PRECISION,(VOID*) 5,0);
? ? l_uiReturn = SQLSetDescField (l_hDesc,1,SQL_DESC_SCALE,(VOID*) 2,0);
#pragma endregion
? ? l_uiReturn = SQLFetch(m_hStatement);
? ? if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
? ? {
? ? ? ? return l_oRetval;
? ? }
? ? SQL_NUMERIC_STRUCT l_tFloat;
? ? SQLINTEGER l_siLength = 0;
/* 獲得相應的浮點數形數據 */
? ? SQLGetData(m_hStatement,1,SQL_ARD_TYPE,&l_tFloat,sizeof(l_tFloat),&l_siLength);
//這里一定要使用SQL_ARD_TYPE,意思是要使用我們上面設置好的數據格式 ?SQLFreeHandle(SQL_HANDLE_DESC,l_hDesc);
? ? SQLFreeHandle(SQL_HANDLE_STMT,m_hStatement);
復制代碼
然后調試,開始看l_tFloat中的數據,結果發現基本上不懂,所以還是繼續看文章吧,在其下方建立了一個函數,負責數據轉化,所以重寫了一個自己的版本并理解了一下,簡單的來說,這是一個16進制浮點數據轉化的過程:
復制代碼
double GetDoubleFromHexStruct(SQL_NUMERIC_STRUCT & p_rtNumeric)
{
? ??
? ? long l_lValue =0;
? ? int l_iLastVal = 1,l_iCurrentVal = 0;
? ? int l_iLsd = 0 ,l_iMsd = 0;
? ? for(int i = 0;i < 16 != 0;i++)
? ? {
? ? ? ? l_iCurrentVal = (int) p_rtNumeric.val[i];
? ? ? ? l_iLsd = l_iCurrentVal % 16;
? ? ? ? l_iMsd = l_iCurrentVal / 16;
? ? ? ? l_lValue += l_iLastVal * l_iLsd; ? ?
? ? ? ? l_iLastVal = l_iLastVal * 16; ? ?
? ? ? ? l_lValue += l_iLastVal * l_iMsd;
? ? ? ? l_iLastVal = l_iLastVal * 16; ? ?
? ? }
? ? long l_lDivisor = 1;
? ? for (int i =0;i < p_rtNumeric.scale;i++)
? ? {
? ? ? ? l_lDivisor = l_lDivisor * 10;
? ? }
? ? return (double)(l_lValue/(double)l_lDivisor);
? ??
}
將l_tFloat經過這個函數就可以順利得到我們想要的值了,雖然在數值上會有差距,但基本上是由于計算機表達浮點數的誤差導致的,這個就誰也沒辦法了。
思考
可以看到,其實上面的代碼還是挺復雜的,尤其是當面對不停的用戶需求變化,這種方式簡直就有點像在自殺,每個固定的SQL語句的地方都要進行修改,雖然現在維護的程序需要數據庫的方面較少,但是抱著未雨綢繆的思想,還是提前準備一下比較好,結果就是,沒準備出來。按說ODBC API已經被MFC給面向對象過了,理論上是肯定可以實現的,但奈何自己還是沒那個本事,當時是因為看上了SQLGetDescField這個函數,我就想看一看到底能否獲得數據格式,按說由于數據庫這些設計雖然不一定讓改,但是查詢數據格式的功能應該是向外提供的,但是弄了半天,這個函數一直返回一個SQL_NO_DATA的錯誤,整整一個下午的時間也沒弄明白。
總的來說,自己還是對數據庫的知識不扎實,MSDN上的長篇描述也是讓自己很頭疼,希望如果有了解這方面的人可以告訴我一下,我也想簡單的封裝一下API,以此來熟悉數據庫的駕馭方式。
========
使用C語言來操作SQL SERVER數據庫
http://blog.csdn.net/chen_swe/article/details/45438409? ? ? ? ? ? ? ? ? ? ? ? http://simpledev.iteye.com/blog/339537
1.使用C語言來操作SQL SERVER數據庫,采用ODBC開放式數據庫連接進行數據的添加,修改,刪除,查詢等操作。
?step1:啟動SQLSERVER服務,例如:HNHJ,開始菜單 ->運行 ->net start mssqlserver
?step2:打開企業管理器,建立數據庫test,在test庫中建立test表(a varchar(200),b varchar(200))
?step3:建立系統DSN,開始菜單 ->運行 ->odbcad32,
? 添加->SQL SERVER
?名稱:csql,服務器:HNHJ
?使用用戶使用登錄ID和密碼的SQLSERVER驗證,登錄ID:sa,密碼:?
? 更改默認的數據庫為:test
?...
?測試數據源,測試成功,即DNS添加成功。
2.cpp文件完整代碼
//##########################save.cpp##########################
C++代碼 ?收藏代碼
[cpp] view plain copy
#include <stdio.h> ? ? ??
#include <string.h> ? ? ??
#include <windows.h> ? ? ??
#include <sql.h> ? ? ??
#include <sqlext.h> ? ? ??
#include <sqltypes.h> ? ? ??
#include <odbcss.h> ? ? ??
? ??
SQLHENV henv = SQL_NULL_HENV; ? ? ??
SQLHDBC hdbc1 = SQL_NULL_HDBC; ? ? ??
SQLHSTMT hstmt1 = SQL_NULL_HSTMT; ? ? ??
? ??
/* ?
? ? cpp文件功能說明: ?
? ? 1.數據庫操作中的添加,修改,刪除,主要體現在SQL語句上 ?
? ? 2.采用直接執行方式和參數預編譯執行方式兩種 ?
*/ ? ?
int main(){ ? ? ??
? ? RETCODE retcode; ? ? ??
? ? UCHAR ? szDSN[SQL_MAX_DSN_LENGTH+1] ? = ? "csql", ? ? ??
? ? ? ? ? ? szUID[MAXNAME] ? = ? "sa", ? ? ??
? ? ? ? ? ? szAuthStr[MAXNAME] ? = ? ""; ? ? ?
? ? //SQL語句 ? ?
? ? ? ? //直接SQL語句 ? ?
? ? UCHAR ? sql[37] = "insert into test values('aaa','100')"; ? ?
? ? ? ? //預編譯SQL語句 ? ?
? ? UCHAR ? pre_sql[29] = "insert into test values(?,?)"; ? ?
? ? //1.連接數據源 ? ?
? ? ? ? //1.環境句柄 ? ?
? ? retcode ? = ? SQLAllocHandle ? (SQL_HANDLE_ENV, ? NULL, ? &henv); ? ? ??
? ? retcode ? = ? SQLSetEnvAttr(henv, ? SQL_ATTR_ODBC_VERSION, ? ? ??
? ? ? ? ? ? ? ? ? (SQLPOINTER)SQL_OV_ODBC3, ? ? ??
? ? ? ? ? ? ? ? ? SQL_IS_INTEGER); ? ? ??
? ? ? ? //2.連接句柄 ? ? ?
? ? retcode ? = ? SQLAllocHandle(SQL_HANDLE_DBC, ? henv, ? &hdbc1); ? ? ??
? ? retcode ? = ? SQLConnect(hdbc1, ? szDSN, ? 4, ? szUID, ? 2, ? szAuthStr, ? 0); ? ? ? ?
? ? //判斷連接是否成功 ? ?
? ? if ? ( ? (retcode ? != ? SQL_SUCCESS) ? && ? (retcode ? != ? SQL_SUCCESS_WITH_INFO) ? ) ? { ? ? ? ??
? ? ? ? printf("連接失敗!\n"); ? ?
? ? } ? else ? { ? ? ??
? ? ? ? //2.創建并執行一條或多條SQL語句 ? ?
? ? ? ? /* ?
? ? ? ? 1.分配一個語句句柄(statement handle) ?
? ? ? ? 2.創建SQL語句 ?
? ? ? ? 3.執行語句 ?
? ? ? ? 4.銷毀語句 ?
? ? ? ? */ ? ?
? ? ? ? retcode ? = ? SQLAllocHandle(SQL_HANDLE_STMT, ? hdbc1, ? &hstmt1); ? ? ??
? ? ? ? //第一種方式 ? ?
? ? ? ? //直接執行 ? ?
? ? ? ? //添加操作 ? ?
? ? ? ? //SQLExecDirect (hstmt1,sql,37); ? ?
? ? ? ? ? ??
? ? ? ? //第二種方式 ? ?
? ? ? ? //綁定參數方式 ? ?
? ? ? ? char a[200]="bbb"; ? ?
? ? ? ? char b[200]="200"; ? ?
? ? ? ? SQLINTEGER ? p ? = ? SQL_NTS; ? ?
? ? ? ? //1預編譯 ? ?
? ? ? ? SQLPrepare(hstmt1,pre_sql,29); //第三個參數與數組大小相同,而不是數據庫列相同 ? ?
? ? ? ? //2綁定參數值 ? ?
? ? ? ? SQLBindParameter(hstmt1,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,200,0,&a,0,&p); ? ?
? ? ? ? SQLBindParameter(hstmt1,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,200,0,&b,0,&p); ? ?
? ? ? ? //3 執行 ? ?
? ? ? ? SQLExecute(hstmt1); ? ?
? ? ? ? ? ??
? ? ? ? printf("操作成功!"); ? ?
? ? ? ? //釋放語句句柄 ? ?
? ? ? ? SQLCloseCursor (hstmt1); ? ?
? ? ? ? SQLFreeHandle (SQL_HANDLE_STMT, hstmt1); ? ?
? ? ? ??
? ? } ? ? ??
? ? //3.斷開數據源 ? ?
? ? /* ?
? ? ?1.斷開與數據源的連接. ?
? ? ?2.釋放連接句柄. ?
? ? ?3.釋放環境句柄 (如果不再需要在這個環境中作更多連接) ?
? ? */ ? ?
? ? SQLDisconnect(hdbc1); ? ? ? ?
? ? SQLFreeHandle(SQL_HANDLE_DBC, hdbc1); ? ? ??
? ? SQLFreeHandle(SQL_HANDLE_ENV, henv); ? ? ??
? ? return(0); ? ? ??
} ? ??
?
//##########################list.cpp##########################
C代碼 ?收藏代碼
[cpp] view plain copy
#include <stdio.h> ? ??
#include <string.h> ? ??
#include <windows.h> ? ??
#include <sql.h> ? ??
#include <sqlext.h> ? ??
#include <sqltypes.h> ? ??
#include <odbcss.h> ? ??
??
SQLHENV henv = SQL_NULL_HENV; ? ??
SQLHDBC hdbc1 = SQL_NULL_HDBC; ? ??
SQLHSTMT hstmt1 = SQL_NULL_HSTMT; ? ??
??
/*?
? ? 查詢SQLSERVER數據庫,1.條件查詢,2.直接查詢全部?
*/ ?
int main(){ ? ??
? ? RETCODE retcode; ? ??
? ? UCHAR ? szDSN[SQL_MAX_DSN_LENGTH+1] ? = ? "csql", ? ??
? ? ? ? ? ? szUID[MAXNAME] ? = ? "sa", ? ??
? ? ? ? ? ? szAuthStr[MAXNAME] ? = ? ""; ? ?
? ? UCHAR ? sql1[39] = "select b from test where a = 'aaa'"; ?
? ? UCHAR ? sql2[35] = "select b from test where a = ? "; ?
? ? UCHAR ? sql3[19] = "select b from test"; ?
? ? ??
? ? retcode ? = ? SQLAllocHandle ? (SQL_HANDLE_ENV, ? NULL, ? &henv); ? ??
? ? retcode ? = ? SQLSetEnvAttr(henv, ? SQL_ATTR_ODBC_VERSION, ? ??
? ? ? ? ? ? ? ? ? (SQLPOINTER)SQL_OV_ODBC3, ? ??
? ? ? ? ? ? ? ? ? SQL_IS_INTEGER); ? ? ?
? ? retcode ? = ? SQLAllocHandle(SQL_HANDLE_DBC, ? henv, ? &hdbc1); ? ??
? ? //1.連接數據源 ?
? ? retcode ? = ? SQLConnect(hdbc1, ? szDSN, ? 4, ? szUID, ? 2, ? szAuthStr, ? 0); ? ? ?
? ? if ? ( ? (retcode ? != ? SQL_SUCCESS) ? && ? (retcode ? != ? SQL_SUCCESS_WITH_INFO) ? ) ? { ? ??
? ? ? ? printf("連接失敗!"); ?
? ? } ? else ? { ? ??
? ? ? ? //2.創建并執行一條或多條SQL語句 ?
? ? ? ? /*?
? ? ? ? 1.分配一個語句句柄(statement handle)?
? ? ? ? 2.創建SQL語句?
? ? ? ? 3.執行語句?
? ? ? ? 4.銷毀語句?
? ? ? ? */ ?
? ? ? ? retcode ? = ? SQLAllocHandle(SQL_HANDLE_STMT, ? hdbc1, ? &hstmt1); ? ??
? ? ? ? //第一種方式 ?
? ? ? ? /*?
? ? ? ? //直接執行?
? ? ? ? SQLExecDirect (hstmt1,sql1,39);?
? ? ? ? char list[5];?
? ? ? ? SQLBindCol(hstmt1, 1, SQL_C_CHAR, list, 5, 0);?
? ? ? ? SQLFetch(hstmt1);?
? ? ? ? printf("%s\n",list);?
? ? ? ? */ ?
? ? ? ? ??
? ? ? ? //第二種方式 ?
? ? ? ? /*?
? ? ? ? //綁定參數方式?
? ? ? ? char a[200]="aaa";?
? ? ? ? SQLINTEGER ? p ? = ? SQL_NTS;?
? ? ? ? //1.預編譯?
? ? ? ? SQLPrepare(hstmt1,sql2,35); //第三個參數與數組大小相同,而不是數據庫列相同?
? ? ? ? //2.綁定參數值?
? ? ? ? SQLBindParameter(hstmt1,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,200,0,&a,0,&p);?
? ? ? ? //3.執行?
? ? ? ? SQLExecute(hstmt1);?
? ? ? ? char list[5];?
? ? ? ? SQLBindCol(hstmt1, 1, SQL_C_CHAR, list, 5, 0);?
? ? ? ? SQLFetch(hstmt1);?
? ? ? ? printf("%s\n",list);?
? ? ? ? */ ?
??
? ? ? ? //第三種方式全部輸出 ?
? ? ? ? /*?
? ? ? ? 1.確認一個結果集是否可用。?
? ? ? ? 2.將結果集的列綁定在適當的變量上。?
? ? ? ? 3.取得行?
? ? ? ? */ ?
? ? ? ? //3.檢查結果記錄(如果有的話) ?
? ? ? ? SQLExecDirect (hstmt1,sql3,19); ?
? ? ? ? char list[5]; ?
? ? ? ? SQLBindCol(hstmt1, 1, SQL_C_CHAR, list, 5, 0); ?
? ? ? ? do{ ?
? ? ? ? ? ? retcode = SQLFetch(hstmt1); ?
? ? ? ? ? ? if(retcode == SQL_NO_DATA){ ?
? ? ? ? ? ? ? ? break; ?
? ? ? ? ? ? } ?
? ? ? ? ? ? printf("%s\n",list); ?
? ? ? ? }while(1); ?
? ? ? ? ??
? ? ? ? //釋放語句句柄 ?
? ? ? ? SQLCloseCursor (hstmt1); ?
? ? ? ? SQLFreeHandle (SQL_HANDLE_STMT, hstmt1); ?
? ? ??
? ? } ? ??
? ?
? ? //4.斷開數據源 ?
? ? /*?
? ? ?1.斷開與數據源的連接.?
? ? ?2.釋放連接句柄.?
? ? ?3.釋放環境句柄 (如果不再需要在這個環境中作更多連接)?
? ? */ ?
? ? SQLDisconnect(hdbc1); ? ? ?
? ? SQLFreeHandle(SQL_HANDLE_DBC, hdbc1); ? ??
? ? SQLFreeHandle(SQL_HANDLE_ENV, henv); ? ??
? ? return(0); ? ??
} ?
========
ODBC:直接調用 ODBC API 函數
https://msdn.microsoft.com/zh-cn/library/1dwe8111.aspx?
數據庫類提供的到數據源的接口比 ODBC 提供的更為簡單。 因此,這些類不封裝全部的 ODBC API。 對于這些類的功能以外的任何功能,都必須直接調用 ODBC API 函數。 例如,您必須直接調用 ODBC 目錄函數(::SQLColumns、::SQLProcedures、::SQLTables 和其他一些函數)。
System_CAPS_ICON_note.jpg 說明
通過 MFC ODBC 類(如本主題所述)或通過 MFC 數據訪問對象 (DAO) 類,都可以訪問 ODBC 數據源。
若要直接調用 ODBC API 函數,必須采取的步驟和在無框架情況下調用所采取的步驟相同。 這些步驟是:
為調用返回的任何結果分配存儲空間。
根據函數的參數簽名,傳遞 ODBC HDBC 或 HSTMT 句柄。 使用 AFXGetHENV 宏檢索 ODBC 句柄。
因為成員變量 CDatabase::m_hdbc 和 CRecordset::m_hstmt 是可用的,所以不需要您親自分配和初始化這些變量。
也許還要調用其他的 ODBC 函數為主調用做準備或者跟在主調用之后。
調用完成后,解除分配存儲空間。
有關這些步驟的更多信息,請參見 MSDN 文檔中的開放式數據庫連接 (ODBC) SDK。
除這些步驟之外,您需要采取另外一些步驟檢查函數返回值,確保您的程序不是在等待完成一個異步調用等。 可以通過使用 AFX_SQL_ASYNC 和 AFX_SQL_SYNC 宏簡化最后的這些步驟。 有關更多信息,請參見《MFC 參考》中的宏和全局。
請參閱
ODBC 基礎
========
總結
以上是生活随笔為你收集整理的ODBC API 学习总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SPY++ 学习总结
- 下一篇: 体感开发学习总结 - 二