ODBC 数据库编程(VC)
目录
ODBC 概述
ODBC 数据类型 API 函数
ODBC 编程
(本章节中例子都是用 VS2005 编译调试的,数据库为 SQL Server 2005)
ODBC 概述
ODBC 概述
ODBC(Open Database Connectivity),开放数据库互连.ODBC 是上个世纪八十年代末九十年代初出现的技术,它为编写关系数据库的客户软件提供了一种统一的接口.ODBC 提供一个单一的 API,可用于处理不同数据库的客户应用程序.使用 ODBC API 的应用程序可以与任何具有ODBC驱动程序的关系数据库进行通信
ODBC 是为客户应用程序访问关系数据库时提供的一个标准的接口,对于不同的数据库,ODBC 提供了统一的 API,使应用程序锁提供的 API 来访问任何提供了 ODBC 驱动程序的数据库.
-
- 应用程序(Application,即对应下图的客户程序): 应用程序本身不直接与数据库打交道,主要负责处理并调用ODBC函数,发送对数据库的SQL请求及取得结果.
- 驱动程序管理器(Driver Manager,即对应下图的 ODBC 驱动管理程序): 驱动程序管理器是一个带有输入程序的动态链接库(DLL),主要目的是加载驱动程序,处理ODBC调用的初始化调用,提供ODBC调用的参数有效性和序列有效性.
- 驱动程序(Driver,即对应下图的 ODBC 驱动程序): 驱动程序是一个完成ODBC函数调用并与数据库相互影响的DLL,这些驱动程序可以处理对于特定的数据的数据库访问请求.对于应用驱动程序管理器送来的命令,驱动程序再进行解释形成自己的数据库所能理解的命令.驱动程序将处理所有的数据库访问请求,对于应用程序来讲不需要关注所使用的是本地数据库还上网络数据库.
三者的交互过程如下图所示:
优点
由于ODBC是一种底层访问技术,因为ODBC API可以使客户应用程序能够从底层设置和控制数据库,完成一些高层数据库技术无法完成的功能
缺点
由于ODBC只能用于关系数据库,使得利用ODBC很难访问对象数据库及其他非关系数据库.另外,直接使用ODBC API编写应用程序要编制大量的代码,增加了程序编制的复杂程度
相关文件及概述:
- sql.h: 包含有基本的ODBC API的定义
- sqlext.h: 包含有扩展的ODBC的定义
- sqltypes.h: 包含有SQL的类型的定义
- odbc32.lib: 库文件
ODBC API执行SQL语句方式
直接执行
- 特点: 直接执行是最快捷的执行方式,一次准备,一次提交并执行
准备执行
- 特点: 准备执行是逐步设定执行需要的参数,最后来执行,也就是经历了一个执行准备和执行提交的过程
- 阶段:
- 执行准备阶段: 应用程序通过调用SQLPrepare函数来实现,每次准备的SQL语句先被预编译一遍,以等待语句的执行,这样多次执行的语句值须编译一次,从而加快SQL语句的多次执行,另外准备执行也可以减少网络负载,因为在每次执行语句时,驱动程序可以只想,数据源发送一个语句标识符,而不是整个SQL语句
- 执行提交阶段: 应用程序使用SQLExecute函数来实现
- 步骤
- 调用SQLPrepare函数并向其传递一个含有SQL语句的字符串
- 设置参数值用SQLBindParameter来绑定参数
- 调用SQLExecute函数执行SQL语句
使用编码函数
在应用程序中获取数据源的内部系统信息,如数据库中包含的所有表信息,表中包含所有列信息
ODBC 光标类型
- 向前光标: SQL_CURSOR_FORWARD_ONLY,光标仅仅向前滚动
- 静态光标: QL_CURSOR_STATIC,结果集的数据是静态的,这就是说明在执行查询后,返回的结果集的数据不会再改变,即使是有其他程序更新了数据库中的记录,结果集中的记录也不会发生改变
- 动态光标: SQL_CURSOR_DYNAMIC,在光标打开以后,当结果集中的行所对应的数据值发生变化时,其变化能够能够反映到光标所对应的结果集上,这些变化包括:字段的修改,添加,结果集中行的顺序变化.但是请注意如果行别删除则无法在当前结果集中反映出,因为被删除的行不再出现在当前的结果集中.动态光标所对应的结果集在数据发生变化时会被重建.例如,假设动态光标已获取到了两行,然后,另一应用程序更新了这两行中的一行,并删除了另一行,如果动态游标再试图获取那些行,它将不能检测已删除的行(因为当前结果集中只有一行,但是不要利用这个办法去检测被删除的行,因为出现这种情况还可能是因为行的数据被改变后不能再满足查询条件),而是返回已更新行的新值
- 键集光标: SQL_CURSOR_KEYSET_DRIVEN,和上面的动态光标所不同的是键集光标能够检测到行的删除和修改,但是无法检测到检测到行的添加和结果集顺序变化.因为在光标创建时就创建了整个结果集,结果集合中记录和顺序已经被固定,这一点和静态光标一样.所以键集光标可以说是一种介于静态光标和动态光标之间的光标类型
ODBC 编程
操作步骤:
建立 ODBC DSN:
说明:
DSN(Data Source Name)是用于指定ODBC与相关的驱动程序相对应的一个入口,所有DSN的信息由系统进行管理,一般来讲当应用程序要使用ODBC访问数据库时,就需要指定一个DSN以便于连接到一个指定的ODBC驱动程序.
DSN 共分为三类:
- 用户 DSN: 对当前登录用户可见,只能够用于当前计算机.
- 系统 DSN: 对当前系统上所有用户可见,包括NT中的服务.
- 文件 DSN: DSN信息存放在文件中,对能够访问到该文件的用户可见.
步骤:
菜单 -> 控制面板 -> 管理工具 -> 数据源 ODBC -> 添加数据源(操作过程如下面图片所示,单击可以看到大图)
在这里我遇到过一个问题,当时我的系统是 Win7 ,然后装 SQL Server 2005 时候是利用默认实例,结果我在添加数据源时候一直没有找到服务器,然后我在晚上查原因时候,网上给我的解释是默认实例时装数据库是不建立服务器的,所以我有重新装了一遍 SQL Server 2005 然后采用的是命名实例,实例名为 SQL2005 然后便可以在数据源添加时候找到自己的服务器了.如果有遇到这类情况即找不到服务器时候可以使试试这个方法.但是在用 SQL Server Management Studio 登入数据库时候服务器名称就是 "本机名 + \ + 命名实例名" 例如: KANG-PC\SQL2005.
代码样例:
首先在 SQL Server 2005 中,先建立了一个数据库,数据库名为 MyTest ,数据库中有一个表,表名为 T1,表项和数据类容如下:
然后为这个数据库在数据源 ODBC 中添加了一个名为 SQL Server 的数据源驱动程序.接着开始数据库编程:
直接执行:
查询数据(这里注意下:用 SQLAllocHandle 分配的句柄用完后需要用 SQLFreeHandle 去释放这个句柄)
#include"windows.h" #include"sql.h" #include"sqlext.h" #include"sqltypes.h" //以上四个头文件是必须要的 #include<cstdlib> #include<iostream> using namespace std; void main() { SQLHENV serverhenv; SQLHDBC serverhdbc; SQLHSTMT serverhstmt; SQLRETURN ret; SQLCHAR name[20]={0}; SQLINTEGER id = 0,age=0,num=0,length; //分配环境句柄 ret = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&serverhenv); //设置环境属性 ret = SQLSetEnvAttr(serverhenv,SQL_ATTR_ODBC_VERSION,(void*)SQL_OV_ODBC3,0); if(!SQL_SUCCEEDED(ret)) { cout<<"AllocEnvHandle error!"<<endl; system("pause"); return; } //分配连接句柄 ret = SQLAllocHandle(SQL_HANDLE_DBC,serverhenv,&serverhdbc); if(!SQL_SUCCEEDED(ret)) { cout<<"AllocDbcHandle error!"<<endl; system("pause"); return; } //数据库连接 ret = SQLConnect(serverhdbc,(SQLWCHAR*)L"SQL Server",SQL_NTS,NULL,SQL_NTS,NULL,SQL_NTS); if(!SQL_SUCCEEDED(ret)) { cout<<"SQL_Connect error!"<<endl; system("pause"); return; } //分配执行语句句柄 ret = SQLAllocHandle(SQL_HANDLE_STMT,serverhdbc,&serverhstmt); //查询数据 ret=SQLExecDirect(serverhstmt,(SQLWCHAR*)L"select * from T1",SQL_NTS); if(ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { //绑定数据 SQLBindCol(serverhstmt,1, SQL_C_CHAR, (void*)name,sizeof(name), &length); SQLBindCol(serverhstmt,2, SQL_C_ULONG, (void*)&id,sizeof(id), &length); SQLBindCol(serverhstmt,3, SQL_C_ULONG, (void*)&age,sizeof(age), &length); //将光标移动到下行,即获得下行数据 while(SQL_NO_DATA != SQLFetch(serverhstmt)) { cout<<"name:"<<name<<" ID:"<<id<<" Age:"<<age<<" "; cout<<endl; } } //释放语句句柄 ret=SQLFreeHandle(SQL_HANDLE_STMT,serverhstmt); if(SQL_SUCCESS!=ret && SQL_SUCCESS_WITH_INFO != ret) cout<<"free hstmt error!"<<endl; //断开数据库连接 ret=SQLDisconnect(serverhdbc); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"disconnected error!"<<endl; //释放连接句柄 ret=SQLFreeHandle(SQL_HANDLE_DBC,serverhdbc); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"free hdbc error!"<<endl; //释放环境句柄句柄 ret=SQLFreeHandle(SQL_HANDLE_ENV,serverhenv); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"free henv error!"<<endl; system("pause"); }
运行结果:
准备执行:
插入数据:
#include"windows.h" #include"sql.h" #include"sqlext.h" #include"sqltypes.h" //以上四个头文件是必须要的 #include<cstdlib> #include<iostream> using namespace std; void main() { SQLHENV serverhenv; SQLHDBC serverhdbc; SQLHSTMT serverhstmt; SQLRETURN ret; SQLCHAR name[20]={0}; SQLINTEGER id = 0,age=0,num=0,length=20; SQLINTEGER pointer=sizeof(name); //分配环境句柄 ret = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&serverhenv); //设置环境属性 ret = SQLSetEnvAttr(serverhenv,SQL_ATTR_ODBC_VERSION,(void*)SQL_OV_ODBC3,0); if(!SQL_SUCCEEDED(ret)) { cout<<"AllocEnvHandle error!"<<endl; system("pause"); return; } //分配连接句柄 ret = SQLAllocHandle(SQL_HANDLE_DBC,serverhenv,&serverhdbc); if(!SQL_SUCCEEDED(ret)) { cout<<"AllocDbcHandle error!"<<endl; system("pause"); return; } //数据库连接 ret = SQLConnect(serverhdbc,(SQLWCHAR*)L"SQL Server",SQL_NTS,NULL,SQL_NTS,NULL,SQL_NTS); if(!SQL_SUCCEEDED(ret)) { cout<<"SQL_Connect error!"<<endl; system("pause"); return; } //分配执行语句句柄 ret = SQLAllocHandle(SQL_HANDLE_STMT,serverhdbc,&serverhstmt); //准备语句 ret=SQLPrepare(serverhstmt,(SQLWCHAR*)L"insert T1 values(?,?,?)",SQL_NTS); //参数绑定 ret=SQLBindParameter(serverhstmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,pointer,0,(void *)name,pointer, &pointer); if( ret != SQL_SUCCESS) cout<<"SQLBindParameter name error!"<<endl; ret=SQLBindParameter(serverhstmt,2,SQL_PARAM_INPUT,SQL_C_LONG,SQL_INTEGER,0,0,(void*)&id,0,0); if( ret != SQL_SUCCESS) cout<<"SQLBindParameter id error!"<<endl; ret=SQLBindParameter(serverhstmt,3,SQL_PARAM_INPUT,SQL_C_LONG,SQL_INTEGER,0,0,(void*)&age,0,0); if( ret != SQL_SUCCESS) cout<<"SQLBindParameter age error!"<<endl; //插入数据 strcpy((char*)name,(char *)"Mile"); id = 1223; age = 45; ret=SQLExecute(serverhstmt); if( ret != SQL_SUCCESS) cout<<"SQLExecute error!"<<endl; //插入数据 strcpy((char*)name,(char *)"Kee"); id = 1224; age = 16; ret=SQLExecute(serverhstmt); if( ret != SQL_SUCCESS) cout<<"SQLExecute error!"<<endl; //释放语句句柄 ret=SQLFreeHandle(SQL_HANDLE_STMT,serverhstmt); if(SQL_SUCCESS!=ret && SQL_SUCCESS_WITH_INFO != ret) cout<<"free hstmt error!"<<endl; //断开数据库连接 ret=SQLDisconnect(serverhdbc); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"disconnected error!"<<endl; //释放连接句柄 ret=SQLFreeHandle(SQL_HANDLE_DBC,serverhdbc); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"free hdbc error!"<<endl; //释放环境句柄句柄 ret=SQLFreeHandle(SQL_HANDLE_ENV,serverhenv); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"free henv error!"<<endl; system("pause"); }
运行结果(数据库内容如下):
获得错误信息:
实现代码:
#include"windows.h" #include"sql.h" #include"sqlext.h" #include"sqltypes.h" //以上四个头文件是必须要的 #include<cstdlib> #include<iostream> using namespace std; void main() { SQLHENV serverhenv; SQLHDBC serverhdbc; SQLHSTMT serverhstmt; SQLRETURN ret; SQLWCHAR errorString[100]; SQLSMALLINT MsgLen; SQLWCHAR SqlState[6]; SQLINTEGER NativeError; //分配环境句柄 ret = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&serverhenv); //设置环境属性 ret = SQLSetEnvAttr(serverhenv,SQL_ATTR_ODBC_VERSION,(void*)SQL_OV_ODBC3,0); if(!SQL_SUCCEEDED(ret)) { cout<<"AllocEnvHandle error!"<<endl; system("pause"); return; } //分配连接句柄 ret = SQLAllocHandle(SQL_HANDLE_DBC,serverhenv,&serverhdbc); if(!SQL_SUCCEEDED(ret)) { cout<<"AllocDbcHandle error!"<<endl; system("pause"); return; } //数据库连接 ret = SQLConnect(serverhdbc,(SQLWCHAR*)L"SQL Server",SQL_NTS,NULL,SQL_NTS,NULL,SQL_NTS); if(!SQL_SUCCEEDED(ret)) { cout<<"SQL_Connect error!"<<endl; system("pause"); return; } //分配执行语句句柄 ret = SQLAllocHandle(SQL_HANDLE_STMT,serverhdbc,&serverhstmt); //执行语句,数据库中已经存在这条数据,所以插入肯定失败. ret=SQLExecDirect(serverhstmt,(SQLWCHAR*)L"insert T1 values('Jame',1221,21)",SQL_NTS); if(SQL_SUCCESS!=ret && SQL_SUCCESS_WITH_INFO != ret) cout<<"SQLExecDirect error!"<<endl; //获取错误信息 int i=1; while(true) { if((ret = SQLGetDiagRecW(SQL_HANDLE_STMT,serverhstmt,i,SqlState,&NativeError,errorString,100,&MsgLen)) != SQL_NO_DATA) //wcout<<errorString<<endl; //这里有些字符输不出来,所以我改为了消息框弹出显示错误信息 MessageBox(NULL,errorString,L"",MB_OK); else break; i++; } //释放语句句柄 ret=SQLFreeHandle(SQL_HANDLE_STMT,serverhstmt); if(SQL_SUCCESS!=ret && SQL_SUCCESS_WITH_INFO != ret) cout<<"free hstmt error!"<<endl; //断开数据库连接 ret=SQLDisconnect(serverhdbc); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"disconnected error!"<<endl; //释放连接句柄 ret=SQLFreeHandle(SQL_HANDLE_DBC,serverhdbc); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"free hdbc error!"<<endl; //释放环境句柄句柄 ret=SQLFreeHandle(SQL_HANDLE_ENV,serverhenv); if(SQL_SUCCESS!=ret&&SQL_SUCCESS_WITH_INFO!=ret) cout<<"free henv error!"<<endl; system("pause"); }
运行结果:
相关资料: