ODBC API,odbc接口程序
ODBC API开发课程
作者:温逸阳
未经作者允许,请勿转载。
http://wyy.vchelp.net/
目录
第一章引言.2
第2章ODBC API对数据库的访问.2
2.1 ODBC简介.2
2 . 1 . 1 ODBC之前.2
2 . 1 . 2 ODBC简介.3
2.1.3 ODBC结构.3
2 . 1 . 4 odbc一致性.四
2.2利用ODBC进行数据库开发的基础知识介绍.四
2.2.1建立ODBC DSN.四
2.2.2使用ODBC所需的文件.6
2 . 2 . 3 SQL语句执行模式介绍.6
2.2.4获取SQL语句执行的结果.七
2.2.5程序执行的基本流程图.8
数据类型的定义.9
ODBC句柄.11
2.3为本章中的例程创建DSN和数据库表.11
2.4 ODBC基本功能介绍.11
2.4.1关于ODBC API 11您需要了解的内容
2.5 ODBC其他功能介绍.18
2 . 5 . 1 ODBC连接句柄的参数设置.18
2 . 5 . 2 ODBC语句句柄的参数设置.19
2.5.3在ODBC中使用滚动光标.20
2.5.4存储过程执行和参数的绑定.21
SQL的准备和执行.28
2.5.6通过列绑定获取字段数据.30
2 . 5 . 7 odbc中BLOB(二进制大对象)字段数据的处理.31
2.5.8 ODBC对事务的支持.34
多线程.34
SQL语句的异步执行.34
第三章结束语.35
第一章引言在文章开头做一个习惯性的介绍。
这篇文章写于2002年11月,2002年12月基本完成。本来是一本书的一章,但是因为某种原因,这本书没有完成。这段时间,我把这篇文章的内容整理了一下,放到网上,希望能给你一些帮助。
本文内容主要是关于ODBC的功能,都是兼容ODBC 3 . x版的。
本文简要介绍了ODBC的历史和发展,并介绍了ODBC的基本功能。大致包括:
使用ODBC进行数据库连接。
使用ODBC直接执行SQL语句。
odbc游标类型介绍
l使用滚动光标或非滚动光标来查询结果集。
l存储过程调用和参数绑定
准备和执行SQL语句的方法
l查询和修改blob数据字段
本文的数据库使用MS SQL Server,ODBC独立于数据库,所以所有的例程都可以运行在其他数据库上,比如Oracle。其实用Access数据库练习是可以的,但是因为Access不能支持存储过程,所以我没有用Access数据库。
因为没有找到套路代码,没有就没办法提供,但是文中的代码很详细,有具体的解释。
这本书有许多错误和缺点。希望你能包容和包容他们。也欢迎大家来信指出。
温宜阳2003年7月1日
http://www.vchelp.net/
第2.1章ODBC API访问数据库2.1 ODBC 2 . 1 . 1简介ODBC之前,请允许我把它称为第二个黑暗时代,第一个黑暗时代是没有数据库的时代。
ODBC的出现结束了数据库开发无标准的时代。在ODBC之前,不同数据库的开发标准并不统一。一般来说,不同的数据库厂商都有自己的数据库开发工具包,支持两种模式的数据库开发:预编译嵌入式模式(如Oracle的ProC,SQL Server的ESQL)和API调用(如Oracle的OCI)。
对于一个开发者来说,通过预编译进行开发是极其痛苦的。我有过这样的经历。所有的SQL语句都应该写在程序中,并遵守一定的规则。然后通过数据库厂商的预编译翻译工具进行处理,形成C代码,最后由C编译器进行编译。预编译最大的问题是不能动态生成SQL语句,我觉得作为程序员很难接受。
接下来就是使用API进行开发了,相比预编译是一大进步。数据库厂商提供开发包,你可以通过各种API函数连接数据库,进行查询、修改、删除、光标操作、存储过程执行等。程序员有更多的自由,可以创建自己的开发包。但这一切都只能针对同类数据库开发。
甲骨文的OCI是非常好的C语言开发工具包,ODBC中很多地方都引用了OCI的设计。
2 . 1 . 2 ODBC简介ODBC(开放式数据库连接)是微软公司提出的访问数据库的统一接口标准。随着客户机/服务器体系结构在各行各业的广泛应用,各种数据库之间的互连和访问成为一个突出的问题,ODBC成为目前一个强有力的解决方案。ODBC之所以能操作很多数据库,是因为现在的数据库大部分完全或部分遵循关系数据库的概念。当ODBC查看这些数据库时,它关注这些共同点。虽然支持很多数据库,但并不意味着ODBC会变得复杂。ODBC基于结构化查询语言(SQL),使用SQL可以大大简化其应用编程接口(API)。由于ODBC的思想先进,且没有类似的标准或产品与之竞争,因此越来越受到众多厂商和用户的青睐。目前,ODBC已经成为客户机/服务器系统的重要支撑技术。
1994年,ODBC有了第一个版本,这种被称为开放式数据库连接的技术很快被标准化,并得到了各种数据库供应商的支持。ODBC当时解决了两个问题,一是Windows平台上的数据库开发,二是建立统一的标准。只要数据供应商提供的开发包支持这一标准,开发人员通过ODBC开发的程序就可以在不同的数据库之间自动转换。这对于开发者来说真的是值得庆祝的事情。
参考ODBC x/open data management: SQL调用级接口和ISO/ICE1995调用级接口标准,这两个标准的所有要求在ODBC版本3中已经完全实现。X.所以这本书的所有内容都是基于ODBC版或以上。
当初只有SQL Server,ACCESS,FoxPro支持ODBC,都是微软的产品。他们能支持ODBC并不奇怪,但在那个时候,Windows的图形界面已经成为客户端软件最理想的载体,所以各大数据厂商也很快发布了ODBC的驱动。
在Windows 3中。x和Windows 95中,ODBC并没有作为系统的一个组成部分出现,使用前必须单独安装。但是到了Windows 98,你安装了操作系统之后,ODBC就不需要单独安装了,因为它已经成为操作系统的一部分了。这是很多拒绝ODBC的人的又一个借口。
作为一个程序员,至少我找不到任何不为ODBC喝彩的理由。另外,ODBC的结构非常简单明了。学习和了解ODBC的机制和开发方法,将有助于学习ADO等数据库访问技术。
2.1.3 ODBC结构图2.1显示了ODBC的结构。
图2.1
应用程序(应用程序)
应用程序本身并不直接处理数据库,主要负责处理和调用ODBC函数,向数据库发送SQL请求并获取结果。
司机经理(司机经理)
驱动程序管理器是一个带有输入程序的动态链接库(DLL)。其主要目的是加载驱动程序,处理ODBC调用的初始调用,提供ODBC调用的参数有效性和序列有效性。
司机(司机)
驱动程序是完成ODBC函数调用并与数据库交互的DLL。这些驱动程序可以处理特定数据的数据库访问请求。对于应用程序驱动程序管理器发送的命令,驱动程序解释它以形成它自己的数据库可以理解的命令。驱动程序将处理所有数据库访问请求,应用程序不需要关注它是本地数据库还是在线数据库。
2 . 1 . 4 ODBC的一致性ODBC接口的优点之一是互操作性,程序员可以创建ODBC应用程序,而不需要指定具体的数据源。从应用的角度来看,为了使每个驱动和数据源支持相同的ODBC函数调用和SQL语句集,ODBC接口定义了一致性级别,即ODBC API一致性和ODBC SQL语法一致性。SQL一致性指定了SQL语句语法的要求,而API一致性指定了驱动程序需要实现的ODBC函数。一致性通过建立一套标准的函数来帮助应用程序和驱动程序的开发者。应用程序可以很容易地确定驱动程序是否提供所需的功能,并且可以开发驱动程序来支持应用程序选项,而不考虑每个应用程序的特定请求。
2.2利用ODBC进行数据库开发的基础知识介绍2 . 2 . 1 ODBC DSNDSN(数据源名称)的建立是指定ODBC及相关驱动程序的条目。所有DSN信息都由系统管理。一般来说,当应用程序想要使用ODBC访问数据库时,需要指定一个DSN来连接到指定的ODBC驱动程序。在控制面板中打开ODBC管理器,会看到如图2.2所示的界面。
图2.2
DSN分为三类:
l用户DSN:对当前登录用户可见,只能用于当前计算机。
L system DSN:对当前系统上的所有用户可见,包括nt中的服务。
l文件DSN:DSN信息存储在一个文件中,对可以访问该文件的用户可见。
使用Access数据库的DSN中的信息如下:
[ODBC]
DRIVER=DRIVER do Microsoft Access(*。mdb)
UID=admin
default dir=C:/www . VC help . net/DB
DBQ=C:/www . VC help . net/DB/chat . MDB
对于文件DSN,此信息存储在文件中,对于用户DSN和系统DSN,此信息存储在注册表中。您可以通过创建文件DSN来查看每个DSN对应的信息内容。
以下示例将告诉您如何为SQL Server添加DSN。
图2.3
图2.3中的四个步骤是:
l选择SQL Server作为驱动程序。
输入DSN名称和SQL Server地址或别名。
l输入用户名和密码进行连接。
l选择默认数据库并完成。
2.2.2使用ODBC所需的文件您需要以下文件:
SQL。h:包含基本ODBC API的定义。
Sqlext.h:包含扩展ODBC的定义。
Lbc32.lib:库文件。
这些文件已随VC6和VC7中的开发工具一起提供,因此不需要单独安装它们。
另外,所有ODBC函数都是以SQL开头的,比如SQLExecute和SQLAllocHandle。
2 . 2 . 3 SQL语句执行方法介绍在ODBC中,SQL语句的执行方法有两种,直接执行和就绪执行。
直接执行是指程序直接提供SQL语句,例如Select * from test_table,调用SQLExecDirect执行。准备执行是指提供一条SQL语句并调用SQLPrepare,然后在语句准备就绪时调用SQLExecute执行之前准备好的语句。准备主要用于数据插入和数据删除。在准备的时候,ODBC驱动会对语句进行分析,可以避免实际执行中花费在SQL语句分析上的时间。所以大规模数据操作的速度会比直接执行有明显的提升。在后面的章节中,我将详细介绍准备执行行列绑定和参数替换的方法。
2.2.4获取SQL语句执行的结果。对于SQL查询语句,ODBC会返回一个游标,对应一个结果集(可以理解为一个表)。开发人员使用光标浏览所有结果。可以使用ODBC API函数移动光标,获取光标所指向的当前行的列字段的值。此外,您可以修改光标当前所指的数据,修改会直接反映到数据库中。
对于insert、delete、modify等数据更新语句,执行后可以得到当前操作影响的数据行数。
2.2.5程序执行的基本流程图
图2.4
图2.4显示了使用ODBC API的基本过程。以上功能你现在都不了解,所以没关系。但是希望能通过这张图给大家一个初步的形象,就是使用ODBC API的开发并不复杂。
2.2.6数据类型的定义使用ODBC开发时一个重要的问题就是数据转换的问题。ODBC中有以下类型的数据:
L数据库中用SQL语言表示的数据类型
用odbc表示的数据类型
用c语言表示的数据类型
在程序运行过程中,数据需要经历两次转换:C语言的数据或结构类型与ODBC的数据类型之间的转换,以及ODBC与SQL之间的转换。因此,ODBC定义的数据类型起到了中间桥梁的作用。当ODBC驱动程序调用其DBMS数据库访问接口时,需要转换数据类型。我们需要注意的是C语言数据类型和ODBC数据类型的转换关系。
从下图可以看出ODBC中定义的数据类型和SQL语言中的数据类型的对应关系,所以我们可以通过下表将ODBC和SQL语言一一对应,下面的文字中不再区分ODBC数据类型和SQL语言数据类型。
ODBC数据类型名称
SQL语言数据类型名称
SQL_CHAR
字符
SQL_VARCHAR
可变字符
SQL_LONGVARCHAR
LONG VARCHAR
SQL_WCHAR
WCHAR(北)
SQL_WVARCHAR
瓦罗查尔(北)
SQL _ WLONGVARCHAR
LONGWVARCHAR
SQL _小数
十进制
SQL _数字
数字(p,s)
SQL_SMALLINT
短整型
SQL_INTEGER
整数
SQL_REAL
真实的
SQL_FLOAT
浮动
SQL_DOUBLE
双倍精密度
SQL_BIT
少量
SQL_TINYINT
TINYINT
SQL_BIGINT
大整数
SQL_BINARY
二进制
SQL_VARBINARY
可变二进制数
SQL_LONGVARBINARY
LONG VARBINARY
SQL_TYPE_DATE[6]
日期
SQL_TYPE_TIME[6]
时间(p)
SQL _ TYPE _时间戳[6]
时间戳(p)
SQL_GUID
全局唯一标识符
图2.5
如果使用C/C语言进行开发,就会出现ODBC和ODBC之间的数据转换问题,因为ODBC中存在的一些数据类型在C语言中是不存在的。C语言和ODBC中使用的数据类型是由ODBC中的宏定义定义的:
c语言数据类型名称
ODBC数据类型定义
语言的实际类型
SQL_C_CHAR
SQLCHAR *
无符号字符*
SQL_C_SSHORT[j]
SQLSMALLINT
短整型
SQL_C_USHORT[j]
SQLUSMALLINT
无符号短整型
SQL_C_SLONG[j]
SQLINTEGER
长整型
SQL_C_ULONG[j]
SQLUINTEGER
无符号长整型
SQL_C_FLOAT
SQLREAL
漂浮物
SQL_C_DOUBLE
SQLDOUBLE,SQLFLOAT
两倍
SQL_C_BIT
SQLCHAR
无符号字符
SQL_C_STINYINT[j]
SQLSCHAR
有符号字符
SQL_C_UTINYINT
SQLCHAR
无符号字符
SQL_C_SBIGINT
SQLBIGINT
_int64[h]
SQL_C_UBIGINT
SQLUBIGINT
unsigned _int64[h]
SQL_C_BINARY
SQLCHAR *
无符号字符*
SQL _ C _书签[i]
书签
无符号长整型[d]
SQL_C_VARBOOKMARK
SQLCHAR *
无符号字符*
SQL_C_TYPE_DATE
SQL_DATE_STRUCT
结构标记日期_结构{
SQLSMALLINT年;
SQLUSMALLINT月份;
SQLUSMALLINT日;
} DATE _ STRUCT[a]
SQL_C_TYPE_TIME
SQL_TIME_STRUCT
结构标记时间_结构{
SQLUSMALLINT小时;
SQLUSMALLINT分钟;
SQLUSMALLINT秒;
} TIME _ STRUCT[a]
SQL _ C _ TYPE _时间戳[c]
SQL _时间戳_结构
结构标记时间戳_结构{
SQLSMALLINT年;
SQLUSMALLINT月份;
SQLUSMALLINT日;
SQLUSMALLINT小时;
SQLUSMALLINT分钟;
SQLUSMALLINT秒;
SQLUINTEGER分数;[b]
}时间戳_结构;[a]
SQL _ C _数字
SQL _数字_结构
结构标记SQL_NUMERIC_STRUCT {
SQLCHAR精度;
SQLSCHAR刻度;
SQLCHAR符号[g];
SQL char val[SQL _ MAX _ NUMERIC _ LEN];[英]、[法]
} SQL _ NUMERIC _ STRUCT
SQL_C_GUID
方法
struct标记SQLGUID {
双字数据1;
WORD Data2
WORD Data3
字节数据4[8];
} SQLGUID[k]
图2.6
所以在ODBC的开发过程中,不应该使用int、float等C语言的实际类型来定义变量,而应该使用ODBC定义的数据类型来定义变量,比如SQLINTEGER、SQLFLOAT。
2.2.7 ODBC句柄ODBC句柄分为三类:环境句柄、数据库连接句柄、SQL语句句柄。
图2.4说明了使用ODBC函数时,首先要申请环境句柄,然后基于环境句柄创建数据库连接,最后执行基于数据连接的SQL语句。
2.3为本章中的例程创建DSN和数据库表。为了顺利执行下面的示例,请创建一个名为“test”的DSN,并使用下面的语句在数据库中创建表和插入基本数据。在本示例和后面的示例中,我们使用SQL Server作为数据库。您需要连接到SQL Server并执行以下语句来创建表和插入数据。
创建表test_t1(iID int主键,tmJoin datetime,szName varchar(40),fTall float);
插入到test_t1值中(1, 2002-1-1 15:25 , user_1 ,1.56);
插入到test_t1值中(2, 2002-1-2 12:25 , user_2 ,1.53);
插入到test_t1值中(3, 2002-1-3 13:25 , user_3 ,1.76);
2.4 ODBC基本功能介绍2.4 ODBC API 2.4.1.1 SQLAllocHandle创建ODBC句柄SQLRETURN SQLAllocHandle(
SQLSMALLINT HandleType,
SQLHANDLE InputHandle,
SQL handle * OutputHandlePtr);
第一个参数HandleType的值可以是:
L SQL_HANDLE_ENV:申请环境句柄。
DBC:申请数据库连接句柄。
L SQL_HANDLE_STMT:申请SQL语句的句柄,每次执行SQL语句时申请,执行完毕后释放。
第二个参数是输入句柄,第三个参数是输出句柄,也就是您在第一个参数中指定要申请的句柄。
根据1.2.7节的说明,使用ODBC函数时,必须先申请一个环境句柄,然后基于环境句柄创建一个数据库连接,最后基于数据连接执行SQL语句。所以有三种可能的调用方式。
SQLAllocHandle(SQL_HANDLE_ENV,NULL,hEnv);
SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,(SQLPOINTER) SQL_OV_ODBC3,SQL _ IS _ INTEGER);
SQLAllocHandle(SQL_HANDLE_DBC,hEnv,hDBC);
SQLAllocHandle(SQL_HANDLE_STMT,hDBC,hSTMT);
请注意,创建环境句柄后,一定要调用:
SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,(SQLPOINTER) SQL_OV_ODBC3,SQL _ IS _ INTEGER);
将ODBC设置为版本3,否则将不支持某些ODBC API函数。
2.4.1.2 ODBC API的返回值ODBC API的返回值定义为:SQLRETURN。成功时,返回值为:SQL _ success,SQL _ success _ with _ info失败时会返回一个错误代码。
需要注意的是,如果ODBC返回值为:SQL_SUCCESS_WITH_INFO,并不代表执行完全成功,而是成功了,有一些错误信息。执行错误时,ODBC返回错误信息的结果集。需要遍历结果集中的所有行,类似于后面提到的查询SQL语句执行结果集的思路。
在ODBC中,可以使用SQLGetDiagRec来获取错误描述信息:
SQLRETURN SQLGetDiagRec(
SQLSMALLINT HandleType,
SQLHANDLE句柄,
SQLSMALLINT记录编号,
SQLCHAR * Sqlstate,
SQLINTEGER * NativeErrorPtr,
SQLCHAR * MessageText,
SQLSMALLINT缓冲区长度,
SQLSMALLINT * textlength ptr);
RecNumber:表示需要获取的错误状态行,从1递增到1。
Sqlstate,NativeErrorPtr,MessageText:返回错误状态、错误代码和错误描述。
Length:指定MessageText的最大长度。
TextLengthPtr:指定返回的MessageText中的有效字符数。
函数的返回值可能是:SQL_SUCCESS、SQL_SUCCESS_WITH_INFO、SQL_ERROR、SQL_INVALID_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)
{
//显示错误的代码
我;
}
以下函数来自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 //指明句柄是否为动态制动控制系统句柄
)
{
RETCODE PLM _ RETCODE=SQL _ SUCCESS;
UCHAR PLM _ szSqlState[MAXBUFLEN]= ,
PLM _ szErrorMsg[MAXBUFLEN]=" ";
SDWORD plm _ pfNativeError=0L
剑PLM _ pcbErrorMsg=0;
SQLSMALLINT PLM _ cRecNmbr=1;
SDWORD plm_SS_MsgState=0,PLM _ SS _ Severity=0;
SQL整数PLM _ row number=0;
USHORT plm _ SS _ Line
SQLSMALLINT plm_cbSS_Procname,plm_cbSS_Srvname .
SQL char PLM _ SS _ Procname[MAXNAME],PLM _ SS _ Srvname[MAXNAME];
printf(logstring);
while (plm_retcode!=SQL_NO_DATA_FOUND) {
PLM _ retcode=SQLGetDiagRec(plm_handle _ type,PLM _ handle,
plm_cRecNmbr,plm_szSqlState,plm_pfNativeError,
plm_szErrorMsg,MAXBUFLEN - 1,PLM _ pcbErrorMsg);
//请注意,如果应用程序尚未发出
//成功连接,SQLGetDiagField
开放式数据库连接性尚未缓存信息
//驱动程序管理器和这些对SQLGetDiagField的调用
//会失败。
if (plm_retcode!=SQL_NO_DATA_FOUND) {
if (ConnInd) {
plm_retcode=SQLGetDiagField(
plm_handle_type,plm_handle,plm_cRecNmbr,
SQL _ DIAG行号,plm _行号,
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 _消息状态,plm _ SS _消息状态
SQL_IS_INTEGER,
NULL);
plm_retcode=SQLGetDiagField(
plm_handle_type,plm_handle,plm_cRecNmbr,
SQL _ DIAG _ SS _严重性,plm _ SS _严重性,
SQL_IS_INTEGER,
NULL);
plm_retcode=SQLGetDiagField(
plm_handle_type,plm_handle,plm_cRecNmbr,
SQL _ DIAG _ SS _产品名称,plm _ SS _产品名称,
sizeof(plm_SS_Procname),
PLM _ cbSS _ Procname);
plm_retcode=SQLGetDiagField(
plm_handle_type,plm_handle,plm_cRecNmbr,
SQL_DIAG_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 _ row number);
printf(SSrvrLine=%d/n ,PLM _ row number);
printf(SSrvrMsgState=%d/n ,PLM _ SS _ msg状态);
printf( ssrvrsevery=% d/n ,PLM _ SS _ Severity);
printf(SSrvrProcname=%s/n ,PLM _ SS _ Procname);
printf(SSrvrSrvname=%s/n/n ,PLM _ SS _ Srvname);
}
}
plm _ cRecNmbr//增加到下一个诊断记录。
} //结束而.
}
2.4.1.3 SQLConnect连接数据库SQLRETURN SQLConnect(
连接句柄连接句柄,
SQLCHAR *服务器名,
SQLSMALLINT NameLength1,
SQLCHAR *用户名,
SQLSMALLINT NameLength2,
SQLCHAR *身份验证,
SQLSMALLINT名称长度3);
连接方式:为动态制动控制系统句柄,也就是前面提到到利用:
SQLAllocHandle(SQL_HANDLE_DBC,hEnv,hDBC);申请的句柄。
服务器名称:为开放式数据库连接性的数据平滑网络(雷达)名称。
名称长度1:指明参数主机名数据的长度。
用户名:数据库用户名。
名称长度2:指明参数用户名数据的长度。
认证:数据库用户密码。
名称长度3:指明参数证明数据的长度。
关于服务器名,用户名,验证参数长度可以直接指定也可以指定为SQL_NTS表明参数是以空字符结尾。
示例代码:
retcode=SQLConnect(hdbc,(SQLCHAR*) odbc_demo ,SQL_NTS,(SQLCHAR*)用户,SQL_NTS,(SQLCHAR*)密码,SQL _ NTS);
2.4.1.4 sqlexec direct直接执行结构化查询语言语句SQLRETURN SQLExecDirect(
SQLHSTMT StatementHandle,
SQLCHAR *语句文本,
SQL整数TextLength);
StatementHandle:SQL语句句柄,也就是前面提到的利用:
SQLAllocHandle(SQL_HANDLE_STMT,hDBC,hSTMT);申请的句柄。
语句文本:SQL语句。
文本长度:参数陈述文本的长度,可以使用SQL_NTS表示字符串以空字符结尾。
如果函数执行成功,你将会得到一个结果集,否则将返回错误信息。
除Select语句外,SQLExecDirect函数还可以执行Insert、Update和Delete语句。在执行修改后的SQL语句后,可以使用SQLRowCount函数来获取更新记录的数量。
2.4.1.5 SQL Fetch移动游标SQL返回SQL Fetch (SQL HSTMT语句句柄);
调用SQLExecDirect执行SQL语句后,需要遍历结果集以获取数据。StatementHandle是STMT句柄,必须已经执行。
当调用SQLFetch函数时,光标将移动到下一条记录。当光标移动到最后一个记录集时,该函数将返回SQL_NO_DATA。
若要遍历所有结果集,可以使用以下方法:
while(SQL_NO_DATA!=SQLFetch(hSTMT)) //将光标移动到集合的末尾。
{//获取结果
}
2.4.1.6 SQL getdata获取游标SQLRETURN SQLGetData(
SQLHSTMT StatementHandle,
SQLUSMALLINT列号,
SQLSMALLINT目标类型,
SQLPOINTER TargetValuePtr,
SQLINTEGER缓冲区长度,
SQL integer * StrLen _ or _ IndPtr);
STMT汉德尔。
ClumnNumber:列号,从1开始。
TargetType:数据缓冲区的C语言数据类型(TargetValuePtr),请参考图2.6。
Length:数据缓冲区的长度(TargetValuePtr)。
Rlen _ or _ indptr:返回当前获取字段的字节长度。
以下是通过SQLFetch和SQLGetData获取记录集的示例:
//假设SQL=selectcustid,name,phone来自客户
SQLINTEGER sCustID
SQLCHAR szName[50],SZ phone[50];
SQLINTEGER cbName,cbAge,cbBirthday//用于保存获取数据的长度
While (TRUE) {//循环获取所有行
retcode=SQL fetch(hstmt);//移动光标
if(retcode==SQL _ ERROR retcode==SQL _ SUCCESS _ WITH _ INFO){
printf(" error SQL fetch/n ");
}
if(retcode==SQL _ SUCCESS retcode==SQL _ SUCCESS _ WITH _ INFO){
/*获取当前光标处每列的值*/
SQLGetData(hstmt,1,SQL_C_ULONG,sCustID,0,cbCustID);
//此处未指明BufferLength参数的值,因为数据类型是定长的。
SQLGetData(hstmt,2,SQL_C_CHAR,szName,50,CB name);
SQLGetData(hstmt,3,SQL_C_CHAR,szPhone,50,CB phone);
printf(out,] %s %s ,sCustID,szName,SZ phone);
}否则{
打破;
}
}
SQLGetData的另一个用途是获取一些变长字段的实际长度,比如VARCHAR字段和TEXT字段。例如:
SQLGetData(hstmt,2,SQL_C_CHAR,szName,0,CB name);
当BufferLength参数设置为0时,字段的实际长度将在StrLen_or_IndPtr参数中返回。但是,请注意,第四个参数必须是有效的指针,不能为空。
另外,在获取一个字段的值时,还有一个数据类型转换的问题。例如,如果数据库中的字段类型是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.7 sqlnumresultTools获取结果集SQLRETURN SQLNumResultCols(
SQLHSTMT StatementHandle,
SQLSMALLINT * ColumnCountPtr:
状态句柄:stmt句柄
ColumnCountPtr:返回列数
2 .4 .1 .8 SQL描述ol得到结果集中列的描述sqlreturn sqldescribecol(
sqlhstmt statementhandle,
SQLSMALLINT列号,
SQLCHAR * ColumnName,
SQLSMALLINT缓冲区长度,
sqlsmallint * namelengthptr,
sqlsmallint * datatypeptr,
sqluinteger * columnsizeptr,
SQL smallint * decimalprt,
sqlsmallint * nullableptr:
状态句柄:stmt句柄。
列号(列编号):需要得到的列的序号,从一开始计算。
列名:得到列的名称。
缓冲长度(缓冲长度)
:指明ColumnName参数的最大长度。
NameLengthPtr:返回列名称的长度。
DataTypePtr:得到列的ODBC数据类型,请参照图2.5。
ColumnSizePtr:得到列的长度。
DecimalDigitsPtr:当该列为数字类型时返回小数点后数据的位数。
NullablePtr:指明该列是否允许为空值。
2.4.1.9 SQLRowCount 执行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.5 ODBC的其他功能介绍2.5.1 ODBC连接句柄的参数设置在上一小节中提到了 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对应的取值:
Attribute
ValuePtr
作用
SQL_ATTR_CONNECTION_DEAD
提供一个合法的SQLINTEGER 指针作为输出参数
检查连接是否已经断开
返回:SQL_CD_TRUE表明连接已经断开,
SQL_CD_FALSE表明连接还保持。
SQL_ATTR_CONNECTION_TIMEOUT
设置一个合法的整数
建立与数据库连接时最大等待的超时秒数,ValuePtr设置为0表明不使用超时控制。
SQL_ATTR_LOGIN_TIMEOUT
设置一个合法的整数
建立与数据库用户登录时最大等待的超时秒数,ValuePtr设置为0表明不使用超时控制。
SQL_ATTR_TRACE
整数,取值为:SQL_OPT_TRACE_OFF,SQL_OPT_TRACE_ON
设置是否跟踪ODBC驱动程序中函数的调用。
SQL_ATTR_TRACEFILE
设置一个合法的文件名字符串,表明记录跟踪记录的文件名。
设置记录函数调用的文件名称。
2.5.2 ODBC语句句柄的参数设置如同数据库连接句柄一样,语句句柄也可以设置参数。函数为:
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对应的取值:
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
设置光标的类型
2.5.3 ODBC中使用可以滚动的光标2.5.3.1 ODBC光标类型从上面的函数SQLSetStmtAttr可以看到我们在ODBC中可以使用不同的光标类型,那么这些光标之间有什么区别。
l 向前光标:SQL_CURSOR_FORWARD_ONLY,光标仅仅向前滚动。
l 静态光标:SQL_CURSOR_STATIC,结果集的数据是静态的,这就是说明在执行查询后,返回的结果集的数据不会再改变,即使是有其他程序更新了数据库中的记录,结果集中的记录也不会发生改变。
l 动态光标:SQL_CURSOR_DYNAMIC,在光标打开以后,当结果集中的行所对应的数据值发生变化时,其变化能够能够反映到光标所对应的结果集 上,这些变化包括:字段的修改,添加,结果集中行的顺序变化。但是请注意如果行别删除则无法在当前结果集中反映出,因为被删除的行不再出现在当前的结果集 中。动态光标所对应的结果集在数据发生变化时会被重建。例如,假设动态光标已获取到了两行,然后,另一应用程序更新了这两行中的一行,并删除了另一行,如 果动态游标再试图获取那些行,它将不能检测已删除的行(因为当前结果集中只有一行,但是不要利用这个办法去检测被删除的行,因为出现这种情况还可能是因为 行的数据被改变后不能再满足查询条件),而是返回已更新行的新值。
l 键集光标:SQL_CURSOR_KEYSET_DRIVEN,和上面的动态光标所不同的是键集光标能够检测到行的删除和修改,但是无法检测到检测到行的 添加和结果集顺序变化。因为在光标创建时就创建了整个结果集,结果集合中记录和顺序已经被固定,这一点和静态光标一样。所以键集光标可以说是一种介于静态 光标和动态光标之间的光标类型。
需要说明的是并不是每个DBMS的ODBC驱动程序都能够支持所有的这几种光标,具体情况可以通过SQLGetInfo 函数进行查询。
2.5.3.2 利用可滚动光标进行查询前面介绍的SQLFetch函数只能够让光标向前移动,但在很多时候我们需要光标能够前后移动。我们需要利用另一个函数SQLFetchScroll,但是再这之前请利用SQLSetStmtAttr正确设置光标类型。SQLFetchScroll的原型如下:
SQLRETURN SQLFetchScroll(
SQLHSTMT StatementHandle,
SQLSMALLINT FetchOrientation,
SQLINTEGER FetchOffset);
与SQLFetch不同的是多了后面两个参数。
FetchOrientation:表明滚动的方式,允许的值如下:
FetchOrientation
含义
SQL_FETCH_NEXT
滚动到下一行,这时候调用相当与SQLFetch,参数FetchOffset将被忽略
SQL_FETCH_PRIOR
滚动到上一行,参数FetchOffset将被忽略
SQL_FETCH_FIRST
滚动到第一行,参数FetchOffset将被忽略
SQL_FETCH_LAST
滚动到最后一行,参数FetchOffset将被忽略
SQL_FETCH_ABSOLUTE
滚动到参数FetchOffset指定的绝对行
SQL_FETCH_RELATIVE
由当前位置滚动到参数FetchOffset指定的相对行,FetchOffset大于0表示向前滚动,FetchOffset小于0表示向后滚动
FetchOffset:表明光标滚动的位置。
光标滚动后,获取数据的方法和SQLFetch相同。滚动时如果指定的位置超出结果集区域会返回错误。
2.5.4 存储过程的执行与参数的绑定在ODBC中调用存储过程需要遵守ODBC的约定,SQL语句的格式为:
{[?=]call procedure-name[([parameter][,[parameter]]...)]}
在这里必须讲解问号在SQL语句中的作用,问号在SQL语句中代表参数的含义,这个参数可以是作为输入,也可以作为输出或者是既输入又输出,参数在SQL语句执行后进行指定,后面的章节中会进一步讲解参数的用法。
其中第一问号的作用是取得存储过程的返回值,在执行的过程中必须将此参数绑定到某一个变量,在执行结束后被绑定的变量中就会被赋值。
后面的参数可以是在SQL语句中直接指定,例如:
{ call insert_this ( 1 , ‘myName’ ) }
也可以利用参数来指定例如:
{ call insert_this ( 1 , ? ) }
参数的值会在SQL语句执行的过程中进行指定。
执行存储过程直接利用SQLExecDirect API 函数就可以了。但是存储过程中很多时候会返回结果,这些结果必须用参数的形式才能够得到,所以下面要介绍一下如何利用参数绑定的方法来得到存储过程的返回值,此外利用参数绑定还可以动态的向存储过程提供参数。
但是进行参数绑定就需要使用另一个ODBC API:
SQLRETURN SQLBindParameter(
SQLHSTMT StatementHandle,
SQLUSMALLINT ParameterNumber,
SQLSMALLINT InputOutputType,
SQLSMALLINT ValueType,
SQLSMALLINT ParameterType,
SQLUINTEGER ColumnSize,
SQLSMALLINT DecimalDigits,
SQLPOINTER ParameterValuePtr,
SQLINTEGER BufferLength,
SQLINTEGER * StrLen_or_IndPtr);
StatementHandle:执行SQL语句STMT句柄。
ParameterNumber:指明要将变量与第几个参数绑定,从1开始计算。
InputOutputType:指明是输入还是输出参数。可以取值的范围为:SQL_PARAM_INPUT,SQL_PARAM_OUTPUT ,SQL_PARAM_INPUT_OUTPUT。
ValueType:指明用于和参数绑定的C语言数据类型。
ParameterType:指明在存储过程中ODBC数据类型。
ColumnSize:指明接收数据的宽度,对于字符串和结构需要指明数据的宽度,而对于普通的变量如SQLINTEGER,SQLFLOAT等设置为0就可以了。
DecimalDigits :当数据类型为SQL_NUMERIC,SQL_DECIMAL时指明数字小数点的精度,否则填0。
ParameterValuePtr:在作为输入参数指明参数的指针,在作为输出参数时指明接收数据的变量指针。
BufferLength:指明参数指针所指向的缓冲区的字节数大小。对于字符串和结构需要指明大小,而对于普通的变量如SQLINTEGER,SQLFLOAT等设置为0就可以了。
StrLen_or_IndPtr: 作为输入参数时指明数据的字节数大小,对于普通的定长变量如SQLINTEGER,SQLFLOAT等设置为0就可以了,对于字符号串需要在此参数中指定 字符串数据的长度,或者设置为SQL_NULL_DATA表明此参数为空值,或者设置为SQL_NTS表明字符串以NULL字符结尾,对于结构需要指明结 构的长度。当作为输出参数时,当SQL执行完毕后会在这个参数中返回拷贝的缓冲区的数据的字节数。
2.5.4.1 例子一:调用含有输出参数的存储过程在SQL Server中建立存储过程:
create proc p_f
@myid int output,@myname varchar(20) output,@mytall float output,@mytall2 dec(30,10) output,
@inid int,@inname varchar(10),@intall float,@intall2 dec(30,10)
as
print do it
set @myid=1+isnull(@inid,-1)
set @myname=test+isnull(@inname,_NULL)
set @mytall=100.100 + @intall
set @mytall2=100.100 + @intall2
return 1024
这个存储过程的前四个参数是输出参数,后四个参数是输入参数。并且最后返回1024作为返回值。四个参数分别是不同的数据类型:整数,字符串,浮点数和带有10位小数的30位数字。
这个例子中我们不使用后面四个参数,只演示输出参数的使用方法,后四个输出参数直接在SQL语句中指定,程序代码如下。
void OnTestP4()
{
//省略分配句柄和连接数据库的部分
SQLCHAR szOutput[40],szOutput2[40];
SQLINTEGER iOutput=0,iReturnVal=0;
SQLFLOAT fOutput=0;
//只使用输出参数
SQLCHAR szSQL[100]="{?=call p_f(?,?,?,?,1,_OK,1000.001,1000.001)}";
//用于保存各个返回的输出参数的字节数
SQLINTEGER cb1,cb2,cb3,cb4,cb5;
//分配STMT句柄
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, hstmt1);
//绑定参数
retcode = SQLBindParameter(hstmt1, 1, SQL_PARAM_OUTPUT, SQL_C_LONG,SQL_INTEGER, 0, 0, iReturnVal, 0, cb1);
if ( (retcode != SQL_SUCCESS) (retcode != SQL_SUCCESS_WITH_INFO) )
{
ProcessLogMessages(SQL_HANDLE_STMT, hstmt1,"bind para1() Failed/n/n", TRUE);
}
retcode = SQLBindParameter(hstmt1, 2, SQL_PARAM_OUTPUT, SQL_C_LONG,SQL_INTEGER, 0, 0, iOutput, 0, cb2);
if ( (retcode != SQL_SUCCESS) (retcode != SQL_SUCCESS_WITH_INFO) )
{
ProcessLogMessages(SQL_HANDLE_STMT, hstmt1,"bind para2() Failed/n/n", TRUE);
}
retcode = SQLBindParameter(hstmt1, 3, SQL_PARAM_OUTPUT, SQL_C_CHAR,SQL_VARCHAR, 20, 0, szOutput, 20, cb3);
if ( (retcode != SQL_SUCCESS) (retcode != SQL_SUCCESS_WITH_INFO) )
{
ProcessLogMessages(SQL_HANDLE_STMT, hstmt1,"bind para3() Failed/n/n", TRUE);
}
retcode = SQLBindParameter(hstmt1, 4, SQL_PARAM_OUTPUT, SQL_C_DOUBLE,SQL_FLOAT, 0, 0, fOutput, 0, cb4);
if ( (retcode != SQL_SUCCESS) (retcode != SQL_SUCCESS_WITH_INFO) )
{
ProcessLogMessages(SQL_HANDLE_STMT, hstmt1,"bind para4() Failed/n/n", TRUE);
}
retcode = SQLBindParameter(hstmt1, 5, SQL_PARAM_OUTPUT, SQL_C_CHAR,SQL_DECIMAL, 25, 10, szOutput2, 40, cb5);
if ( (retcode != SQL_SUCCESS) (retcode != SQL_SUCCESS_WITH_INFO) )
{
ProcessLogMessages(SQL_HANDLE_STMT, hstmt1,"bind para5() Failed/n/n", TRUE);
}
//执行SQL语句,调用存储过程
retcode = SQLExecDirect (hstmt1,szSQL, SQL_NTS);
if ( (retcode != SQL_SUCCESS) (retcode != SQL_SUCCESS_WITH_INFO) )
{
ProcessLogMessages(SQL_HANDLE_STMT, hstmt1,"SQLExecute() Failed/n/n", TRUE);
}
else
//得到结果集
while ( ( retcode = SQLMoreResults(hstmt1) ) != SQL_NO_DATA ) ;
TRACE("%d %d %s %f %s/n",iReturnVal,iOutput,szOutput,fOutput,szOutput2);
SQLFreeHandle(SQL_HANDLE_STMT,hstmt1);
//省略释放句柄部分
}
我们现在来分别分析返回值和四个参数绑定的调用。
绑定返回值和第一个整数:
retcode = SQLBindParameter(hstmt1, 1, SQL_PARAM_OUTPUT, SQL_C_LONG,SQL_INTEGER, 0, 0, iReturnVal, 0, cb1);
ParameterNumber =1,2
InputOutputType = SQL_PARAM_OUTPUT,指明作为输出参数。
ValueType = SQL_C_LONG,指明C语言中数据类型为 int 。
ParameterType = SQL_INTEGER,指明SQL数据类型为Integer。
ColumnSize = 0
DecimalDigits = 0
ParameterValuePtr = iReturnVal , iOutput 为保存返回参数的缓冲区(变量)指针。
BufferLength = 0
StrLen_or_IndPtr = cb1,cb1未赋初值,用于得到返回参数的字节大小。
绑定第二个字符串:
retcode = SQLBindParameter(hstmt1, 3, SQL_PARAM_OUTPUT, SQL_C_CHAR,SQL_VARCHAR, 20, 0, szOutput, 20, cb3);
ParameterNumber =3
InputOutputType = SQL_PARAM_OUTPUT,指明作为输出参数。
ValueType =SQL_C_CHAR,指明C语言中数据类型为 char[ ] 。
ParameterType = SQL_VARCHAR,指明SQL数据类型为 Varchar。
ColumnSize = 20 ,指明字符串宽度为20。
DecimalDigits = 0
ParameterValuePtr = szOutput 为保存返回参数的缓冲区(变量)指针。
BufferLength = 20 ,指明缓冲区的最大字节数。
StrLen_or_IndPtr = cb3,cb3未赋初值,用于得到返回参数的字节大小。
绑定第三个浮点数:
retcode = SQLBindParameter(hstmt1, 4, SQL_PARAM_OUTPUT, SQL_C_DOUBLE,SQL_FLOAT, 0, 0, fOutput, 0, cb4);
ParameterNumber =4
InputOutputType = SQL_PARAM_OUTPUT,指明作为输出参数。
ValueType =SQL_C_DOUBLE,指明C语言中数据类型为 double 。
ParameterType = SQL_FLOAT,指明SQL数据类型为 float或double。
ColumnSize = 0
DecimalDigits = 0
ParameterValuePtr = fOutput为保存返回参数的缓冲区(变量)指针。
BufferLength = 0
StrLen_or_IndPtr = cb4,cb4未赋初值,用于得到返回参数的字节大小。
绑定第四个数字:
retcode = SQLBindParameter(hstmt1, 5, SQL_PARAM_OUTPUT, SQL_C_CHAR,SQL_DECIMAL, 25, 10, szOutput2, 40, cb5);
ParameterNumber =4
InputOutputType = SQL_PARAM_OUTPUT,指明作为输出参数。
ValueType =SQL_C_ CHAR,指明C语言中数据类型为 char[ ] ,虽然可以使用SQL_NUMERIC_STRUCT结构来进行参数绑定,不过ODBC中默认的类型是SQL_C_CHAR(见图2.6)。
ParameterType = SQL_ DECIMAL,指明SQL数据类型为 Dec或Number。
ColumnSize = 25 ,指明数据的宽度,请注意在存储过程中定义的参数为dec(30, 10)表明参数为30位,而实际上返回的不足30位,所以这里可以指定一个比较小的值,但是如果返回的数的位数如果超过25。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。