首页 嵌入式SQL和ODBC的使用

嵌入式SQL和ODBC的使用

举报
开通vip

嵌入式SQL和ODBC的使用实验2 SQL的使用 实验4 嵌入式SQL和ODBC的使用 实验人:田超(31311039) 黄伟(31311064) 实验目的 1.​ 熟悉ODBC的配置和使用 2.​ 熟悉嵌入式SQL编程 3.​ 巩固SQL的知识 实验平台 1.​ OS: WindowsXP 2.​ DBMS: SQLServer2000 3.​ Compiler: Visual C++ 6.0 预备知识 1)嵌入式SQL编程: 嵌入式SQL由SQL语句和C/C++代码组成。其中SQL语句由预处理器翻译成C或C++的源代码。对预处理后的源代...

嵌入式SQL和ODBC的使用
实验2 SQL的使用 实验4 嵌入式SQL和ODBC的使用 实验人:田超(31311039) 黄伟(31311064) 实验目的 1.​ 熟悉ODBC的配置和使用 2.​ 熟悉嵌入式SQL编程 3.​ 巩固SQL的知识 实验平台 1.​ OS: WindowsXP 2.​ DBMS: SQLServer2000 3.​ Compiler: Visual C++ 6.0 预备知识 1)嵌入式SQL编程: 嵌入式SQL由SQL语句和C/C++代码组成。其中SQL语句由预处理器翻译成C或C++的源代码。对预处理后的源代码进行编译、连接生成可执行程序后方可运行。 ​ SQL预处理器 SQLServer的预处理程序是nsqlprep.exe。其常用的语法格式如下: nsqlprep 程序文档名 ​ nsqlprep详细的语法格式以及参数意义,请看联机帮助。 经查阅联机帮助,nsqlprep的语法格式如下: nsqlprep program_file_name [/SQLACCESS | /NOSQLACCESS] [/FLAGGER {ENTRY | NONE}] [/DB [server_name.]database_name /PASS {login[.password] | $INTEGRATED}] [/BIND file_name] [/MSG file_name] [/NOLOGO] [/PLAN name] [/NOLINES] [/user_defined_option] ​ 要求程序文档名的后缀为.sqc,可以省略。 ​ 预编译后得到的文档,与程序文档同名,后缀为.c;放在与程序文档名同一个路径下 nsqlprep.exe在SQLServer的安装目录的 MSSQL\Binn下。 在本机中SQLServer的安装目录是C:\Program Files\Microsoft SQL Server, nsqlprep.exe在C:\Program Files\Microsoft SQL Server\MSSQL\Binn。由于默认的安装方式(典型安装)并没有安装应用程序nsqlprep.exe,因此,需要把本机中E:\院内学习工作\大三\数据库\SQLServer2000个人版\x86\binn目录下的 内容 财务内部控制制度的内容财务内部控制制度的内容人员招聘与配置的内容项目成本控制的内容消防安全演练内容 拷贝到该目录下。 ​ 连接方式 经预处理后的c文件就可以用c的编译器进行编译连接了。 使用Visual C++ 6.0进行编译连接,连接方式是动态连接,用到动态链接库SQLakw32.dll,sqlaiw32.dll;此两文件已经随同binn.rar的其他内容,被拷贝到C:\Program Files\Microsoft SQL Server\MSSQL\Binn下;但仍然需要把该路径加到系统的路径变量中,以使得程序运行时能找到这两个文件。 方法1:把该两文件拷贝到操作系统目录下的子目录system32中 方法2:把C:\Program Files\Microsoft SQL Server\MSSQL\Binn加到系统环境变量path中。“我的电脑”->“属性”->“高级”->“环境变量”->“path,编辑”,如下图所示: 在变量值中加入该路径值;注意,路径间用分号”;”分开。 在实验过程中发现如果只做第二步,在之后的运行过程中会报错,只有将方法一也执行了这个连接的准备工作才算是正式完成。 2)通过ODBC访问数据库: 配置ODBC,为SQL Server添加数据源。如下图所示: 初始化环境: 1.​ SQL Server2000为其嵌入式SQL提供了一些特殊的接口;默认的安装方式(典型安装)并没有安装这些接口;因此,需要把devtools.rar解压到SQLServer的系统目录下(注意,不是安装目录);本机是把操作系统安装在C盘,则SQLServer的系统目录则是C:\Program Files\Microsoft SQL Server。 2.​ 初始化SQL Server的预编译环境。 ​ 初始化Visual C++ 6.0 编译器环境,运行文件:\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT 这个过程要在DOS下运行才有效,即找vcvars32.bat的路径,在DOS下运行。 具体步骤如下: ​ 初始化SQLServer的预编译环境,运行文件:\devtools\samples\esqlc\setenv.bat。 运行方式与vcvars32.bat相同,结果如下: 3.​ 初始化Visual C++ 6.0环境。 ​ Tools->options->directories->Include Files: C:\Program Files\Microsoft SQL Server\devtools\include ​ Tools->options->directories->Lib Files:C:\Program Files\Microsoft SQL Server\devtools\x86lib 注意:这些路径需要设为第一项。如下图所示: ​ Project->Settings->Link->Object/Library Modules,添加库文件: SQLakw32.lib,Caw32.lib 注意,两个文件之间用空格分开。 以上部分均在上机课上在师姐的指导下完成。 实验活动 1.​ 阅读和分析程序esql.sqc,解释程序的主要内容和主要数据结构。 程序的主要内容: 嵌入式SQL的使用,将SQL嵌入到C语言中,在SQL的数据库中通过SELECT语句获取数据,并将获得的信息通过主变量传递给主语言并打印输出。 给出的程序中将程序连接到WXF的SQL服务器上的pubs数据库上,查询满足以下SELECT语句的数据信息: select EmployeeID from orders where OrderID = 345 查询得到的结果由主变量EmployeeID传给主语言(C语言),C语言得到数据信息后打印输出。 程序分为以下几块: 1)​ 主变量的声明: 在EXEC SQL BEGIN DECLARE SECTION;和EXEC SQL END DECLARE SECTION;之间声明主变量EmployeeID。 2)​ 连接到DBMS: 连接到数据库的管理系统以便从中查询登陆的数据库。连接到数据库的语法为: EXEC SQL CONNECT TO SQLServer.DataBaseName USER logName.passwd 3)​ 查错: 为了更好的判断和查找错误以修改程序,所以在每次执行完SQL语句后都应该对sqlca所返回的状态值加以判断。若sqlcode等于0,则输出建立成功信息,否则报错。 4)​ 执行查询: 在这里嵌入SQL的查询语句EXEC SQL select EmployeeID INTO :EmployeeID from orders where OrderID = 345 ,并通过主变量EmployeeID返回查询到的结果,打印结果。 5)​ 断开所有连接: 执行完所有的数据库的访问后,断开所有的连接以释放系统资源。语法为: EXEC SQL DISCONNECT ALL; 数据结构: 主要为在数据库中存储的表,及针对表进行的查找。 2.​ 对程序esql.sqc作适当的修改,使之可以在本地系统上可以运行。进行预处理、编译、连接(lib连接),查看运行结果。 1)修改: 只需要对连接的服务器及数据库进行修改即可: EXEC SQL CONNECT TO SKY.pubs USER abc.abc; 同时由于pubs数据库中没有orders表,所以对SELECT语句也作了修改如下: EXEC SQL select emp_id INTO :EmployeeID from employee where lname = 'Accorti'; 2)进行预处理: 将esql.sqc文件放在C:\Program Files\Microsoft SQL Server\MSSQL\Binn目录下,运行cmd,进入C:\Program Files\Microsoft SQL Server\MSSQL\Binn目录,用nsqlprep esql.sqc的方式运行该文件,可以在C:\Program Files\Microsoft SQL Server\MSSQL\Binn中找到预编译后生成的.c文件。 运行过程显示如下: 3)连接操作已经在前面的初始化环境过程中完成。运行结果如下:不知为什么,在黄伟的机子上运行时,不管怎么换查询条件,结果总是0 但是在田超的机子上运行的时候,就能够得出正确结果。田超查询的是Northwind 数据库中的orders表,语句为EXEC SQL select EmployeeID INTO :EmployeeID from orders where OrderID = 10248 ; 运行结果如下: 经反复实验,首先排除了黄伟机子的问题,因为在田超的机器上运行也是0。于是又开始怀疑是数据库的问题。首先在程序中打印出了sqlcode,如图: 看到sqlcode<0,说明这个语句没有被执行。 开始以为是用户没有访问数据库的权限的问题,但是发现把用户改成sa,把默认数据库改为pubs依然存在这个问题。 将田超的sqc文件和黄伟的放在一起比较,最终发现原来是因为pubs数据库中emp_id的数据类型是empid,而在c中定义的主变量是int型,两者类型不匹配,造成了错误。 黄伟机子上的正确结果: 4)出现的怪异问题: 尽管之前的准备步骤都已经全部做完,但是在预编译时总是会出现以下问题, 每次都要重新把安装文件下的BIN和DEVTOOLS重新拷一遍到C盘SQLSERVER相应得路径下,然后再在DOS下运行setenv.bat和vcvars32.bat两个文件才能解决这个问题,有时甚至所有步骤都要重新做一次。不明白这是什么原因。我们在网上查到.bat格式的文件是批处理文件,它在日常的应用中可以发挥巨大的作用,但批处理命令执行的效率比较低,而且不小心会把里面的命令破坏掉。所以不知是不是因为.bat的稳定性不好。 思考: ​ sqlca结构中主要的数据项有哪些?我们常用的有哪些? sqlca是一个含有错误变量和状态指示符的数据结构。通过检查sqlca,应用程序能够检查出嵌入式SQL语句是否成功,并根据成功与否决定是否继续往下执行。与编译器自动在嵌入SQL语句中包含SQLCA数据结构。 从联机帮助上我们可以找到,SQLCA结构中主要的数据项如下(用结构体表示): 在SQLCA中我们经常用到的是sqlcode,它用来记录最近执行的SQL状态: "0" :表示该SQL语句被正确执行,无错误发生 >0 :表示执行了该语句,但遇到了错误 <0 :表示由于数据库、系统、网络或应用程序错误等等原因未执行该语句 ​ SQL操作的常见的错误类型有哪些?如何得到错误信息?举例说明 SQL中常见的错误类型有:通过下面的这些返回值,可以得到相应的出错信息。例子是我们上面黄伟机子上出现的问题,当时sqlcode返回值为-10039,我觉得它代表的应该是类型不匹配的错误,但不知道为什么,在下面这张表(联机丛书)里查不到这个值 Message number Run time/ compile time Description -4998 C Attempt to connect to the specified database server failed. -19031 C Unable to open bindfile. -19051 C Too many sections. -19101 R Statement too long. -19103 R Illegal %s value %s. Nonnumeric %s value %s. (Invalid number for the time-out value.) -19104 R/C Incorrect SQL statement syntax. -19199 C ESQL keyword(s) detected in PREPARE statement. -19306 C Host variable used but not declared. -19313 R Too few host variables. -19324 C Host variable may not be used in this context. -19408 R Invalid SQL data type for SQL_TYP_DECIMAL. -19413 R Data overflow occurred during decimal data conversion. -19422 R Unknown SQL Server data type. -19423 R Invalid destination data type. -19501 R No cursor declared. -19505 C Duplicate cursor name: %s. -19508 R Cursor not positioned on a row. -19514 R Cursor not prepared. -19517 R Cursor open attempted for non-SELECT prepared statement. -19521 R Open cursor failure for section %d of plans. -19523 R Failure to locate/close cursor. Section %d, plan %s. -19524 R Table for this cursor not updatable. -19525 R Attempt to fetch on unopened cursor. -19526 R No access plan for this cursor. -19527 R Could not get section for this cursor. -19528 R Connection for section %d of plan %s has NULL DBPROCESS. -19701 R NULL connection name.     Connection %s not found. -19702 R Connection name not found.     Attempt to close nonexistent connection. -19703 R Failed to get DBPROCESS. Autoconnect failure. -19706 R Login failure in section %d. -19707 R Duplicate connection name. -19822 R Improperly initialized user SQLDA. -19911 C The SQL data type specified for a host variable is invalid. -19913 C The token identifier has already been used. -19917 C Invalid or incorrect option to sqlainit(). -19946 C Cursor %s not declared. -19953 C Invalid call type. -19955 R Text not found in %s section %u. -19956 R Access plan section or statement text not found. -19957 R Access plan or statement text not found. -19994 R Cannot run next BEGIN DECLARE sections. Statement ignored. -19995 R END DECLARE encountered without preceding BEGIN DECLARE statement. Statement ignored. -19999 C An internal error occurred. 连接DBMS错误 通过sqlca中的变量sqlcode的返回值获得错误信息,当返回值为0时,表示连接成功,否则,连接失败。例如esql.sqc中用以下语句获得错误信息: if (SQLCODE == 0) { printf("Connection to SQL Server established\n"); } else { // 连接DBMS错误 printf("ERROR: Connection to SQL Server failed\n"); return (1); } ​ 如何处理SQL操作的错误信息? a) 对于DBMS连接错误: 检查连接是否正常:1)看SQL服务器是否启动 2)所连接的数据库是否存在 3)登录用户是否具备相应的权限(登录用户的数据库角色) 4)网络连接端口(1433)是否打开 b) 用sqlcode: 在程序的error段输入如下语句: 根据sqlcode的值查看联机帮助,根据在联机帮助上查到的错误信息检查相应的SQL语句,针对性的进行修改,直到其能正确运行 c)WHENEVER语句: 在每条嵌入式SQL语句之后立即编写一条检查SQLCODE值的程序是一件很繁琐的事情,为了简化错误处理,可以使用WHENEVER语句: WHENEVER是说明语句,不是可执行语句,不返回SQLCODE,只是根据SQLCA中的返回码指定相应的措施。它通知预编译程序在每条可执行嵌入式SQL语句之后自动生成错误处理程序,并指定了错误处理操作。 用户可以使用WHENEVER语句通知预编译程序去如何处理三种异常处理: lWHENEVER SQLERROR action:表示一旦sql语句执行时遇到错误信息,则执行action,action中包含了处理错误的代码(SQLCODE<0)。 lWHENEVER SQLWARNING action:表示一旦sql语句执行时遇到警告信息,则执行aciton,即action中包含了处理警报的代码(SQLCODE=1)。 lWHENEVER NOT FOUND:表示一旦sql语句执行时没有找到相应的元组,则执行action,即action包含了处理没有查到内容的代码(SQLCODE=100)。 针对上述三种异常处理,用户可以指定预编译程序采取以下三种行为(action): lWHENEVER …GOTO:通知预编译程序产生一条转移语句。 lWHENEVER…CONTINUE:取消先前的WHENEVER语句的作用,通知预编译程序让程序的控制流转入到下一个主语言语句。 lWHENEVER…CALL:通知预编译程序调用函数。 例如: EXEC SQL WHENEVER SQLERROR GOTO er1 EXEC SQL INSERT INTO student VALUES (‘s1’, ..) EXEC SQL INSERT INTO student VALUES (‘s2’, ..) 经过预编译后会转换为以下语句: EXEC SQL INSERT INTO student VALUES (‘s1’, ..) If (sqlca.sqlcode < 0) goto er1 ; EXEC SQL INSERT INTO student VALUES (‘s2’, ..) If (sqlca.sqlcode < 0) goto er1; 出错处理函数如下: void hSQLError(char *msg) { printf("\n%s,%ld,%s\n", msg,sqlca.sqlcode,(char *)sqlca.sqlerrm.sqlerrmc); EXEC SQL ROLLBACK RELEASE; exit(-1); } ​ 如何断开连接,并释放资源清除环境?建议尝试程序esql.sqc的方法以外的别的方法。 释放资源清除环境: EXEC SQL COMMIT WORK RELEASE; 断开连接: EXEC SQL DISCONNECT ALL;//断开所有连接 EXEC SQL DISCONNECT connection_name;//如果在连接时定义了连接名,则可通过断开连接名来断开连接 (如定义了EXEC SQL CONNECT TO SKY.pubs AS Myconnection USER sa.sa;就可以通过EXEC SQL DISCONNECT Myconnection断开和数据库pubs的连接,此语句对和其他数据库的连接无效) EXEC SQL DISCONNECT CURRENT;//表示断开当前连接 3.​ 阅读和分析程序odbc.cpp,解释程序的主要内容和主要数据结构。 程序的主要内容: 在连接到的数据源上查询sno=’s1’的sname和city. 程序主要分为以下几块: 以下为程序开始部分: 1)​ 定义retcode, henv, hdbc,hstmt四个变量,分别对应错误返回码, 环境句柄, 数据库连接句柄,语句句柄; 2)​ 用SQLAllocHandle分配环境变量,同时用SQLSetEnvAttr告诉odbc这是一个odbc3.0的应用程序; 3)​ 用SQLAllocHandle分配连接句柄; 4)​ 用SQLConnect连接数据库; 以下为程序主体部分: 5)​ 使用SQLAllocHandle分配语句句柄; 6)​ 用SQLExecDirect或别的语句执行表的创建,查询,及修改; 7)​ 用SQLBindCol把某个结果属性列绑定到变量上,用SQLFetch获取查询信息;或是用SQLFetch和SQLGetData来获取查询信息。 以下为程序结束部分,释放所有句柄和环境空间: 8)​ 用SQLFreeHandle释放语句句柄; 9)​ 用SQLDisconnect断开连接; 10)​ 用SQLFreeHandle释放连接句柄 11)​ 用SQLFreeHandle释放环境句柄 心得: 在程序中申请三个句柄的过程是环环相扣的,系统在分配句柄的过程中要用到以前句柄的参数。而在释放句柄的时候,是要按照申请句柄的逆序来逐一释放的。 程序的主要数据结构: 我认为是三个句柄以及在处理结果的时候SQLBindCol(或SQLGetData)函数所开辟的缓冲区。 4.​ 对程序odbc.cpp作适当的修改,使之可以在本地系统上可以运行。进行编译、连接,查看运行结果。 首先配置ODBC,为SQL Server添加数据源,如下图: 添加了sqlhw数据源,其配置如下: 更改默认数据库为exp 之后要在程序中对数据源以及登录ID和密码做修改,修改如下: char* szDSN = "sqlhw"; char* szUID = "abc";//log name char* szAuthStr = "abc";//passward 并对SELECT语句做相应的修改,修改为如下查询语句: SElECT sname,city FROM provider where sno='s1' 查询结果如下: 思考: a)​ 如何判断调用ODBC函数是否正确?常见的错误类型有哪些?如何得到错误信息?举例说明 在驱动程序执行ODBC函数的时候会根据其执行情况返回不同的代码,数据类型为ERTCODE。函数的返回码有一下几种取值: ​ SQL_SUCCESS 返回值为0,执行成功,无错误信息。 ​ SQL_SUCCESS_WITH_INFO返回值大于0, 函数执行成功,但可能产生了非致命的错误。 ​ SQL_ERROR 返回值小于0,函数执行失败。 ​ 此外还有一些在我们这次实验用不多的返回值。 可以使用两个 ODBC 函数调用来检索 ODBC 信息: SQLGetDiagRec 和 SQLGetDiagField。 若要获得 SQLState、pfNative 和 ErrorMessage 诊断字段中与 ODBC 有关的主要信息,则调用 SQLGetDiagRec,直到它返回 SQL_NO_DATA 为止。可对每个诊断记录调用 SQLGetDiagField 以检索个别字段。所有驱动程序专用字段都必须用 SQLGetDiagField 检索。 SQLGetDiagRec 和 SQLGetDiagField 由 ODBC 驱动程序管理器而不是个别的驱动程序来处理。ODBC 驱动程序管理器在连接成功建立之前,不高速缓存驱动程序专用诊断字段。在连接成功建立之前无法对驱动程序专用诊断字段调用 SQLGetDiagField。这还包括 ODBC 连接命令,即便它们返回 SQL_SUCCESS_WITH_INFO。驱动程序专用诊断字段在下一个 ODBC 函数调用之前不可用。 b)​ 如何判断缓冲区中的数据已经读取完毕? 通过返回 SQL_NO_DATA来判断。 SQLFetch 发出结果集结束的信号时,调用 SQLMoreResults 确定是否还有其它结果集可用。 如果返回 SQL_SUCCESS,表明有另一个结果集可用。 如果返回 SQL_NO_DATA,表明没有其它结果集可用。 如果返回 SQL_SUCCESS_WITH_INFO 或 SQL_ERROR,则调用 SQLGetDiagRec 确定 PRINT 或 RAISERROR 语句的输出是否可用。 c)​ 读取数据,有两种方法:SQLBindCol和SQLGetData,他们有什么不一样? SQLBindCol和SQLGetData的函数参数顺序完全一样,它们的作用也基本相同,不同之处在于: ①SQLBindCol以列为单位为结果集分配空间。一次可以处理多行。而SQLGetData一次只能从当前行中读取一列数据,他需要SQLFetch为其定位游标。 ②SQLGetData可以读取较长的数据,因为在函数的参数中columnLen为结果中未读取数据的长度,不是该列全部数据的长度,而SQLBindCol的这个参数为读取列的全部数据长度。 d)​ 如何判断结果为空值? 在调用SQLBindCol函数的时候有参数columnLen返回读取列的实际长度,当columnLen的值小于0的时候表示该列为空值。本实验中的null值就是通过它来判断和输出的。 e)​ 可否重新读取该缓冲区的数据?(假设已经通过SQLFetch读了一遍)。如果可以,怎么操作?如果不可以,为什么? 我们认为是可以实现的,因为游标的实现上应该就是一个缓冲区和一个指针,再c中对文件的操作里有把指针移动到文件开始位置的函数,但是在odbc中我们没有查到类似的函数。所以没能实现。 编写程序,分别使用嵌入式SQL和ODBC调用实现如下的功能: 1.​ 编写程序,创建下列关系表,并插入数据。 create table provider ( SNO char(5) primary key, SNAME char(10) not null, STATUS int, CITY char(10) ); insert into provider values('S1','精益','20','天津'); insert into provider values('S2','胜锡','10','北京'); insert into provider values('S3','东方红','30','天津'); insert into provider values('S4','丰泰盛','20','天津'); insert into provider values('S5','为民','30','上海'); insert into provider values('S6','通天','25',null); 2.​ 实现从employee表中读取sname和city属性的数据,并在屏幕上显示出来。 说明:以上两个步骤在两种方法中都是在同一个程序中实现,关键部分用黄色标出,exp为在企业管理器中新建的数据库。 源程序如下: A)用嵌入式SQL实现: #include #include EXEC SQL INCLUDE sqlca; int main() { EXEC SQL BEGIN DECLARE SECTION; //主变量 char sno[5]; char sname[10]; int status; char city[10]; short cityInd;//指示变量 EXEC SQL END DECLARE SECTION; printf("This is my Embedded SQL for C application\n"); EXEC SQL CONNECT TO SKY.exp USER sa.sa;//连接到数据库 if (SQLCODE == 0) { printf("Connection to SQL Server established\n"); } else { // 连接DBMS错误 printf("ERROR: Connection to SQL Server failed\n"); return (1); } //下面为程序的主体部分 EXEC SQL create table provider ( sno char(5) primary key, sname char(10) not null, status int, city char(10) ); EXEC SQL insert into provider values('S1','精益','20','天津'); EXEC SQL insert into provider values('S2','胜锡','10','北京'); EXEC SQL insert into provider values('S3','东方红','30','天津'); EXEC SQL insert into provider values('S4','丰泰盛','20','天津'); EXEC SQL insert into provider values('S5','为民','30','上海'); EXEC SQL insert into provider values('S6','通天','25',null); EXEC SQL WHENEVER SQLERROR GOTO error;//错误处理 EXEC SQL WHENEVER NOT FOUND GOTO done; EXEC SQL DECLARE providerCursor CURSOR FOR SELECT sname, city FROM provider; EXEC SQL OPEN providerCursor ; for ( ; ; ) { /* Fetch next row of the result table */ EXEC SQL FETCH providerCursor INTO :sname,:city:cityInd; /* display data */ printf ("Sname:%s ",sname); if ( cityInd< 0)//通过设置指示变量,并判断指示变量的值<0来确认和输出null值 printf ("City:NULL\n"); else printf ("City:%s\n",city); } error: printf ("SQL error %d\n",sqlca->sqlcode);//若照书上写sqlca.sqlcode编译报语法错 done: /* Close the cursor before completing*/ EXEC SQL WHENEVER SQLERROR continue; EXEC SQL CLOSE providerCursor; EXEC SQL COMMIT WORK RELEASE; //断开连接 EXEC SQL DISCONNECT ALL; return 0; } 以上程序已在VC中调试成功,实验结果显示为: 在查询分析器中查询该表内容如下: 实验中出现的问题: 开始的时候始终不显示空值,甚至不能将有空值的元组插入到数据库中,显示结果如下: 分别做了以下尝试; 1)将语句: EXEC SQL insert into provider values('S6','通天','25',null); 改为: EXEC SQL insert into provider(sno,sname,status)values('S6','通天','25'); 本以为通过不填city可以将city赋空,但运行结果没有改变。 2)将语句: EXEC SQL insert into provider values('S6','通天','25',null); 改为: EXEC SQL insert into provider values('S6','通天','25',’’); 结果如下: 与预期的不相符,在City的位置应该显示NULL却没有显示。 3)由于在程序里的改动不能使结果发生变化,我们在企业管理器中做了相应的修改: 在企业管理器中打开exp数据库,右键点击表provider,选择设计表,将city允许空的位置打对勾,如下: 通过这样的改变后才得到以上的正确结果。 在改动过程中出现了一下提示: 设计表:(允许空打勾后出现的提示:) 'provider' 表 - 警告:一或多个现存列的 ANSI_PADDING 设置为 'off' ,并且将以 ANSI_PADDING 为 'on' 的设置被重新创建。 - 警告:表的 ANSI_NULLS 设置为 'off ',并且将以 ANSI_NULLS 为 'on' 的设置被重新创建。 4)受到上面警告中红色字段的启发,发现嵌入式SQL在建表时如不说明则默认为非空,因而将建表语句做以下修改: EXEC SQL create table provider ( sno char(5) primary key, sname char(10) not null, status int, city char(10) null ); 通过这样修改得到的运行结果如下: 另外,实验中还遇到一些其他问题 ①出现过以下提示: 发现是SELECT语句中查询有错,在SELECT语句中的引号是用智能拼音打入的,不能识别,修改后就没有这个错误了。 ②刚开始的时候由于在A)步骤中已经建立了provider表了,因而最开始时出现了以下情况:所以在以后做实验的时候要先珊掉provider表。也可在程序中加一个if判断,如果表存在,那么就删掉原表。 EXEC SQL if exists (select name from sysobjects where name='provider') drop table sa.provider ; B)用ODBC实现,源程序如下: #include #include #include #include #include #include #include #define MAXBUFLEN 255 #define MaxNameLen 20 下面这句是预处理语句,使程序在编译时分别编译绑定段或SQLGetdata段 //#define SQLBINDCOL SQLHENV henv = SQL_NULL_HENV;//定义环境句柄 SQLHDBC hdbc1 = SQL_NULL_HDBC;//定义数据库连接句柄 SQLHSTMT hstmt1 = SQL_NULL_HSTMT;//定义语句句柄 int main() { RETCODE retcode;//错误返回码 // Allocate the ODBC Environment and save handle. retcode = SQLAllocHandle (SQL_HANDLE_ENV, NULL, &henv); if(retcode < 0 )//错误处理 { cout<<"allocate ODBC Environment handle errors."<0) printf("name = %s city = %s\n", name,city); else printf("name = %s city = NULL\n", name,city); } #else while(1 ) { retcode = SQLFetch(hstmt1); if(retcode == SQL_NO_DATA) break; retcode = SQLGetData(hstmt1, 1, SQL_C_CHAR, name, MaxNameLen, &columnLen); retcode = SQLGetData(hstmt1, 2, SQL_C_CHAR, city, MaxNameLen, &columnLen); if(columnLen>0) printf("name = %s city = %s\n", name,city); else printf("name = %s city = NULL\n", name,city); } #endif /* Clean up.*/ SQLFreeHandle(SQL_HANDLE_STMT, hstmt1); SQLDisconnect(hdbc1); SQLFreeHandle(SQL_HANDLE_DBC, hdbc1); SQLFreeHandle(SQL_HANDLE_ENV, henv); return(0); } 执行结果为: 在查询分析器中查看该表: 实验中出现的问题: 1)建表要写在一行,否则报错: c:\documents and settings\administrator\桌面\嵌入式sql和odbc\odbc.cpp(74) : error C2001: newline in constant 2)以下程序段在没有加if...else..语句时,执行结果如下图: while(1 ) { retcode = SQLFetch(hstmt1); if(retcode == SQL_NO_DATA) break; retcode = SQLGetData(hstmt1, 1, SQL_C_CHAR, name, MaxNameLen, &columnLen); retcode = SQLGetData(hstmt1, 2, SQL_C_CHAR, city, MaxNameLen, &columnLen); if(columnLen>0) printf("name = %s city = %s\n", name,city); else printf("name = %s city = NULL\n", name,city); 添加了黄色注明部分后才能够得出正确的结果。 如果用绑定的方式做,也照此处理。我们通过定义#define SQLBINDCOL和注掉这一句,验证了绑定的方式和SQLGetData的方式都是可以正确执行的。 3)用嵌入式SQL建表的时候,默认为非空,空值不可插入,而在ODBC中如不约束则默认为空: 实验体会: 本次实验主要是对嵌入式SQL和ODBC的使用,环境以及连接的配置很重要,是实 验的前提条件,还没有配置的时候有点无从下手的感觉,之后在上机课上在师姐的指导下完 成了环境的配置,并且各项必要的连接也已经做完,这使实验的其他步骤能够顺利的开始进 行。 总体上来说,程序部分的修改相对比较简单,能够很快的发现错误做相应的修改然后使 之运行出正确结果,但是预编译的过程相对繁琐,每次都要重复做环境和连接设置的一些步 骤才能正确的预编译,这个问题还不是很明白原因。另外esql.sqc的修改原本是非常简单的, 结果因为忽略了一个小问题:在主变量中EmployeeID是定义为int型的,而修改查询时查 询的内容都是char型的,结果一直是0,找了整整一天才
本文档为【嵌入式SQL和ODBC的使用】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_159992
暂无简介~
格式:doc
大小:502KB
软件:Word
页数:0
分类:互联网
上传时间:2011-05-21
浏览量:17