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.html

1.      数据类型:
通过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 学习总结相关推荐

  1. 应用ODBC API建立应用程序

    虽然直接应用ODBC API编制应用程序相对来说较为繁琐,但是,由于直接使用ODBC API编写的程序相对要简洁.高效.所以,我们有必要学习直接使用ODBC API编程. 看图之王ACDSee 6.0 ...

  2. Crypto API 学习笔记一

    标 题: [原创]Crypto API 学习笔记一 作 者: jdxyw 时 间: 2006-09-01,16:47 链 接: http://bbs.pediy.com/showthread.php? ...

  3. IOS 14.5版本之解档和归档的API学习

    IOS 14.5版本之解档和归档的API学习 第一部分 回顾一下老api的使用,将对象持久化至硬盘里面. 1.为什么我们要学习解档和归档, 有什么作用.当 plist 文件存储无法满足我们的需求的时候 ...

  4. libvirt API学习笔记

    为环境CentOS5.5 从官方网站上下载了文档   libvirt 0.7.5  Application  Development Guide 由于CentOS自带libvirt版本为0.6.3的, ...

  5. Netty 学习之旅:ByteBuf 篇之 ByteBuf 内部结构与 API 学习

    1.原生 ByteBuffer.ByteBuf 内部结构设计 首先我们来回顾一下 java.nio.ByteBuffe r的内部结构设计. ByteBuffer 内部持有一个 byte[] bt, 再 ...

  6. javascript:常用API学习Math.random, toString,slice(),substr(),Math.ceil()

    javascript:常用API学习 1.获得随机数:Math.random() 如何随机获得整数? 2.如何转进制:十进制转二进制?: 变量名.toString(进制数) 3. 36进制:能把一个小 ...

  7. 图形API学习工程(10):基础光照

    工程GIT地址:https://gitee.com/yaksue/yaksue-graphics 目标 在<图形API学习工程(6):创建并使用UniformBuffer>中,Unifor ...

  8. openGL之API学习(一八九)gl_Position gl_Vertex

    gl_Position是顶点着色器(所有版本的顶点着色器)必须计算的值,是每个顶点的最终的位置信息.这里的顶点位置信息是通过glVertexAttribPointer上传的. #version 430 ...

  9. SIEMENS PLC Web API 学习记录

    SIEMENS PLC Web API 学习记录 为顺应时代发展,西门子为 S7-200SMART/1200/1500 PLC 添加IT通讯所需要的 Web API.(由于S7-200 SMART 新 ...

最新文章

  1. 求幂运算、多项式乘法及Horner法则的应用
  2. 带你学python基础:文件读写,俗称IO操作
  3. 数据产品必知的4层技术知识
  4. php页头滚动文字公告,jQuery公告栏文字滚动插件
  5. 利用Python进行数据分析(1) 简单介绍
  6. java帐篷_Java多线程之 Park和Unpark(十四)
  7. 搜狐提出畅游私有化要约 后者收盘涨近50%
  8. 用PHP写距离圣诞节还有多久,距离圣诞节还有多少天
  9. python基本语法总结(超级全面,细致,只用一周就可以入门python到实践),会持续更新
  10. 雷电模拟器android4.2,雷电安卓模拟器-雷电模拟器下载 v4.0.55.0官方版--pc6下载站...
  11. python 正则表达式语法大全_Python正则表达式知识汇总
  12. 【Python框架】Scrapy简单入门及实例讲解
  13. 【技巧】Windows 10系统连接共享打印机报错0x00000709、0x0000007c、0x0000011b
  14. icloud验证失败连接服务器时出现问题,登录 iCloud 提示验证失败连接到服务器时出现问题怎么办及苹果iPhone手机安装两个微信教程...
  15. 颜宁:学术圈问题很多,也不分国籍,希望年轻一代守住底线
  16. diy 单片机 自动浇花_基于单片机的自动浇花系统
  17. Android 架构之路 (1)-- Android 客户端与服务器的数据交互总结
  18. 边缘风行视频采集软件V1.0
  19. 命令行 查看自己的系统版本
  20. ArcGIS版本高级应用 数据归档(历史归档 存档数据)

热门文章

  1. Spring Boot与Docker||Docker基本使用、Docker环境||安装Docker
  2. 实验15:通过注解分别创建Dao、Service、Controller★
  3. Jquery判断元素是否隐藏:display属性状态值
  4. 使用 MWC V2.5 中的 MPU6050中的DMP进行计算姿态(转载)
  5. scipy模块计算导数方法(central_diff_weights)
  6. 第七周实践项目3 负数把正数赶出队列
  7. SDUT_1299 最长上升子序列
  8. 三次样条插值Python实现
  9. mfc cedit 默认显示内容_拼多多评价不显示的几大原因,看完之后才知道是触碰了这几个环节...
  10. MongoDB复制集与Raft协议异同点分析