您的位置:首页 > 数据库

嵌入式SQL(E-SQL)简介 (1)

2012-12-07 17:14 162 查看


第一节 什么是嵌入SQL语言?

SQL是一种双重式语言,它既是一种用于查询和更新的交互式数据库语言,又是一种应用程序进行数据库访问时所采取的编程式数据库语言。SQL语言在这两种方式中的大部分语法是相同的。在编写访问数据库的程序时,必须从普通的编程语言开始(如C语言),再把SQL加入到程序中。所以,嵌入式SQL语言就是将SQL语句直接嵌入到程序的源代码中,与其他程序设计语言语句混合。专用的SQL预编译程序将嵌入的SQL语句转换为能被程序设计语言(如C语言)的编译器识别的函数调用。然后,C编译器编译源代码为可执行程序。

各个数据库厂商都采用嵌入SQL语言,并且都符合ANSI/ISO的标准。所以,如果采用合适的嵌入SQL语言,那么可以使得你的程序能够在各个数据库平台上执行(即:源程序不用做修改,只需要用相应数据库产品的预编译器编译即可)。当然,每个数据库厂商又扩展了ANSI/ISO的标准,提供了一些附加的功能。这样,也使得每个数据库产品在嵌入SQL方面有一些区别。本章的目标是,对所有的数据库产品的嵌入SQL做一个简单、实用的介绍。

当然,嵌入SQL语句完成的功能也可以通过应用程序接口(API)实现。通过API的调用,可以将SQL语句传递到DBMS,并用API调用返回查询结果。这个方法不需要专用的预编译程序。

1.1 嵌入SQL程序的组成元素

我们以IBM的DB2嵌入SQL为例,来看看嵌入SQL语句的组成元素。

例1、连接到SAMPLE数据库,查询LASTNAME为JOHNSON的FIRSTNAME信息。

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "util.h"

#include <sqlca.h>

EXEC SQL INCLUDE SQLCA; (1)

main()

{

EXEC SQL BEGIN DECLARE SECTION; (2)

char firstname[13];

char userid[9];

char passwd[19];

EXEC SQL END DECLARE SECTION;

EXEC SQL CONNECT TO sample; (3)

EXEC SQL SELECT FIRSTNME INTO :firstname (4)

FROM employee

WHERE LASTNAME = 'JOHNSON';(4)

printf( "First name = %s\n", firstname );

EXEC SQL CONNECT RESET; (5)

return 0;

}

上面是一个简单的静态嵌入SQL语句的应用程序。它包括了嵌入SQL的主要部分:

(1)中的include SQLCA语句定义并描述了SQLCA的结构。SQLCA用于应用程序和数据库之间的通讯,其中的SQLCODE返回SQL语句执行后的结果状态。

(2)在BEGIN DECLARE SECTION和END DECLARE SECTION之间定义了宿主变量。宿主变量可被SQL语句引用,也可以被C语言语句引用。它用于将程序中的数据通过SQL语句传给数据库管理器,或从数据库管理器接收查询的结果。在SQL语句中,主变量前均有“:”标志以示区别。

(3)在每次访问数据库之前必须做CONNECT操作,以连接到某一个数据库上。这时,应该保证数据库实例已经启动。

(4)是一条选择语句。它将表employee中的LASTNAME为“JOHNSON”的行数据的FIRSTNAME查出,并将它放在firstname变量中。该语句返回一个结果。可以通过游标返回多个结果。当然,也可以包含update、insert和delete语句。

(5)最后断开数据库的连接。

从上例看出,每条嵌入式SQL语句都用EXEC SQL开始,表明它是一条SQL语句。这也是告诉预编译器在EXEC SQL和“;”之间是嵌入SQL语句。如果一条嵌入式SQL语句占用多行,在C程序中可以用续行符“\”。

1.2 什么是静态SQL和动态SQL?

嵌入SQL语言,分为静态SQL语言和动态语言两类。静态SQL语言,就是在编译时已经确定了引用的表和列。宿主变量不改变表和列信息。可以使用主变量改变查询参数值,但是不能用主变量代替表名或列名。

动态SQL语言就是:不在编译时确定SQL的表和列,而是让程序在运行时提供,并将SQL语句文本传给DBMS执行。静态SQL语句在编译时已经生成执行计划。而动态SQL语句,只有在执行时才产生执行计划。动态SQL语句首先执行PREPARE语句要求DBMS分析、确认和优化语句,并为其生成执行计划。DBMS还设置SQLCODE以表明语句中发现的错误。当程序执行完“PREPARE”语句后,就可以用EXECUTE语句执行执行计划,并设置SQLCODE,以表明完成状态。

1.3 什么是SQLCA?

应用程序执行时,每执行一条SQL语句,就返回一个状态符和一些附加信息。这些信息反映了SQL语句的执行情况,它有助于用户分析应用程序的错误所在。这些信息都存放在sqlca.h的sqlca结构中。如果一个源文件中包含SQL语句,则必须要在源程序中定义一个SQLCA结构,而且名为SQLCA。最简单的定义方法是在源文件中加入一些语句:EXEC SQL INCLUDE sqlca.h。每个数据库产品都提供了SQLCA结构。

1.4 什么是SQLDA?

我们知道,动态SQL语句在编译时可能不知道有多少列信息。在嵌入SQL语句中,这些不确定的数据是通过SQLDA完成的。SQLDA的结构非常灵活,在该结构的固定部分,指明了多少列等信息,在该结构的后面有一个可变长的结构,说明每列的信息。在从数据库获得数据时,就可以采用SQLDA来获得每行的数据。各个数据库产品的SQLDA结构都不完全相同。

第二节 SYBASE SQL Server嵌入式SQL语言

2.1 一个嵌入SQL语言的简单例子

我们首先来看一个简单的嵌入式SQL语言的程序(C语言):用sa(口令为password)连接数据库服务器,并将所有书的价格增加10%。这个例子程序如下:

例1、

/*建立通讯区域*/

Exec sql include sqlca;

main()

{

/*声明宿主变量*/

EXEC SQL BEGIN DECLARE SECTION;

char user[30],passwd[30];

EXEC SQL END DECLARE SECTION;

/*错误处理*/

EXEC SQL WHENEVER SQLERROR CALL err_p();

/*连接到SQL SERVER服务器*/

printf("\nplease enter your userid ");

gets(user);

printf("\npassword ");

gets(passwd);

exec sql connect :user identified by :passwd;

exec sql use pubs2;

EXEC SQL update titles set price=price*1.10;

EXEC SQL commit work;

/*断开数据库服务器的连接*/

Exec sql disconnect all;

return (0);

}

/*错误处理程序*/

err_p()

{

printf("\nError occurred: code %d.\n%s", \

sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);

}

从上面这个例子,我们看出嵌入SQL的基本特点是:

1、每条嵌入式SQL语句都用EXEC SQL开始,表明它是一条SQL语句。这也是告诉预编译器在EXEC SQL和“;”之间是嵌入SQL语句。

2、如果一条嵌入式SQL语句占用多行,在C程序中可以用续行符“\”,在Fortran中必须有续行符。其他语言也有相应规定。

3、每一条嵌入SQL语句都有结束符号,如:在C中是“;”。

4、嵌入SQL语句的关键字不区分大小写。

5、可以使用“/*….*/”来添加注释。也可以使用“--”来添加注释。

2.2 嵌入SQL的处理过程

嵌入SQL的处理过程如下图所示:

 

 

 

图6-1 SYBASE SQL SERVER嵌入SQL程序处理过程

嵌入SQL程序的后缀为.cp。嵌入SQL处理的第一步是预编译。预编译器(cpre.exe)处理过程分为两个小步:

第一小步:语法分析。检查嵌入SQL语句的语法正确性。

第二小步:增加一些预编译器变量,并注释了所有的嵌入的SQL语句,将嵌入SQL语句转换为对client-library中函数的调用(注意:在连接时,编译后的代码需要使用client-library中的库文件)。如果在编译时,设置一些选项,则生成存储过程。预编译后可能产生3个文件:一个C文件(肯定产生),一个列表文件(需要在编译时设置选项,才能产生)和一个isql脚本文件(需要在编译时设置选项,才能产生)。列表文件包含了输入文件的源语句和一些警告信息和错误信息。Isql脚本文件包含了预编译器产生的存储过程脚本文件。这个存储过程是用T-SQL写的。总之,预编译器的处理方法是,注释了嵌入的SQL语句,用一些特定的函数代替。

第二步是C源程序的编译和链接。cl是编译和链接命令的集成命令,编译的结果是产生.obj,在链接时,将C的系统库和SQL Server提供的库文件同目标文件连接在一起。最后生成.exe。也可以使用SET LIB语句设置库文件的环境信息。

2.3 嵌入SQL语句总览

除了print、readtext和writetext外,大多数的Transact-SQL语句都可以在嵌入SQL中使用。嵌入SQL语句的语法为:“exec sql [at connection_name] sql_statement; ”。那么,你可以用Transact-SQL语句来替代sql_statement 就可以完成嵌入SQL的编写。(同T-SQL相比,嵌入SQL提供了:自动数据类型转换、动态SQL、SQLCA数据结构等功能。)

但是,也有一些嵌入式SQL所特有的语句,有些嵌入式SQL语句的名字同Transact-SQL语句相同,但是语句的语法有所不同。

嵌入SQL语句应该包含五个步骤:

1)、通过SQLCA建立应用程序和SQL SERVER的SQL通信区域。

2)、声明宿主变量。

3)、连接到SQL SERVER。

4)、通过SQL语句操作数据。

5)、处理错误和结果信息。

嵌入式SQL语句分为静态SQL语句和动态SQL语句两类。下面我们按照功能讲解这些语句。本节讲解静态SQL语句的作用。动态SQL语句将在下一节讲解。同动态SQL相关的一些语句也在下一节中讲解。

2.3.1 宿主变量

1)、声明方法

宿主变量(host variable)就是在嵌入式SQL语句中引用主语言说明的程序变量(如例中的user[31]变量)。如:

EXEC SQL BEGIN DECLARE SECTION;

char user[31],passwd[31];

EXEC SQL END DECLARE SECTION;

…………

exec sql connect :user identified by :passwd;

………….

在嵌入式SQL语句中使用主变量前,必须采用BEGIN DECLARE SECTION 和END DECLARE SECTION之间给主变量说明。这两条语句不是可执行语句,而是预编译程序的说明。主变量是标准的C程序变量。嵌入SQL语句使用主变量来输入数据和输出数据。C程序和嵌入SQL语句都可以访问主变量。

另外,在定义宿主变量时也可以使用client-library定义的数据类型,如:CS_CHAR。这些定义存放在cspublic.h文件中。如:

EXEC SQL BEGIN DECLARE SECTION;

CS_CHAR user[30],passwd[30];

EXEC SQL END DECLARE SECTION;

client-library定义的数据类型共有:CS_BINARY、CS_BIT、 CS_BOOL、 CS_CHAR、 CS_DATETIME、CS_DATETIME4、 CS_DECIMAL、 CS_FLOAT、 CS_REAL、CS_IMAGE、 CS_INT、 CS_MONEY、 CS_MONEY4、 CS_NUMERIC、CS_RETCODE、 CS_SMALLINT、 CS_TEXT、 CS_TINYINT、CS_VARBINARY、 CS_VARCHAR、 CS_VOID。

为了便于识别主变量,当嵌入式SQL语句中出现主变量时,必须在变量名称前标上冒号(:)。冒号的作用是,告诉预编译器,这是个主变量而不是表名或列名。不能在声明时,初始化数组变量。

由上可知,SYBASE SQL SERVER使用宿主变量传递数据库中的数据和状态信息到应用程序,应用程序也通过宿主变量传递数据到SYBASE数据库。根据上面两种功能,宿主变量分为输出宿主变量和输入宿主变量。在SELECT INTO和FETCH语句之后的宿主变量称作“输出宿主变量”,这是因为从数据库传递列数据到应用程序。如:

exec sql begin declare section;

CS_CHAR id[5];

exec sql end declare section;

exec sql select title_id into :id from titles

where pub_id = "0736" and type = "business";

除了SELECT INTO和FETCH语句外的其他SQL语句(如:INSERT、UPDATE等语句)中的宿主变量,称为“输入宿主变量”。这是因为从应用程序向数据库输入值。如:

exec sql begin declare section;

CS_CHAR id[7];

CS_CHAR publisher[5];

exec sql end declare section;

...

exec sql delete from titles where title_id = :id;

exec sql update titles set pub_id = :publisher

where title_id = :id;

另外,也可以通过宿主变量获得存储过程的执行状态信息。如:

exec sql begin declare section;

CS_SMALLINT retcode;

exec sql end declare section;

exec sql begin transaction;

exec sql exec :retcode = update_proc;

if (retcode != 0)

{

exec sql rollback transaction;

也可以通过宿主变量获得存储过程的返回值。如:

exec sql exec a_proc :par1 out, :par2 out;

2)、主变量的数据类型

SYBASE SQL SERVER支持的数据类型与程序设计语言支持的数据类型之间有很大差别。这些差别对主变量影响很大。一方面,主变量是一个用程序设计语言的数据类型说明并用程序设计语言处理的程序变量;另一方面,在嵌入SQL语句中用主变量保存数据库数据。所以,在嵌入SQL语句中,必须映射C数据类型为合适的SQL Server数据类型。必须慎重选择主变量的数据类型。在SQL SERVER中,预编译器能够自动转换兼容的数据类型。请看下面这个例子:

EXEC SQL BEGIN DECLARE SECTION;

int hostvar1 = 39;

char *hostvar2 = "telescope";

float hostvar3 = 355.95;

EXEC SQL END DECLARE SECTION;

EXEC SQL UPDATE inventory

SET department = :hostvar1

WHERE part_num = "4572-3";

EXEC SQL UPDATE inventory

SET prod_descrip = :hostvar2

WHERE part_num = "4572-3";

EXEC SQL UPDATE inventory

SET price = :hostvar3

WHERE part_num = "4572-3";

在第一个update语句中,department列为smallint数据类型(integer ),所以应该把hostvar1定义为int数据类型(integer)。这样的话,从C到SQL Server的hostvar1可以直接映射。在第二个update语句中,prod_descip列为varchar数据类型,所以应该把hostvar2定义为字符数组。这样的话,从C到SQL Server的hostvar2可以从字符数组映射为varchar数据类型。在第三个update语句中,price列为money数据类型。在C语言中,没有相应的数据类型,所以用户可以把hostvar3定义为C的浮点变量或字符数据类型。SQL
Server可以自动将浮点变量转换为money数据类型(输入数据),或将money数据类型转换为浮点变量(输出数据)。

注意的是,如果数据类型为字符数组,那么SQL Server会在数据后面填充空格,直到填满该变量的声明长度(CS_CHAR数据类型除外)。

下表列出了C的数据类型和SQL SERVER数据类型的一些兼容关系:

可兼容的C数据类型分配的SQL Server数据类型SYBASE提供的数据类型描述

shortSmallintCS_SMALLINT2字节整数

IntSmallintCS_SMALLINT2字节整数

LongIntCS_INT4字节整数

FloatRealCS_REAL4字节浮点数

DoubleFloatCS_FLOAT8字节浮点数

CharCarchar[X]VARCHARCS_CHAR字符数据类型

Unsigned charBinaryVarbinaryCS_BINARYBinary数据类型

Unsigned char tinyintCS_TINYINT1字节整数

无DatetimeCS_DATETIME8字节datetime类型

无SmalldatetimeCS_DATETIME44字节datetime类型

无DecimalCS_DECIMALDecimal数据类型

无numericCS_NUMERICNumeric数据类型

无MoneyCS_MONEY8字节money类型

无smallmoneyCS_MONEY44字节money类型

Unsigned char TextCS_TEXT文本数据类型

Unsigned char imageCS_IMAGE图象数据类型

无booleanCS_BITBit数据类型

因为C没有date或time数据类型,所以SQL Server的date或time列将被转换为字符。缺省情况下,使用以下转换格式:mm dd yyyy hh:mm:ss[am | pm]。你也可以使用字符数据格式将C的字符数据存放到SQL Server的date列上。你也可以使用Transact-SQL中的convert语句来转换数据类型。如:SELECT CONVERT(char, date, 8) FROM sales。

下表是从SQL SERVER数据类型到C的数据类型的转换关系:

SQL SERVER 数据类型C数据类型

CS_TINYINTCS_SMALLINTCS_INTCS_REALCS_CHARCS_MONEYCS_DATETIME

char可以可以可以可以可以可以可以

varchar可以可以可以可以可以可以可以

bit可以可以可以可以可以可以

binary可以可以可以可以可以可以

tinyint可以可以可以可以可以可以

smallint可以可以可以可以可以可以

int可以可以可以可以可以可以

float可以可以可以可以可以可以

money可以可以可以可以可以可以

datetime可以可以

decimal可以可以可以可以可以可以

numeric可以可以可以可以可以可以

下表是从C的数据类型到SQL SERVER数据类型的转换关系:

C数据类型SQL SERVER数据类型

tinyintbitsmallintintfloatcharmoneydatetimedecimalnumeric

Unsigned char可以可以可以可以可以需要自己转换可以可以可以

Short int可以可以可以可以可以需要自己转换可以可以可以

Long int可以可以可以可以可以需要自己转换可以可以可以

Double float可以可以可以可以可以需要自己转换可以可以可以

Char需要自己转换需要自己转换需要自己转换需要自己转换需要自己转换可以需要自己转换可以需要自己转换需要自己转换

money可以可以可以可以可以可以可以可以可以

datetime需要自己转换可以

3)、主变量和NULL

大多数程序设计语言(如C)都不支持NULL。所以对NULL的处理,一定要在SQL中完成。我们可以使用主机指示符变量(host indicator variable)来解决这个问题。在嵌入式SQL语句中,主变量和指示符变量共同规定一个单独的SQL类型值。如:

EXEC SQL SELECT price INTO :price :price_nullflag FROM titles

WHERE au_id = "mc3026"

其中,price是主变量,price_nullflag是指示符变量。

使用指示符变量的语法为:: host_variable [[indicator] : indicator_variable]。其中,indicator可以不写。针对宿主变量是输出宿主变量,还是输入宿主变量。指示符变量共分两种情况。

情况1:同输出宿主变量一起使用,则indicator_varibale为:

l-1。表示相应列值为NULL。表示主变量应该假设为NULL。(注意:宿主变量的实际值是一个无关值,不予考虑)。

l0。表示非NULL值。该变量存放了非NULL的列值。

l>0。表示宿主变量包含了列值的截断值。该指示变量存放了该列值的实际长度。

下面是一个同输出宿主变量一起使用的指示变量的例子:

exec sql begin declare section;

CS_CHAR id[6];

CS_SMALLINT indic;

CS_CHAR pub_name[41];

exec sql end declare section;

exec sql select pub_id into :id indicator :indic

from titles where title

like "%Stress%";

if (indic == -1)

{

printf("\npub_id is null");

}

else

{

exec sql select pub_name into :pub_name

from publishers where pub_id = :id;

printf("\nPublisher: %s", pub_name);

情况2:同输入宿主变量一起使用,则indicator_varibale为:

l-1。表示主变量应该假设为NULL。(注意:宿主变量的实际值是一个无关值,不予考虑)。应该将NULL赋值给相应列。

l0。表示非NULL值。该变量存放了非NULL值。应该将宿主变量的值赋值给相应列。

对于以下语句:

EXEC SQL SELECT price INTO :price :price_nullflag FROM titles

WHERE au_id = "mc3026"

如果不存在mc3026写的书,那么price_nullflag为-1,表示price为NULL;如果存在,则price为实际的价格。下面我们再看一个update的例子:

EXEC SQL UPDATE closeoutsale

SET temp_price = :saleprice :saleprice_null, listprice = :oldprice;

如果saleprice_null是-1,则上述语句等价为:

EXEC SQL UPDATE closeoutsale

SET temp_price = null, listprice = :oldprice;

我们也可以在指示符变量前面加上“INDICATOR”关键字,表示后面的变量为指示符变量。如:

EXEC SQL UPDATE closeoutsale

SET temp_price = :saleprice INDICATOR :saleprice_null;

指示符变量也是宿主变量,定义指示符变量同定义宿主变量一样。它应该是一个2个字节的整数(short或CS_SMALLINT)。

2.3.2 连接数据库

在程序中,使用CONNECT语句来连接数据库。该语句的完整语法为:

exec sql connect : user [identified by : password]

[at : connection_name] [using : server]

[labelname labelname labelvalue labelvalue...] 其中,

lserver为服务器名。如省略,则为本地服务器名。

lconnection_name为连接名。可省略。如果你仅仅使用一个连接,那么无需指定连接名。可以使用SET CONNECTION来使用不同的连接。

luser为登录名。

lpassword为密码。

如:使用my_id用户和passes密码连接到SYBASE服务器。

exec sql begin declare section;

CS_CHAR user[16];

CS_CHAR passwd[16];

CS_CHAR server[BUFSIZ];

exec sql end declare section;

strcpy(server,"SYBASE");

strcpy(passwd,"passes");

strcpy(user, "my_id");

exec sql connect :user identified by :passwd using

:server;

请看下面这些例子来理解连接名的使用方法。

...

exec sql begin declare section;

CS_CHAR user[16];

CS_CHAR passwd[16];

CS_CHAR name;

CS_INT value, test;

CS_CHAR server_1[BUFSIZ];

CS_CHAR server_2[BUFSIZ];

exec sql end declare section;

...

strcpy (server_1, "sybase1");

strcpy (server_2, "sybase2");

strcpy(user, "my_id");

strcpy(passwd, "mypass");

exec sql connect :user identified by :passwd

at connection_2 using :server_2;

exec sql connect :user identified by :passwd using

:server_1;

/* 下面这个语句使用了"server_1"的连接*/

exec sql select royalty into :value from authors

where author = :name;

if (value == test)

{

/* 下面这个语句使用了"connection_2"连接 */

exec sql at connection_2 update authors

set column = :value*2

where author = :name;

在嵌入SQL语句中,使用DISCONNECT语句断开数据库的连接。其语法为:

DISCONNECT [connection_name | ALL | CURRENT]

其中,connection_name为连接名。ALL表示断开所有的连接。CURRENT表示断开当前连接。断开连接会回滚当前事务、删除临时表、关闭游标和释放锁等。

2.3.3 数据的查询和修改

可以使用SELECT INTO语句查询数据,并将数据存放在主变量中。如:查询lastname为stringer的firstname信息。

EXEC SQL SELECT au_fname INTO :first_name

from authors where au_lname = "stringer";

使用DELETE语句删除数据。其语法类似于Transact-SQL中的DELETE语法。如:

EXEC SQL DELETE FROM authors WHERE au_lname = 'White'

使用UPDATE语句可以更新数据。其语法类似Transact-SQL中的UPDATE语法。如:

` EXEC SQL UPDATE authors SET au_fname = 'Fred' WHERE au_lname = 'White'

使用INSERT语句可以插入新数据。其语法就是Transact-SQL中的INSERT语法。如:

EXEC SQL INSERT INTO homesales (seller_name, sale_price)

real_estate('Jane Doe', 180000.00);

多行数据的查询和修改请参见下一节——游标。

2.3.4 游标的使用

用嵌入式SQL语句查询数据分成两类情况。一类是单行结果,一类是多行结果。对于单行结果,可以使用SELECT INTO语句;对于多行结果,你必须使用cursor(游标)来完成。游标(Cursor)是一个与SELECT语句相关联的符号名,它使用户可逐行访问由SQL Server返回的结果集。先请看下面这个例子,这个例子的作用是逐行打印staff表的id、name、dept、 job、years、salary和comm的值。

………..

EXEC SQL DECLARE C1 CURSOR FOR

SELECT id, name, dept, job, years, salary, comm FROM staff;

EXEC SQL OPEN c1;

while (SQLCODE == 0)

{

/* SQLCODE will be zero if data is successfully fetched */

EXEC SQL FETCH c1 INTO :id, :name, :dept, :job, :years, :salary, :comm;

if (SQLCODE == 0)

printf("%4d %12s %10d %10s %2d %8d %8d",

id, name, dept, job, years, salary, comm);

}

EXEC SQL CLOSE c1;

………

从上例看出,你首先应该定义游标结果集,即定义该游标的SELECT语句返回的行的集合。然后,使用FETCH语句逐行处理。

值得注意的是,嵌入SQL语句中的游标定义选项同Transact-SQL 中的游标定义选项有些不同。必须遵循嵌入SQL语句中的游标定义选项。

1)、声明游标:

如:EXEC SQL DECLARE C1 CURSOR FOR

SELECT id, name, dept, job, years, salary, comm FROM staff;

其中,C1是游标的名称。

2)、打开游标

如:EXEC SQL OPEN c1;

完整语法为:EXEC SQL OPEN 游标名 [USING 主变量名 | DEs criptOR 描述名]。关于动态OPEN游标的描述见第四节。

3)、取一行值

如:EXEC SQL FETCH c1 INTO :id, :name, :dept, :job, :years, :salary, :comm;

关于动态FETCH语句见第四小节。

4)、关闭游标

如:EXEC SQL CLOSE c1;

关闭游标的同时,会释放由游标添加的锁和放弃未处理的数据。在关闭游标前,该游标必须已经声明和打开。另外,程序终止时,系统会自动关闭所有打开的游标。

也可以使用UPDATE语句和DELETE语句来更新或删除由游标选择的当前行。使用DELETE语句删除当前游标所在的行数据的具体语法如下:

DELETE [FROM] {table_name | view_name} WHERE CURRENT OF cursor_name

其中,

ltable_name是表名,该表必须是DECLARE CURSOR中SELECT语句中的表。

lview_name是视图名,该视图必须是DECLARE CURSOR中SELECT语句中的视图。

lcursor_name是游标名。

请看下面这个例子,逐行显示firstname和lastname,询问用户是否删除该信息,如果回答“是”,那么删除当前行的数据。

EXEC SQL DECLARE c1 CURSOR FOR

SELECT au_fname, au_lname FROM authors ;

EXEC SQL OPEN c1;

while (SQLCODE == 0)

{

EXEC SQL FETCH c1 INTO :fname, :lname;

if (SQLCODE == 0)

{

printf("%12s %12s\n", fname, lname);

printf("Delete? ");

scanf("%c", &reply);

if (reply == 'y')

{

EXEC SQL DELETE FROM authors WHERE CURRENT OF c1;

printf("delete sqlcode= %d\n", SQLCODE(ca));

}

}

}

EXEC SQL CLOSE c1;

2.3.5 SQLCA

DBMS是通过SQLCA(SQL通信区)向应用程序报告运行错误信息。SQLCA是一个含有错误变量和状态指示符的数据结构。通过检查SQLCA,应用程序能够检查出嵌入式SQL语句是否成功,并根据成功与否决定是否继续往下执行。预编译器自动会在嵌入SQL语句中插入SQLCA数据结构。在程序中可以使用EXEC SQL INCLUDE SQLCA,目的是告诉SQL预编译程序在该程序中包含一个SQL通信区。也可以不写,系统会自动加上SQLCA结构。

下表是SQLCA结构中的变量和作用:

变量 数据类型 作用

sqlcaid char 包含“sqlca”的字符串

sqlcabc long SQLCA的长度

sqlcode long 包含最近一次语句执行的返回代码

sqlwarn[0] 到

sqlwarn[7] char 警告标志。如果是“W”,那么表示有警报信息。

sqlerrm.sqlerrmc[ ] char 错误信息。

sqlerrm.sqlerrml long 错误信息的长度。

sqlerrp char 检测错误或警告信息的过程。

sqlerrd[6] long 警告或错误的详细信息。[2]中存放影响行的个数。

下面仔细讲解几个重要的变量。

1)、SQLCODE

SQLCA结构中最重要的部分是SQLCODE变量。在执行每条嵌入式SQL语句时,DBMS在SQLCA中设置变量SQLCODE值,以指明语句的完成状态:

1、0该语句成功执行,无任何错误或报警。

2、<0 出现了严重错误。

3、>0 出现了报警信息。

4、100没有数据存在。在FETCH语句中,表示到达结果集的末尾。在UPDATE、

DELETE、INSERT语句中,表示没有满足条件的数据。

例:显示错误信息。

printf("\nError occurred: code %d.\n%s",

sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);

在SYBASE SQL SERVER中,也可以单独定义SQLCODE。如:

long SQLCODE;

exec sql open cursor pub_id;

while (SQLCODE == 0)

{

exec sql fetch pub_id into :pub_name;

…..

2)、SQLSTATE

SQLSTATE变量也是SQLCA结构中的成员。它同SQLCODE一样,都是返回错误信息。SQLSTATE是在SQLCODE之后产生的。这是因为,在制定SQL2标准之前,各个数据库厂商都采用SQLCODE变量来报告嵌入式SQL语句中的错误状态。但是,各个厂商没有采用标准的错误描述信息和错误值来报告相同的错误状态。所以,标准化组织增加了SQLSTATE变量,规定了通过SQLSTATE变量报告错误状态和各个错误代码。因此,目前使用SQLCODE的程序仍然有效,但也可用标准的SQLSTATE错误代码编写新程序。值得注意的是,Open
client emebeded SQL/C11.1.x并不完全支持SQLSTATE。

SQLSTATE是一个字符串参数。具体含义如下:

值 作用

00XXX 成功

01XXX 警告

02XXX 不存在数据

其他值 错误

2.3.6 WHENEVER

在每条嵌入式SQL语句之后立即编写一条检查SQLCODE/SQLSTATE值的程序,是一件很繁琐的事情。为了简化错误处理,可以使用WHENEVER语句。该语句是SQL预编译程序的指示语句,而不是可执行语句。它通知预编译程序在每条可执行嵌入式SQL语句之后自动生成错误处理程序,并指定了错误处理操作。

用户可以使用WHENEVER语句通知预编译程序去如何处理三种异常处理:

lWHENEVER SQLERROR action:表示一旦sql语句执行时遇到错误信息,则执行action,action中包含了处理错误的代码(SQLCODE<0)。

lWHENEVER SQLWARNING action:表示一旦sql语句执行时遇到警告信息,则执行aciton,即action中包含了处理警报的代码(SQLCODE=1)。

lWHENEVER NOT FOUND action:表示一旦sql语句执行时没有找到相应的元组,则执行action,即action包含了处理没有查到内容的代码(SQLCODE=100)。

针对上述三种异常处理,用户可以指定预编译程序采取以下三种行为(action):

lWHENEVER …GOTO:通知预编译程序产生一条转移语句。

lWHENEVER…CONTINUE:通知预编译程序让程序的控制流转入到下一个主语言语句。

lWHENEVER…CALL:通知预编译程序调用函数。

其完整语法如下:

WHENEVER {SQLWARNING | SQLERROR | NOT FOUND} {CONTINUE | GOTO stmt_label | CALL function()}

例:WHENEVER的作用

EXEC SQL WHENEVER sqlerror GOTO errormessage1;

EXEC SQL DELETE FROM homesales

WHERE equity < 10000;

EXEC SQL DELETE FROM customerlist

WHERE salary < 40000;

EXEC SQL WHENEVER sqlerror CONTINUE;

EXEC SQL UPDATE homesales

SET equity = equity - loanvalue;

EXEC SQL WHENEVER sqlerror GOTO errormessage2;

EXEC SQL INSERT INTO homesales (seller_name, sale_price)

real_estate('Jane Doe', 180000.00);

.

.

.

errormessage1:

printf("SQL DELETE error: %ld\n, sqlcode);

exit();

errormessage2:

printf("SQL INSERT error: %ld\n, sqlcode);

exit();

WHENEVER语句是预编译程序的指示语句。在上面这个例子中,由于第一个WHENEVER语句的作用,前面两个DELETE语句中任一语句内的一个错误会在errormessage1中形成一个转移指令。由于一个WHENEVER语句替代前面WHENEVER语句,所以,嵌入式UPDATE语句中的一个错误会直接转入下一个程序语句中。嵌入式INSERT语句中的一个错误会在errormessage2中产生一条转移指定。

从上面例子看出,WHENEVER/CONTINUE语句的主要作用是取消先前的WHENEVER语句的作用。WHENEVER语句使得对嵌入式SQL错误的处理更加简便。应该在应用程序中普遍使用,而不是直接检查SQLCODE的值。

#数据库
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: