ODBC API 开发教程 - 闻怡洋

ODBC API 开发教程 - 闻怡洋
ODBC API 开发教程 - 闻怡洋

ODBC API介绍

目录

第 1 章介绍 (2)

第 2 章ODBC API访问数据库 (2)

2.1ODBC简要介绍 (2)

2.1.1在没有ODBC以前 (2)

2.1.2ODBC介绍 (3)

2.1.3ODBC结构 (3)

2.1.4ODBC的一致性 (4)

2.2使用ODBC进行数据库开发基本知识介绍 (4)

2.2.1建立ODBC DSN (4)

2.2.2使用ODBC所需要的文件 (6)

2.2.3SQL语句执行方式介绍 (6)

2.2.4获取SQL语句执行的结果 (7)

2.2.5程序执行的基本流程图 (8)

2.2.6数据类型定义 (9)

2.2.7ODBC句柄 (11)

2.3为本章的例程创建DSN与数据库表 (11)

2.4ODBC的基本功能介绍 (11)

2.4.1所需要了解的ODBC API (11)

2.5ODBC的其他功能介绍 (18)

2.5.1ODBC连接句柄的参数设置 (18)

2.5.2ODBC语句句柄的参数设置 (19)

2.5.3ODBC中使用可以滚动的光标 (20)

2.5.4存储过程的执行与参数的绑定 (21)

2.5.5SQL的准备与执行 (28)

2.5.6通过列绑定获取字段数据 (30)

2.5.7ODBC中BLOB(Binary Large Object)字段数据的处理 (31)

2.5.8ODBC对事务的支持 (34)

2.5.9多线程 (34)

2.5.10SQL语句的异步执行 (34)

第 3 章结束语 (35)

第 1 章介绍

在文章的开头做一个习惯性的介绍。

本文从2002年11月开始写,基本上在2002年12月时完成,当时本来作为一本书的一个章节,后来由于某些原因没有完成该书。这段时间将本文内容进行了一些整理,放在网上希望能够给大家一些帮助。

本文的内容主要是关于ODBC 的功能,所有内容都与ODBC 3.X版本兼容。

本文简要介绍了ODBC的历史和发展,也介绍了ODBC的基本的常用功能。大致包括:

●使用ODBC进行数据库连接

●利用ODBC直接执行SQL语句

●ODBC光标类型介绍

●利用滚动光标或非滚动光标进行结果集查询

●存储过程的调用与参数绑定

●SQL语句的准备执行方式

●BLOB数据字段的查询和修改

本文的数据库利用了MS SQL Server,ODBC在使用时是与数据库无关的所以所有例程都可以运行在其他数据库上,例如Oracle。其实利用Access数据库来进行练习也是可以的,但是由于Access不能支持存储过程,所以我没有使用Access数据库。

由于例程代码没有找到,所以没有就没有办法提供,但是文中的代码都比较详细而且有具体的解释。

书中有很多错误和不足之处希望大家能够容忍和包含,也欢迎来信指出。

闻怡洋 2003年07月01日

https://www.360docs.net/doc/387391335.html,

第 2 章ODBC API访问数据库

2.1ODBC简要介绍

2.1.1在没有ODBC以前

请允许我将那时候成为第二黑暗时代,第一黑暗时代是没有数据库的时代。

ODBC的出现结束了数据库开发的无标准时代。在没有ODBC以前不同的数据库的开发所采用的标准是不统一的。一般来讲不同的数据库厂商都有自己的数据库开发包,这些开发包支持两种模式的数据库开发:预编译的嵌入模式(例如Oracle的ProC,SQL Server的ESQL)和API调用(例如Oracle的OCI)。

对于一个开发人员来讲使用预编译方式开发是极其痛苦的,我就有过这样的经历,所有的SQL语句要写在程序内部,并且遵守一定的规则,然后由数据库厂商的预编译工具处理后形成C代码,最后由C编译器进行编译。预编译的最大问题就在于无法动态的生成SQL语句,我想作为一个程序员是很难接受的。

接下来的是使用API进行开发,和预编译相比算是前进了一大步。数据库厂商提供了开发包,你通过各种API函数就可以连接数据库,执行查询、修改、删除,操纵光标,执行存储过程等。对于程序员来讲有了更多的自由,而且可以创建自己的开发包。但是这一切的开发只能针对同一种数据库。

Oracle的OCI是一个非常优秀的C语言开发包,在ODBC中就在很多地方参照了OCI的设计。

2.1.2ODBC介绍

ODBC(Open Database Connectivity)是由微软公司提出的一个用于访问数据库的统一界面标准,随着客户机/服务器体系结构在各行业领域广泛应用,多种数据库之间的互连访问成为一个突出的问题,而ODBC成为目前一个强有力的解决方案。ODBC之所以能够操作众多的数据库,是由于当前绝大部分数据库全部或部分地遵从关系数据库概念,ODBC看待这些数据库时正是着眼了这些共同点。虽然支持众多的数据库,但这并不意味ODBC会变得复杂,ODBC是基于结构化查询语言(SQL),使用SQL可大大简化其应用程序设计接口(API),由于ODBC思想上的先进性,而且没有同类标准或产品与之竞争,因而越来越受到众多厂家和用户的青睐。目前,ODBC已经成为客户机/服务器系统中的一个重要支持技术。

在1994年时ODBC有了第一个版本,这种名为Open Data Base Connection(开放式数据库互连)的技术很快通过了标准化并且得到各个数据库厂商的支持。ODBC在当时解决了两个问题,一个是在Windows平台上的数据库开发,另一个是建立一个统一的标准,只要数据厂商提供的开发包支持这个标准,那么开发人员通过ODBC开发的程序可以在不同的数据库之间自由转换。这对开发人员来说的确值得庆贺。

ODBC参照了X/OpenData Management: SQL Call-Level Interface和ISO/ICE1995 Call-Level Interface标准,在ODBC 版本3.X中已经完全实现了这两个标准的所有要求。所以本书所有内容都基于ODBC 3.0以上版本。

最开始时支持ODBC的数据库只有SQL Server,ACCESS,FoxPro,这些都时微软的产品,他们能够支持ODBC一点也不奇怪,但是那时候Windows的图形界面已经成为了客户端软件最理想的载体,所以各大数据厂商也在不久后发布了针对ODBC的驱动程序。

在Windows 3.X和Windows 95的时候ODBC并不作为系统的组成部分出现,使用前必须另行安装。但到了Windows 98的时候,当你安装好操作系统后,ODBC不需要另行安装了,因为它已经成为了操作系统的一部分。这对很多拒绝ODBC的人来说又少了一个借口。

作为一个程序员,至少是我,我实在找不出什么理由不为ODBC欢呼。此外ODBC的结构很简单和清晰,学习和了解ODBC的机制和开发方法对学习ADO等其他的数据库访问技术会有所帮助。

2.1.3ODBC结构

图2.1显示了ODBC的结构。

图2.1

应用程序(Application)

应用程序本身不直接与数据库打交道,主要负责处理并调用ODBC函数,发送对数据库的SQL请求及取得结果。

驱动程序管理器(Driver Manager )

驱动程序管理器是一个带有输入程序的动态链接库(DLL),主要目的是加载驱动程序,处理ODBC调用的初始化调用,提供ODBC调用的参数有效性和序列有效性。

驱动程序(Driver)

驱动程序是一个完成ODBC函数调用并与数据库相互影响的DLL,这些驱动程序可以处理对于特定的数据的数据库访问请求。对于应用驱动程序管理器送来的命令,驱动程序再进行解释形成自己的数据库所能理解的命令。驱动程序将处理所有的数据库访问请求,对于应用程序来讲不需要关注所使用的是本地数据库还上网络数据库。

2.1.4ODBC的一致性

ODBC接口的优势之一为互操作性,程序设计员可以在不指定特定数据源情况下创建ODBC应用程序。从应用程序角度方面,为了使每个驱动程序和数据源都支持相同的ODBC函数调用和SQL语句集,ODBC接口定义了一致性级别,即ODBC API一致性和ODBC SQL语法一致性。SQL一致性规定了对SQL语句语法的要求,而API一致性规定了驱动程序需要实现的ODBC函数。一致性级别通过建立标准功能集来帮助应用程序和驱动程序的开发者,应用程序可以很容易地确定驱动程序是否提供了所需的功能,驱动程序可被开发以支持应用程序选项,而不用考虑每个应用程序的特定请求。

2.2使用ODBC进行数据库开发基本知识介绍

2.2.1建立ODBC DSN

DSN(Data Source Name)是用于指定ODBC与相关的驱动程序相对应的一个入口,所有DSN的信息由系统进行管理,一般来讲当应用程序要使用ODBC访问数据库时,就需要指定一个DSN以便于连接到一个指定的ODBC驱动程序。

在控制面板中打开ODBC管理器,回看到如图2.2的界面。

图2.2

DSN共分为三类:

●用户DSN:对当前登录用户可见,只能够用于当前计算机。

●系统DSN:对当前系统上所有用户可见,包括NT中的服务。

●文件DSN:DSN信息存放在文件中,对能够访问到该文件的用户可见。

一个使用Access数据库的DSN中的信息如下:

[ODBC]

DRIVER=Driver do Microsoft Access (*.mdb)

UID=admin

DefaultDir=C:\https://www.360docs.net/doc/387391335.html,\DB

DBQ=C:\https://www.360docs.net/doc/387391335.html,\DB\chat.mdb

对于文件DSN来讲这些信息存放在文件中,对于用户DSN和系统DSN来讲这些信息存放在注册表内。你可以通过创建文件DSN来查看每种DSN对应的信息内容。

下面的例子将告诉你如何添加一个SQL Server的DSN。

图2.3

图2.3中的四个步骤分别是:

●选择SQL Server作为驱动程序

●输入DSN名称和SQL Server服务器地址或别名

●输入用户和口令进行连接

●选择默认数据库并完成

2.2.2使用ODBC所需要的文件

你需要下面的文件:

●sql.h:包含有基本的ODBC API的定义。

●sqlext.h:包含有扩展的ODBC的定义。

●odbc32.lib:库文件。

这些文件在VC6,VC7都已经随开发工具提供了,不需要另外安装。

此外所有的ODBC函数都以SQL开始,例如SQLExecute,SQLAllocHandle。

2.2.3SQL语句执行方式介绍

在ODBC中SQL语句的执行方式分为两种,直接执行和准备执行。

直接执行是指由程序直接提供SQL语句,例如:Select * from test_table并调用SQLExecDirect执行,准备执行是指先提供一个SQL语句并调用SQLPrepare,然后当语句准备好后调用SQLExecute执行前面准备好的语句。准备执行多用于数据插入和数据删除,在进行准备时将由ODBC驱动程序对语句进行分析,在实际执行时可以避免进行SQL语句分析

所花费的时间,所以在进行大批量数据操作时速度会比直接执行有明显改善。在后面的章节中我会详细介绍准备执行与行

列绑定与参数替换的用法。

2.2.4获取SQL语句执行的结果

对于SQL查询语句,ODBC会返回一个光标,与光标对应的是一个结果集合(可以理解为一个表格)。开发人员利用光标来浏览所有的结果,你可以利用ODBC API函数移动光标,并且获取当前光标指向的行的列字段的数值。此外还可以通过光标来对光标当前所指向的数据进行修改,而修改会直接反映到数据库中。

对于数据更新语句,如插入,删除和修改,在执行后可以得到当前操作所影响的数据的行数。

2.2.5程序执行的基本流程图

图2.4

图2.4中是一个基本的使用ODBC API的一个流程,你现在并不理解上面所有的函数的作用,这没有关系。但希望能够通过这幅图给你一个最初的映象,那就是使用ODBC API开发并不复杂。

2.2.6数据类型定义

在使用ODBC开发时一个重要的问题就是数据转换的问题,在ODBC中存在下面的几类数据:

●数据库中SQL语言表达数据的类型

●ODBC中表达数据的类型

●C语言中表达数据的类型

在程序运行过程中数据需要经历两次转换:C语言的数据或结构类型与ODBC的数据类型的转换,ODBC与SQL间数据类型的转换。所以ODBC所定义的数据类型起到了中间桥梁的作用,在ODBC的驱动程序调用自己的DBMS数据库访问接口时就需要对数据类型进行转换。我们所需要关注的是C语言的数据类型和ODBC数据类型间的转换关系。

从下图中可以看到ODBC中定义的数据类型和SQL语言中数据类型的对应关系,所以通过下表我们可以将ODBC 和SQL语言间的数据一一对应,在后面的文字中我们不再区分ODBC数据类型和SQL语言数据类型。

使用C/C++语言开发,那么必定会在与ODBC语言间存在数据的转换的问题,因为ODBC所存在的一些数据类型在C语言中是不存在的。在ODBC以宏定义的方式定义了C语言和ODBC中使用的数据类型:

所以在ODBC的开发过程中不要使用int , float 之类的C语言的实际类型来定义变量而应该使用ODBC定义的数据类型来定义变量,如:SQLINTEGER,SQLFLOAT。

2.2.7ODBC句柄

ODBC中的句柄分为三类:环境句柄,数据库连接句柄,SQL语句句柄。

通过图2.4看出,在使用ODBC功能时必须先申请环境句柄,然后在环境句柄的基础上创建数据库连接,最后在数据连接的基础上执行SQL语句。

2.3为本章的例程创建DSN与数据库表

为了后面的例子能够顺利执行,请创建一个名称为“test”的DSN,并且使用下面的语句在数据库中创建表和插入基本的数据,这个例子和以后的例子中我们使用SQL Server作为数据库,你需要连接到SQL Server上执行下面的语句来创建表和插入数据。

Create table test_t1(iID int primary key , tmJoin datetime , szName varchar(40) ,fTall float );

Insert into test_t1 values(1, '2002-1-1 15:25' , 'user_1',1.56 );

Insert into test_t1 values(2, '2002-1-2 12:25' , 'user_2',1.53 );

Insert into test_t1 values(3, '2002-1-3 13:25' , 'user_3',1.76 );

2.4ODBC的基本功能介绍

2.4.1所需要了解的ODBC API

2.4.1.1SQLAllocHandle 创建ODBC句柄

SQLRETURN SQLAllocHandle(

SQLSMALLINT HandleType,

SQLHANDLE InputHandle,

SQLHANDLE * OutputHandlePtr);

第一个参数HandleType的取值可以为:

●SQL_HANDLE_ENV:申请环境句柄。

●SQL_HANDLE_DBC:申请数据库连接句柄。

●SQL_HANDLE_STMT:申请SQL语句句柄,每次执行SQL语句都申请语句句柄,并且在执行完成后释放。

第二个参数为输入句柄,第三个参数为输出句柄,也就是是你在第一参数指定的需要申请的句柄。

根据 1.2.7节的说明,在使用ODBC功能时必须先申请环境句柄,然后在环境句柄的基础上创建数据库连接,最后在数据连接的基础上执行SQL语句。所以可能的调用方式有三种。

SQLAllocHandle(SQL_HANDLE_ENV,NULL,&hEnv);

SQLSetEnvAttr(henv, SQL_A TTR_ODBC_VERSION,(SQLPOINTER) SQL_OV_ODBC3, SQL_IS_INTEGER);

SQLAllocHandle(SQL_HANDLE_DBC,hEnv,&hDBC);

SQLAllocHandle(SQL_HANDLE_STMT,hDBC,&hSTMT);

请注意,在创建环境句柄后请务必调用:

SQLSetEnvAttr(henv, SQL_A TTR_ODBC_VERSION,(SQLPOINTER) SQL_OV_ODBC3, SQL_IS_INTEGER);

将ODBC设置成为版本3,否则某些ODBC API 函数不能被支持。

2.4.1.2ODBC API的返回值

ODBC API的返回值定义为:SQLRETURN。在成功时返回值为:SQL_SUCCESS, SQL_SUCCESS_WITH_INFO;在失败时返回错误代码。

一点需要注意的是如果ODBC返回值为:SQL_SUCCESS_WITH_INFO并不表明执行完全成功,而是表明执行成功但是带有一定错误信息。当执行错误时ODBC返回的是一个错误信息的结果集,你需要遍历结果集合中所有行,这点和后面讲到的查询SQL语句执行结果集的思路很类似。

在ODBC可以利用SQLGetDiagRec来得到错误描述信息:

SQLRETURN SQLGetDiagRec(

SQLSMALLINT HandleType,

SQLHANDLE Handle,

SQLSMALLINT RecNumber,

SQLCHAR * Sqlstate,

SQLINTEGER * NativeErrorPtr,

SQLCHAR * MessageText,

SQLSMALLINT BufferLength,

SQLSMALLINT * TextLengthPtr);

RecNumber:指明需要得到的错误状态行,从1开始逐次增大。

Sqlstate,NativeErrorPtr,MessageText:返回错误状态,错误代码和错误描述。

BufferLength:指定MessageText的最大长度。

TextLengthPtr:指定返回的MessageText中有效的字符数。

函数的返回值可能为:SQL_SUCCESS,SQL_SUCCESS_WITH_INFO,SQL_ERROR,SQL_INV ALID_HANDLE,SQL_NO_DATA。在没有返回错误的情况下你需要反复调用此函数,并顺次增大RecNumber参数的值,直到函数返回SQL_NO_DATA,以得到所有的错误描述。

示例,得到STMT句柄上的错误信息:

SQLCHAR SqlState[6],SQLStmt[100],Msg[SQL_MAX_MESSAGE_LENGTH];

SQLINTEGER NativeError;

SQLSMALLINT i, MsgLen;

int i = 1;

while ((rc2 = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)

{

//显示错误的代码

i++;

}

下面的函数来自MS文档,用于显示一个错误的详细情况,你可以在你的程序中直接使用:

void ProcessLogMessages(

SQLSMALLINT plm_handle_type,

//出现错误时所使用的ODBC句柄类型,取值为:SQL_HANDLE_ENV ,SQL_HANDLE_DBC ,

SQL_HANDLE_STMT

SQLHANDLE plm_handle, //出现错误时所使用的ODBC句柄

char *logstring, //标题字符串

int ConnInd //指明句柄是否为DBC句柄

)

{

RETCODE plm_retcode = SQL_SUCCESS;

UCHAR plm_szSqlState[MAXBUFLEN] = "",

plm_szErrorMsg[MAXBUFLEN] = "";

SDWORD plm_pfNativeError = 0L;

SWORD plm_pcbErrorMsg = 0;

SQLSMALLINT plm_cRecNmbr = 1;

SDWORD plm_SS_MsgState = 0, plm_SS_Severity = 0;

SQLINTEGER plm_Rownumber = 0;

USHORT plm_SS_Line;

SQLSMALLINT plm_cbSS_Procname, plm_cbSS_Srvname;

SQLCHAR plm_SS_Procname[MAXNAME], plm_SS_Srvname[MAXNAME];

printf(logstring);

while (plm_retcode != SQL_NO_DA TA_FOUND) {

plm_retcode = SQLGetDiagRec(plm_handle_type, plm_handle,

plm_cRecNmbr, plm_szSqlState, &plm_pfNativeError,

plm_szErrorMsg, MAXBUFLEN - 1, &plm_pcbErrorMsg);

// Note that if the application has not yet made a

// successful connection, the SQLGetDiagField

// information has not yet been cached by ODBC

// Driver Manager and these calls to SQLGetDiagField

// will fail.

if (plm_retcode != SQL_NO_DATA_FOUND) {

if (ConnInd) {

plm_retcode = SQLGetDiagField(

plm_handle_type, plm_handle, plm_cRecNmbr,

SQL_DIAG_ROW_NUMBER, &plm_Rownumber,

SQL_IS_INTEGER,

NULL);

plm_retcode = SQLGetDiagField(

plm_handle_type, plm_handle, plm_cRecNmbr,

SQL_DIAG_SS_LINE, &plm_SS_Line,

SQL_IS_INTEGER,

NULL);

plm_retcode = SQLGetDiagField(

plm_handle_type, plm_handle, plm_cRecNmbr,

SQL_DIAG_SS_MSGSTA TE, &plm_SS_MsgState, SQL_IS_INTEGER,

NULL);

plm_retcode = SQLGetDiagField(

plm_handle_type, plm_handle, plm_cRecNmbr,

SQL_DIAG_SS_SEVERITY, &plm_SS_Severity,

SQL_IS_INTEGER,

NULL);

plm_retcode = SQLGetDiagField(

plm_handle_type, plm_handle, plm_cRecNmbr,

SQL_DIAG_SS_PROCNAME, &plm_SS_Procname, sizeof(plm_SS_Procname),

&plm_cbSS_Procname);

plm_retcode = SQLGetDiagField(

plm_handle_type, plm_handle, plm_cRecNmbr,

SQL_DIAG_SS_SRVNAME, &plm_SS_Srvname,

sizeof(plm_SS_Srvname),

&plm_cbSS_Srvname);

}

printf("szSqlState = %s\n",plm_szSqlState);

printf("pfNativeError = %d\n",plm_pfNativeError);

printf("szErrorMsg = %s\n",plm_szErrorMsg);

printf("pcbErrorMsg = %d\n\n",plm_pcbErrorMsg);

if (ConnInd) {

printf("ODBCRowNumber = %d\n", plm_Rownumber); printf("SSrvrLine = %d\n", plm_Rownumber);

printf("SSrvrMsgState = %d\n",plm_SS_MsgState);

printf("SSrvrSeverity = %d\n",plm_SS_Severity);

printf("SSrvrProcname = %s\n",plm_SS_Procname);

printf("SSrvrSrvname = %s\n\n",plm_SS_Srvname);

}

}

plm_cRecNmbr++; //Increment to next diagnostic record. } // End while.

}

2.4.1.3SQLConnect 连接数据库

SQLRETURN SQLConnect(

SQLHDBC ConnectionHandle,

SQLCHAR * ServerName,

SQLSMALLINT NameLength1,

SQLCHAR * UserName,

SQLSMALLINT NameLength2,

SQLCHAR * Authentication,

SQLSMALLINT NameLength3);

ConnectionHanlde:为DBC句柄,也就是前面提到到利用:

SQLAllocHandle(SQL_HANDLE_DBC,hEnv,&hDBC);申请的句柄。

ServerName:为ODBC的DSN名称。

NameLength1:指明参数ServerName数据的长度。

UserName:数据库用户名。

NameLength2:指明参数UserName数据的长度。

Authentication:数据库用户密码。

NameLength3:指明参数Authentication数据的长度。

关于ServerName,UserName,Authentication参数长度可以直接指定也可以指定为SQL_NTS表明参数是以NULL字符结尾。

示例代码:

retcode = SQLConnect(hdbc, (SQLCHAR*) "odbc_demo", SQL_NTS,(SQLCHAR*) "user", SQL_NTS, (SQLCHAR*) "password", SQL_NTS);

2.4.1.4SQLExecDirect 直接执行SQL语句

SQLRETURN SQLExecDirect(

SQLHSTMT StatementHandle,

SQLCHAR * StatementText,

SQLINTEGER TextLength);

StatementHandle:SQL语句句柄,也就是前面提到的利用:

SQLAllocHandle(SQL_HANDLE_STMT,hDBC,&hSTMT);申请的句柄。

StatementText:SQL语句。

TextLength:参数StatementText的长度,可以使用SQL_NTS表示字符串以NULL字符结尾。

如果函数执行成功,你将会得到一个结果集,否则将返回错误信息。

SQLExecDirect函数除可以执行Select 语句外,还可以执行Insert,Update,Delete 语句,在执行修改SQL语句后可以利用SQLRowCount 函数来得到被更新的记录的数量。

2.4.1.5SQLFetch 移动光标

SQLRETURN SQLFetch(SQLHSTMT StatementHandle);

在你调用SQLExecDirect执行SQL语句后,你需要遍历结果集来得到数据。StatementHandle是STMT句柄,此句柄必须是被执行过。

当调用SQLFetch 函数后,光标会被移动到下一条记录处,当光标移动到记录集的最后一条,函数将会返回SQL_NO_DATA。

要遍历所有的结果集可以利用下面的方法:

while(SQL_NO_DA TA != SQLFetch(hSTMT)) //移动光标,一直到集合末尾

{//得到结果

}

2.4.1.6SQLGetData 得到光标处的某列的值

SQLRETURN SQLGetData(

SQLHSTMT StatementHandle,

SQLUSMALLINT ColumnNumber,

SQLSMALLINT TargetType,

SQLPOINTER TargetValuePtr,

SQLINTEGER BufferLength,

SQLINTEGER * StrLen_or_IndPtr);

StatementHanlde:STMT句柄。

ClumnNumber:列号,以1开始。

TargetType:数据缓冲区(TargetValuePtr)的C语言数据类型,请参照图2.6。

BufferLength:数据缓冲区(TargetValuePtr)的长度。

StrLen_or_IndPtr:返回当前得到的字段的字节长度。

下面是通过SQLFetch和SQLGetData得到记录集的例子:

//假设 SQL = SELECT CUSTID, NAME, PHONE FROM CUSTOMERS

SQLINTEGER sCustID

SQLCHAR szName[50], szPhone[50];

SQLINTEGER cbName, cbAge, cbBirthday;//用来保存得到的数据的长度

while (TRUE) {//循环得到所有行

retcode = SQLFetch(hstmt);//移动光标

if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {

printf(“error SQLFetch\n”);

}

if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){

/*得到当前光标处每列的值 */

SQLGetData(hstmt, 1, SQL_C_ULONG, &sCustID, 0, &cbCustID);

//此处并没有指明BufferLength参数的值,是因为数据类型是定长的LONG型

SQLGetData(hstmt, 2, SQL_C_CHAR, szName, 50, &cbName);

SQLGetData(hstmt, 3, SQL_C_CHAR, szPhone, 50,&cbPhone);

printf(out, "%5d %s %s", sCustID, szName, szPhone);

} else {

break;

}

}

SQLGetData的另一个用处就是用于得到一些变长字段的实际长度,例如V ARCHAR字段,TEXT字段。例如:SQLGetData(hstmt, 2, SQL_C_CHAR, szName, 0, &cbName);

当你将BufferLength 参数置为0,则会在 StrLen_or_IndPtr 参数中返回字段的实际长度。但请注意第四个参数必须是一个合法的指针,不能够为NULL。

此外在得到字段的值时还存在一个数据类型转换的问题,比如说数据库内的字段类型为:INTEGER,那么你可以使用SQL_C_INTEGER,SQL_C_CHAR,SQL_C_ULONG数据类型来得到该字段的值。图2.7就说明了所有可能存在的转换关系。

图2.7

参考前面的图2.5和图2.6就可以看出ODBC可以在SQL的数据类型和C的数据类型间提供转换。

请注意我讲解图2.5时提到的时ODBC数据类型和SQL语言数据类型之间的对应关系,而这里提到的是数据之间的转换关系。

图中用圆点标出的交叉点为允许的类型转换,其中实心圆点标出的是默认的数据转换类型,空心圆点标出的是允许的数据转换类型(允许并不表明一定可以,例如Char类型可以转换为SQL_C_INTEGER,但是并不是总能成功,当字符为?123?时可以转换为整数123,而字符为?odbc?时就不能成功)。例如数据库中的Char,VarChar类型默认都是对应SQL_C_CHAR类型。你还可以看到所有的数据库字段类型都可以转换为SQL_C_CHAR类型,例如整数123可以转换为”123”,而浮点数1.23可以转换为”1.23”,日期的2002年10月1日可以转换为”2002-10-1”。所以在初学ODBC并且对性能要求不是非常高时可以用字符类型来得到数据库字段的值,这样做会比较方便。

2.4.1.7SQLNumResultCols 得到结果集中列数

SQLRETURN SQLNumResultCols(

SQLHSTMT StatementHandle,

SQLSMALLINT * ColumnCountPtr);

StatementHandle:STMT句柄

ColumnCountPtr:返回列数

2.4.1.8SQLDescribeCol 得到结果集中列的描述

SQLRETURN SQLDescribeCol(

SQLHSTMT StatementHandle,

SQLSMALLINT ColumnNumber,

SQLCHAR * ColumnName,

SQLSMALLINT BufferLength,

SQLSMALLINT * NameLengthPtr,

SQLSMALLINT * DataTypePtr,

SQLUINTEGER * ColumnSizePtr,

SQLSMALLINT * DecimalDigitsPtr,

SQLSMALLINT * NullablePtr);

StatementHandle:STMT句柄。

ColumnNumber:需要得到的列的序号,从1开始计算。

ColumnName:得到列的名称。

BufferLength:指明ColumnName参数的最大长度。

NameLengthPtr:返回列名称的长度。

DataTypePtr:得到列的ODBC数据类型,请参照图2.5。

ColumnSizePtr:得到列的长度。

DecimalDigitsPtr:当该列为数字类型时返回小数点后数据的位数。

NullablePtr:指明该列是否允许为空值。

2.4.1.9SQLRowCount 执行SQL语句后得到影响的行数

SQLRETURN SQLRowCount(

SQLHSTMT StatementHandle,

SQLINTEGER * RowCountPtr);

你可以通过SQLExecDirect执行SQL语句来插入,修改和删除数据,在执行插入,修改和删除的SQL语句后就可以通过SQLRowCount函数来得到被影响的数据的行数。

Insert into test_t1 values(4, '2002-1-4 11:25' , 'user_4',1.86 );

2.5ODBC的其他功能介绍

2.5.1ODBC连接句柄的参数设置

在上一小节中提到了SQLSetConnectAttr 这个函数,这里需要对这个函数进行一些简单的讲解,你可以通过调用SQLSetConnectAttr 在数据库连接建立或建立后设置连接的一些属性。

SQLSetConnectAttr的函数原型如下:

SQLRETURN SQLSetConnectAttr(

SQLHDBC ConnectionHandle,

SQLINTEGER Attribute,

SQLPOINTER ValuePtr,

SQLINTEGER StringLength);

ConnectionHandle:提供DBC连接句柄。

Attribute:指定需要设置的属性类型,在这里设置为值SQL_ATTR_AUTOCOMMIT。

ValuePtr:提供参数值。

StringLength:指定参数的长度,当参数为整数是设置为SQL_IS_INTEGER,当参数为字符串时设置为字符串长度或者为SQL_NTS 。

这里讲一下常用的参数Attribute可能的取值和ValuePtr对应的取值:

2.5.2ODBC语句句柄的参数设置

如同数据库连接句柄一样,语句句柄也可以设置参数。函数为:

SQLRETURN SQLSetStmtAttr(

SQLHSTMT StatementHandle,

SQLINTEGER Attribute,

SQLPOINTER ValuePtr,

SQLINTEGER StringLength);

ConnectionHandle:提供STMT连接句柄。

Attribute:指定需要设置的属性类型,在这里设置为值SQL_ATTR_AUTOCOMMIT。

ValuePtr:提供参数值。

StringLength:指定参数的长度,当参数为整数是设置为SQL_IS_INTEGER,当参数为字符串时设置为字符串长度或者为SQL_NTS 。

这里讲一下常用的参数Attribute可能的取值和ValuePtr对应的取值:

2.5.3ODBC中使用可以滚动的光标

2.5.

3.1ODBC光标类型

从上面的函数SQLSetStmtAttr可以看到我们在ODBC中可以使用不同的光标类型,那么这些光标之间有什么区别。

●向前光标:SQL_CURSOR_FORWARD_ONLY,光标仅仅向前滚动。

●静态光标:SQL_CURSOR_STATIC,结果集的数据是静态的,这就是说明在执行查询后,返回的结果集的数据

不会再改变,即使是有其他程序更新了数据库中的记录,结果集中的记录也不会发生改变。

●动态光标:SQL_CURSOR_DYNAMIC,在光标打开以后,当结果集中的行所对应的数据值发生变化时,其变

化能够能够反映到光标所对应的结果集上,这些变化包括:字段的修改,添加,结果集中行的顺序变化。但是请注意如果行别删除则无法在当前结果集中反映出,因为被删除的行不再出现在当前的结果集中。动态光标所对应的结果集在数据发生变化时会被重建。例如,假设动态光标已获取到了两行,然后,另一应用程序更新了这两行中的一行,并删除了另一行,如果动态游标再试图获取那些行,它将不能检测已删除的行(因为当前结果集中只有一行,但是不要利用这个办法去检测被删除的行,因为出现这种情况还可能是因为行的数据被改变后不能再满足查询条件),而是返回已更新行的新值。

●键集光标:SQL_CURSOR_KEYSET_DRIVEN,和上面的动态光标所不同的是键集光标能够检测到行的删除和

修改,但是无法检测到检测到行的添加和结果集顺序变化。因为在光标创建时就创建了整个结果集,结果集合中记录和顺序已经被固定,这一点和静态光标一样。所以键集光标可以说是一种介于静态光标和动态光标之间的光标类型。

需要说明的是并不是每个DBMS的ODBC驱动程序都能够支持所有的这几种光标,具体情况可以通过SQLGetInfo 函数进行查询。

2.5.

3.2利用可滚动光标进行查询

前面介绍的SQLFetch函数只能够让光标向前移动,但在很多时候我们需要光标能够前后移动。我们需要利用另一个函数SQLFetchScroll,但是再这之前请利用SQLSetStmtAttr正确设置光标类型。SQLFetchScroll的原型如下:

微信公众平台快速上手教程

微信公众平台快速上手教程Part1 注册账户与认证 刘子骏微信微博营销微信, 微信公众平台17Comments发表评论 这段时间越来越多朋友和客户开始关注微信,我收到大量咨询微信公众平台在哪里注册等相关问题,所以不如写篇教程出来,以后大家看教程就可以了,本教程会根据微信最新变动而不断修改和完善的。 注册前最好做好以下准备: 1、想一个容易给客户识别和方便记忆的中文名称,毕竟用户通过中文名称搜索的频率是最高的,而且最好是品牌名,或者是企业名称的缩写,例如广州火鹰信息科技有限公司,注册名就可以改成火鹰科技,这样方便记忆也方便查询。如果你想加大被搜索几率,可以在名称后面加入主营业务信息,例如卡斯玛男装或卡斯玛正装,这样用户搜男装或正装都有机会找到你。这里还有一点要特别注意的是微信公众平台的名称注册后是不能修改的,所以填写时一定要先谨慎考虑。 2、准备好身份证正背面的扫描件 3、设计好微信头像图片,一般300×300像素就可以了 然后就可以直接登录https://www.360docs.net/doc/387391335.html,/就可以开始注册。 点击注册按钮后进入注册界面

以前微信是强制要求使用QQ账户登陆注册的,现在改成了随便使用一个可以收发邮件的邮箱就可以注册了。完成这一步会提示需要到注册邮箱激活账户!

点击邮件的激活链接后,会出现以下页面,这也是微信公众账户注册的第三步,这就需要之前提到的身份证扫描件(特别提示一下,一个身份证可以注册5个帐号。),还需要用手机进行短信验证。该页面除了个人住址和单位地址是可以不填之外,其他都必须填写,而且资料一定要真实,否则微信日后检查发现资料有问题会随时有封号的危险,当你填写完成后,系统也会提醒说明的。

微信公众平台的Java的开发详解工程代码解析

说明: 本次的教程主要是对微信公众平台开发者模式的讲解,网络上很多类似文章,但很多都让初学微信开发的人一头雾水,所以总结自己的微信开发经验,将微信开发的整个过程系统的列出,并对主要代码进行讲解分析,让初学者尽快上手。 在阅读本文之前,应对微信公众平台的官方开发文档有所了解,知道接收和发送的都是xml格式的数据。另外,在做内容回复时用到了,这是一个自然语言解析的开放平台,可以帮我们解决整个微信开发过程中最困难的问题,此处不多讲,下面会有其详细的调用方式。 在登录微信官方平台之后,开启开发者模式,此时需要我们填写url和token,所谓url就是我们自己服务器的接口,用来实现,相关解释已经在注释中说明,代码如下: [java] 1.package; 2.import 3.import 4.import 5.import 6.import 7.import 8.import 9.import 10.import 11.import 12./** 13.*微信服务端收发消息接口 14.* 15.*@authorpamchen-1 16.* 17.*/ 18.publicclass WechatServlet extends HttpServlet{ 19./** 20.*ThedoGetmethodoftheservlet.
21.*

22.*Thismethodiscalledwhenaformhasitstagvaluemethodequalstoget. 23.* 24.*@paramrequest 25.*therequestsendbytheclienttotheserver 26.*@paramresponse 27.*theresponsesendbytheservertotheclient 28.*@throwsServletException 29.*ifanerroroccurred 30.*@throwsIOException 31.*ifanerroroccurred 32.*/ 33.publicvoid doGet(HttpServletRequestrequest,HttpServletResponseresponse) 34.throws ServletException,IOException{ 35.("UTF-8"); 36.("UTF-8"); 37./**读取接收到的xml消息*/ 38.StringBuffersb=new StringBuffer(); 39.InputStreamis=(); 40.InputStreamReaderisr=new InputStreamReader(is,"UTF-8"); 41.BufferedReaderbr=new BufferedReader(isr); 42.Strings=""; 43.while((s=())!=null){ 44.(s); 45.} 46.Stringxml=();rocessWechatMag(xml); 47.} 48.try{ 49.OutputStreamos=(); 50.("UTF-8")); 51.(); 52.(); 53.}catch(Exceptione){ 54.(); 55.} 56.} 57./** 58.*ThedoPostmethodoftheservlet.
59.* 60.*Thismethodiscalledwhenaformhasitstagvaluemethodequalsto 61.*post. 62.* 63.*@paramrequest 64.*therequestsendbytheclienttotheserver 65.*@paramresponse

微信公众平台开发教程

微信公众平台视频教程 一、注册并登录接口(这里里以微小信平台https://www.360docs.net/doc/387391335.html,/WxUser/login 为 例) 二、添加公众号 如何获取微信初始ID? 企鹅:328258258获取微信开发教程激活码:https://www.360docs.net/doc/387391335.html,/course/79

页面上的gh_xxxxxxxxxxxx就是初始id,不过我想,这么难记的id,大家肯定都修改了。 但偏偏我们还需要使用它,比如我们要加入微信导航网站,比如我们要制作艺术二维码,比如我们要做微信互推等等,都是需要使用微信初识id的。 你修改时没保存吧,没关系的,我告诉大家如何查找微信初始id。 ?首先,登陆微信公众平台。 ?设置/个人设置。 ?下载二维码。 这里请不要使用右键另存为,一定要直接点击下载。 ?弹出对话框。

注意,图示中这个二维码的文件名是:qrcode_for_gh_d019da958e55_430.jpg 去头去尾,gh_d019da958e55就是该微信公众号的原始id。 或者找到你刚下载保存的文件。 也可以看到gh_xxxxxxxxxxxx的字样,复制该字段即可。 三、点配置接口

记下接口地址和Token,然后勾选下面列出的功能(在第七个部分会用到) 四、登录微信公众平台(https://www.360docs.net/doc/387391335.html,/),进行身份认证,填写信息,提交身份证。 五、认证后,点击高级功能→进入开发模式

六、点击"成为开发者"按钮 七、填写接口配置信息 url和Token写刚才记下的(在第4个步骤上记得)

八、确认开启 九、在手机上用微信给你的公众号输入"功能",测试你的接口是否配置正常!

微信开发第三方使用教程

平台功能操作说明 深圳纽扣时代微宝微信第三方开发平台拥有近百项功能,既包含常见的各类智能自动回复(图文、音频等形式)功能、营销活动推广模块、微网站和强大的会员卡系统,又涵盖了针对各个行业(餐饮、房产、汽车、婚庆、医疗、旅游等)的微信营销应用,另外系统还自带完善的粉丝经管和详细的数据统计分析功能,让用户可以方便的经管粉丝信息,并直观的看到各模块的数据统计、粉丝的行为分析结果、粉丝喜好和周期关注趋势对比等信息。这些功能为微信营销平台运营者提供了有力的市场竞争力。 平台功能目录 微宝CMS平台功能特点及优势错误!未定义书签。 微宝CMS平台设置操作3 微宝CMS平台功能页面7 (1)功能经管:8 (2)关注时的回复与帮助:8 (3)微信-文本回复:8 (4)微信-图文回复:8 (5)微信-语音回复:9 (6)自定义LBS回复10 (7)回答不上来的配置:10 3G网站设置10 (1) 首页回复配置10 (2) 分类经管10 (3) 模板经管:12 (4) 首页幻灯片:13 (5) 轮播背景图13 (6) 底部导航菜单13 (7) 自定义菜单14 (8) 留言板14 (9) 微论坛14 行业应用15 (1) 无线订餐15 (3) 婚庆喜帖17 (4) 3G微投票18 (5) 微汽车18 (6) 楼盘房产20 (7) 微教育22 (8) 微医疗:25

(9) 酒店宾馆:26 (10) 万能表单:26 电商系统30 (3)微信商城系统:32 (4) DIY宣传页39 (5) 3G图集:40 微用户经管CRM41 粉丝经管41 分组经管41 粉丝行为分析41 渠道二维码41 人工客服42 群发消息43 分享经管44 推广活动45 (1) 幸运大转盘:45 (2) 优惠券:46 (3) 刮刮卡47 (4) 幸运水果机:47 (5) 砸金蛋:48 (6) 祝福贺卡:48 (7) 摇一摇:48 (8) 微信墙:50 (9) 微信wifi:50 会员卡51 统计分析58

微信人生攻略之微信公众平台教程.

微信人生公众平台开发教程 2.1 如何注册微信公众号? 登录微信公众平台,点击注册填写相关信息即可申请微信公众号,微信公众号只能在PC端登录,不能用 手机端,公众号分服务号和订阅号,企业可申请服务号,个人、自媒体申请订阅号,服务号每个月限制群发一条,而订阅号每天可发送一次。 2.2 如何添加公众号? 登录微信人生后会提示一键升级或手动升级,将您的公众账号和密码填写上,即可实现升级。 注意: 1、公众号名称与微信公众平台公众号名称保持一致。 2、如果您是服务号,将您在公众平台申请的AppId和AppSecret填写到微信人生上去,提交完id和密钥 后,可以在【菜单设置】中设置各个菜单项,然后进行发布,您的微信公众号便支持自定义菜单了 2.3 首次关注 首次关注回复:指粉丝关注微信公众号后推送的第一条信息,可以是文本,也可以是图文,如果需要图文回复,点击切换到图文模式。 TIPS:如果想关注时默认进到微网站,可以不填写图文内容,然后在图文外链地址里面填写微官网首页地址,微官网首页地址在微官网设置里可以看见。 2.4 文本回复 文本回复指添加关键后回复的内容是文本。点击添加按钮 如上示例:定义了关键词你好,当用户在公众号发送“你好”的时候,将会回复“哈哈” 如果想定义多个关键词用英文逗号隔开,拒绝中文逗号,如:你好,在吗,哈哈 匹配类型分完全匹配和模糊匹配。 2.5 图文回复 点击素材管理,进入素材管理界面,选择单图文或多图文。 单图文是指只显示一个图文。填写触发关键词,图文标题、图文简介,上传图文封面。

地址点击图片跳转到的URL。 多图文是指在显示多条图文。 在这里多图文需要一条一条创建,点击图片上的“铅笔”符号,进行某一图文的编辑。 地址点击图片跳转到的URL。 添加完之后,回到关键字回复,回复类型选择图文,在图文资料中选择你要回复的图文即可。 注:素材管理是整个微信人生平台的素材库,关键字回复中的内容可以从素材的图文中选择。 2.6 自定义菜单 自定义菜单是企业微信公众号消息框底部的导航菜单,可以很方便用户进行交互,相当于一个轻量级的APP。 目前自定义菜单只对服务号开放,服务号认证需要企业认证,所以如果是订阅号这个功能是用不了的,如果服务号还没有申请自定义菜单,可以点击-高级功能-开发模式,会有一栏会话界面自定义菜单,点击申请就可以了,申请成功后的界面如下: 将AppId 和AppSecret填写在微信人生平台授权里 菜单设置:自定义菜单目前限制只能3个一级菜单,5个二级菜单,每一步设置后要保存菜单。 主菜单名称就是显示在消息框的一级菜单,可以回复文本或图文,图文信息在素材管理理设置好后,可以选择。 添加二级菜单,点击一级菜单后可以创建二级菜单,二级菜单只能同时启用5个。 点击保存菜单,保存自定义菜单设置,注意这时候并未生成自定义菜单,需要点发布自定义菜单。 2.7 LBS设置 用户点击“新增”,添加商家店面的信息,多个店面可添加多条LBS。 使用方法: 1、LBS数据:设置商铺的地理位置即可; 2、填写标题、店面封面、电话、简介、详细页内容。

相关文档
最新文档