资源描述
北京邮电大学
实验报告
课程名称 数据库系统原理
实验名称 数据库接口实验
教师______ 成绩_________
2013年 5月 15日
实验目的
1. 通过编写数据库应用程序,培养数据库应用程序开发能力。
2. 熟悉数据库应用程序设计的多种接口的配置,培养相关的软件配置能力。
实验原理
数据库应用程序设计是数据库应用的一个重要方面。SQL语言除了以用户交互的方式使用外,还可以被数据库应用程序直接访问。
通常DBMS支持的能直接访问数据库的是SQL语言,而SQL语言不象高级语言(例如C、C++等)具备很好的数据处理能力。通常的情况是需要将两种语言结合起来,利用SQL访问数据,而将数据传递给某种高级语言程序,处理后又利用SQL写回数据库。
这种嵌在高级语言程序中的SQL语句称为嵌入式SQL(或者称为ESQL),和以前使用的交互式SQL(或者称为ISQL)不一样,它们随着程序执行被调用,辅助程序完成数据库数据读写的功能,而高级语言程序则负责对数据库中的数据的分析处理转换等操作。
ESQL又分成两种,如果在程序执行前SQL的结构就已经确定,最多是可以在执行时传递一些数值参数,那么这种ESQL语句称为静态ESQL。如果程序执行时才知道执行的SQL语句时,称为动态ESQL。
SQL SERVER中执行静态ESQL(比如SQLJ)时,首先利用预编译将宿主程序语言中的SQL语句分离出来,代之以过程或函数调用,然后对剩余程序正常编译和连接库函数等。分离出来的SQL语句则在数据库端进行处理,进行语法检查、安全性检查和优化执行策略,绑定在数据库上形成包(packet)供应用程序调用。
而通过ODBC、JDBC、OLEDB等访问数据库都是动态ESQL。SQL SERVER执行它们时因为无法事先确实知道是什么样的SQL语句,从而无法进行静态绑定,而这种绑定过程只能在程序执行过程中生成了确定的要执行的SQL语句时才能进行,称为动态绑定。
应用程序的接口则有JDBC、ADO、OLEDB、ODBC等多种。微软在SQL SERVER 2005开始还增加了几项功能:一是service broker,围绕发送和接收消息而设计,处理通信中的消息收发工作,使开发人员集中精力于解决实际问题。二是CLR(Common Language Runtime)组件,就是Microsoft.NET Framework中的公共语言运行库组件,使开发人员可用任何CLR语言来编写过程、函数、触发器等,甚至自定义数据类型。三是对T-SQL做出了许多扩展。
实验环境
采用SQL SERVER数据库管理系统作为实验平台。其中,SQL SERVER可以采用Express、Develop或者Enterprise版本,选择SQL SERVER 2005、2008或2012。
实验内容
1. 了解通用数据库应用编程接口(例如JDBC、ODBC等)的配置方法。
2. 利用C语言(或其它支持某种数据库应用程序接口的高级程序设计语言)编程实现简单的数据库应用程序,掌握基于该接口的数据库访问的基本原理和方法。
3. 掌握静态嵌入式SQL语言程序设计的原理和方法。
4. 在数据库应用程序中,执行查找、增加、删除、更新等操作。
5. 选择适当的数据访问接口,比较高的要求为完成静态和动态嵌入式SQL的程序设计(需要两个程序),基本要求为两者选择其一。
实验步骤
1. 实验准备:
以教科书第四章关于SQL语言相关内容为基础,课后查阅、自学ODBC/JDBC等接口有关内容,包括体系结构、工作原理、数据访问过程、主要API接口的语法和使用方法等。
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函数关闭同数据库的连接。
2. 在Windows控制面板中通过管理工具下的ODBC数据源工具在客户端新建连接到SQL SERVER数据库服务器的ODBC数据源,测试连接通过后保存,注意名字要和应用程序中引用的数据源一样。另外要注意创建ODBC数据源时使用的驱动程序必须是支持SQL SERVER的,而不可选择其它驱动程序。
建立一个系统DSN(系统数据源)并与SQL数据库相连接
使用sa作为登陆与验证的用户
验证成功后,选择默认数据库
测试成功
3. 以实验二建立的数据库为基础,编写 C语言(或其它支持ODBC/JDBC等接口的高级程序设计语言) 数据库应用程序,利用SQLExecDirect语句,实现数据库应用程序对数据库中表(有数据)进行数据查询、删除、插入、更新等操作。要求先打印出所有记录,然后删除一行,再打印一次,进行更新,再打印一次,最后插入,再打印一次。
注意:SQL SERVER应设置为应用程序可以访问。
本次实验我们使用VC6.0作为实验环境
实验结果
数据库中原BSC元组
进行操作过后
实验总结
在实验中有哪些重要问题或者事件?你如何处理的?你的收获是什么?有何建议和意见等等。
遇到问题:(1)在进行ODBC连接时会出现连接不上的情况,此时要检查ODBC数据源名称是否对应控制面板中新建的ODBC源;还要检查登陆用户名和密码是否正确;再检查SQL server数据库服务是否开启;还要查看系统是否是64位,64位可能出现问题。
(2)刚开始我们组编程的时候用的是visual studio 2012进行的编程,但是在编译运行的时候总是有问题。后来无意间我们用了visual c++ 6.0 运行,最后竟然通过了,而且没有任何问题。最后可以尝试使用其他编译器进行编译,会发现真的和编译器有关系,也许简单的编译器链接运行条件更少一些,不过包括的库函数也少,利弊共存。
(3)在配置ODBC源的时候要选择好默认数据库否则可能无法执行进行正确操作
通过这次实验学习到了ODBC作为一个数据库应用编程接口也能够实现对数据库的各项操作,在编写语句的同时,为今后实现数据库跟程序之间的连接打下了基础。
附录:
源代码
#include<windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "sql.h"
#include "sqltypes.h"
#include "sqlext.h"
RETCODE retcode;//结果返回集
SQLHDBC hdbc;//定义链接句柄
void print();//打印子程序
void SQL(unsigned char[]);//执行SQL语句子程序
int main(){
SQLHANDLE henv; //定义环境句柄
unsigned char SY[]="GSM";//ODBC数据源名称
unsigned char db2[]="sa";//用户名
unsigned char pass[]="sa";//密码
//分配ODBC环境
retcode=SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
if(retcode==SQL_SUCCESS||retcode==SQL_SUCCESS_WITH_INFO)
//声明环境
retcode=SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,(void*)SQL_OV_ODBC3, 0);
if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); //分配连接句柄
if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
retcode=SQLConnect(hdbc,SY,SQL_NTS,db2,SQL_NTS,pass,SQL_NTS);//链接
if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
printf("打印一次");
print();
unsigned char del[]="delete from BSC where BscId=42216";
SQL(del);
printf("删除BscId=42216再打印一次");
print();
unsigned char update[]="update BSC set MscID=9999 where BscId=42215";
SQL(update);
printf("更新BscId=42215再打印一次");
print();
unsigned char insert[]="insert into BSC values('99999','DTBSC','Bell','121.149885','41.120217','9999')";
SQL(insert);
printf("插入新元组BscId=99999再打印一次");
print();
}
SQLFreeConnect(hdbc); ////释放链接句柄
SQLFreeEnv(henv); // 释放ODBC 环境句柄
return 0;
}
void print()
{
unsigned char yuju[]="select * from BSC";
SQLHSTMT hstmt;//定义语句句柄
char BscId[6],BscName[20],BscCompany[20],Longitude[9],Latitude[9],MscID[5];
long lenOut1,lenOut2,lenOut3,lenOut4,lenOut5,lenOut6;
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); //分配语句句柄
if(retcode==SQL_SUCCESS)
{
retcode=SQLExecDirect(hstmt,yuju,SQL_NTS);
if(retcode == SQL_SUCCESS||retcode == SQL_SUCCESS_WITH_INFO)
{
//将结果集中的属性列一一绑定至变量
retcode=SQLBindCol(hstmt,1,SQL_C_CHAR,BscId,sizeof(BscId),&lenOut1);
retcode=SQLBindCol(hstmt,2,SQL_C_CHAR,BscName,sizeof(BscName),&lenOut2);
retcode=SQLBindCol(hstmt,3,SQL_C_CHAR,BscCompany,sizeof(BscCompany),&lenOut3);
retcode=SQLBindCol(hstmt,4,SQL_C_CHAR,Longitude,sizeof(Longitude),&lenOut4);
retcode=SQLBindCol(hstmt,5,SQL_C_CHAR,Latitude,sizeof(Latitude),&lenOut5);
retcode=SQLBindCol(hstmt,6,SQL_C_CHAR,MscID,sizeof(MscID),&lenOut5);
//把所有捆绑过的数据字段的数据拷贝到相应的缓冲区
retcode=SQLFetch(hstmt);
printf("%s\n",yuju);
printf("BscId BscName BscCompany Longitude Latitude MscID\n");
while(retcode == SQL_SUCCESS||retcode == SQL_SUCCESS_WITH_INFO)
{
printf("%-17s%-13s%-17s%-12s%-11s%-9s%\n",BscId,BscName,BscCompany,Longitude,Latitude,MscID);
//把所有捆绑过的数据字段的数据拷贝到相应的缓冲区
retcode=SQLFetch(hstmt);
}
}
}
SQLFreeStmt(hstmt,SQL_DROP);
}
void SQL(unsigned char aaa[]){
//printf("%s\n",aaa);
SQLHSTMT hstmt;//定义语句句柄
retcode=SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); //分配语义句柄
if(retcode==SQL_SUCCESS){
retcode=SQLExecDirect(hstmt,aaa,SQL_NTS);
}
SQLFreeStmt(hstmt,SQL_DROP); //释放语句句柄
}
展开阅读全文