300 likes | 460 Views
Embedded SQL. -Software School of Hunan University- 2006.09. 利用高级语言的过程性结构来弥补 SQL 语言实现复杂应用方面的不足。 嵌入 SQL 的高级语言称为主语言或宿主语言。 在混合编程中, SQL 语句负责操作数据库,高级语言语句负责控制程序流程。 预编译方法:由 DBMS 的预处理程序对源程序扫描,识别出 SQL 语句,把它们转换成主语言调用语句,以使主语言编译器能识别它,最后由主语言编译器将整个源程序编译成目标码。. 嵌入式 SQL 的一般形式.
E N D
Embedded SQL -Software School of Hunan University- 2006.09
利用高级语言的过程性结构来弥补SQL语言实现复杂应用方面的不足。利用高级语言的过程性结构来弥补SQL语言实现复杂应用方面的不足。 • 嵌入SQL的高级语言称为主语言或宿主语言。 • 在混合编程中,SQL语句负责操作数据库,高级语言语句负责控制程序流程。 • 预编译方法:由DBMS的预处理程序对源程序扫描,识别出SQL语句,把它们转换成主语言调用语句,以使主语言编译器能识别它,最后由主语言编译器将整个源程序编译成目标码。
嵌入式SQL的一般形式 • 所有的嵌入式SQL语句都不得必须加前缀EXEC SQL • 例如: • 在C语言中: EXEC SQL <SQL语句> EXEC SQL DROP TABLE Student; • 在COBOL中:EXEC SQL <SQL语句> END-EXEC
嵌入式SQL与主语言的通信 • 向主语言传递SQL语句执行状态信息,使语言能够据此信息控制程序流程,用SQL通信区(SQLCA)实现。 • 主语言向SQL语句提供参数,主要用主变量(Host Variable)实现; • 将SQL语句查询数据库的结果交主语言进一步处理,主要用主变量和游标(Cursor)实现。
SQL通信区 • SQL是一个数据结构,在应用程序中用EXEC SQL INCLUDE SQLCA加以定义。 • SQLCA中有一个存放每次执行SQL语句后返回代码的变量SQLCODE。 • 每次执行完SQL语句后都应该测试一下SQLCODE的值,以了解该SQL语句执行情况并做相应处理,如果SQLCODE等于预定的常量SUCCESS,则表示SQL语句成功,否则在SQLCODE中存放错误代码。
SQL通信区 • 例如:在执行删除语句后,不同的执行情况SQLCA中有下列不同的信息: • 成功删除并有删除的行数(SQLCODE=SUCCESS) • 无条件删除警告信息 • 违反数据保护规则,拒绝操作 • 没有满足条件的行,一行也没有删除 • 由于各种原因,执行出错
主变量(Host Variable) • 主语言程序变量:主变量 • 输入主变量:由应用程序对其赋值,SQL语句引用 • 输出主变量:由SQL语句对其赋值或设置状态信息,返回给应用程序 • 一个主变量既可是输入主变量也可是输出主变量。
指示变量(Indicator Variable) • 指示变量:主变量附带的可选变量,是一个整型变量,用来”指示”所指主变量的值或条件。可以指示输入主变量是否为空,可以检测输出主变量是否为空值,值是否被截断。 • 所有主变量和指示变量必须在SQL语句 BEGIN DECLARE SECTION与END DECLARE SECTION之间进行说明。 • SQL语句的主变量名和指示变量前要加冒号作为标志,且指示变量必须紧随主变量之后。 • 在SQL语句之外,主变量和指示变量直接引用,不须加冒号。
游标(Cursor) • SQL语言面向集合,一条SQL语句原则上可以产生或处理多条记录,而主语言是面向记录的,一组主变量一次只能存放一条记录。所以仅用主变量不能完全满足SQL语句向应用程序输出数据的要求。 • 游标是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果,每个游标区都有一个名字。 • 用户可以通过游标逐一获得数据,并赋给主变量,交由主语言处理。
程序实例(SQL in C) EXEC SQL INCLUDE SQLCA; ……………………定义SQL通信区 EXEC SQL BEGIN DECLARE SECTION; ……….主变量说明开始 CHAR Sno(5) CHAR Cno(3) INT Grade; EXEC SQL END DECLARE SECTION; ……….主变量说明结束 Main() { EXEC SQL DECLARE C1 CURSOR FOR ……….定义游标 SELECT Sno, Cno, Grade FROM SC; EXEC SQL OPEN C1; ……….………. 打开游标 for( ; ; ) {
程序实例(SQL in C) EXEC SQL FETCH C1 INTO :Sno, :Grade; ……….推进游标指针并将当前数据放入主变量 if(sqlca.sqlcode<>SUCCESS ……….利用SQLCA中的状态信息决定何时退出循环 break; printf(“Sno:%s,Cno:%s,Grade:%d”,:Sno,:Cno, :Grade); ……….打印查询结果 } EXEC SQL CLOSE C1; ……….………. 关闭游标 }
不用游标的SQL语句 • 说明性语句 • 数据定义语句 • 数据(权限)控制语句 • 查询结果为单记录的SELECT语句 • 非CURRENT形式的UPDATE语句 • 非CURRENT形式的DELETE语句 • INSERT语句 不需要使用主变量 通常需要使用主变量
查询结果为单记录的SELECT语句 EXEC SQL SELECT [ALL|DISDINCT] < 目标列表达式1>…… INTO <主变量>[<指示变量>]…… FROM <表名或视图名>,…… [ WHERE<Condition>] [GROUP BY<列名>[HAVING<条件表达式>]] [ORDER BY<列名>[ASC|DESC]] • INTO子句、WHERE子句、HAVING短语的条件表达式中均可用主变量 • 当查询得出某个数据项为空时,系统会自动将相应主变量后的指示变量置为负值,而不再向该主变量赋值,即主变量仍为执行SQL语句之前的值,所以当指示变量为负值时,不管主变量为何值,均应认为主变量为NULL。指示变量只能用于INTO子句中。
查询结果为单记录的SELECT语句 • 如果数据库没有满足条件的记录,则DBMS将SQLCODE的值置为100。 • 如果查询结果实际上并不是单条记录,而是多条记录,则程序出错,DBMS会在SQLCA中返回错误信息。 • 例:根据学号查学生信息 EXEC SQL SELECT Sno, Sname, Ssex,Sage,Sdept INTO :Hsno, :Hname, :Hsex, : Hage, :Hdept FROM Student WHERE Sno=:givensno;
查询结果为单记录的SELECT语句 • 例:查询某个学生的某门课程成绩 EXEC SQL SELECT Sno, Cno, Grade INTO :Hsno, :Hcno, :Hgrade:Gradeid FROM SC WHERE Sno=:givensno AND Cno=:givencno; • 由于学生选修一门课后可能没有参加考试,也就是说成绩为空,所以在INTO子句中加了指示变量Gradeid,用于指示主变量是否为空值。
非CURRENT形式的UPDATE语句 • 在UPDATE语句中,SET子句和WHERE子句中均可以使用主变量,其中SET子句中还可以使用指示变量。 • 例:将全体学生的1号课程成绩增加若干分,假设增加的分数已赋值给主变量Raise. EXEC SQL UPDATE SC SET Grade=Grade+:Raise WHERE Cno=‘1’; EXEC SQL UPDATE SC SET Grade=:newgrade WHERE Sno=:givensno;
非CURRENT形式的UPDATE语句 • 例:将软件学院全体学生年龄置为空。 Sageid=-1; EXEC SQL UPDATE Student SET Sage=:Raise:Sageid WHERE Sdept=‘SS’; 由于将指示变量Sageid赋一个负值,因此无论Raise为何值,DBMS会将SS的所有学生年龄置为空值。 等价于: EXEC SQL UPDATE Student SET Sage=NULL WHERE Sdept=‘SS’;
非CURRENT形式的DELETE语句 • 可使用主变量指定删除条件。 • 例:某个学生退学了,删除其所有选课记录。 EXEC SQL DELETE FROM SC WHERE Sno= (SELECT Sno FROM Student WHERE Sname=:stdname); (更直接,更高效) 或: EXEC SQL DELETE FROM SC WHERE :stdname = (SELECT Sname FROM Student WHERE Student.Sno=SC.Sno);
INSERT语句 • INSERT语句的VALUES子句可能使用主变量和指示变量 • 例:某个学生新选修了某门课程。假设学号赋值给了主变量stdno,课程号赋给了couno 。 gradeid=-1; EXEC SQL INSERT INTO SC(Sn0, Cno, Grade) VALUES(:stdno:couno, :gr:gradeid); 由于该学生刚选修课程,尚无考试成绩,所以把指示变量gradeid置为负值。
使用游标的SQL语句 • 查询结果为多条记录的SELECT语句 • CURRENT形式的UPDATE语句 • CURRENT形式的DELETE语句
查询结果为多条记录的SELECT语句 • 使用游标的步骤: (1)说明游标 EXEC SQL DELCARE<游标名> CURSOR FOR <SELECT语句>; (2)打开游标:执行相应的SELECT语句,把结果取到缓冲区中,这时游标处于活动状态,指针指向结果集中第一条记录。 EXEC SQL OPEN<游标名>; (3)推进游标指针并取当前记录。 EXEC SQL FETCH<游标名> INTO <主变量>[<指示变量>]……; 主变量名必须与SELECT语句中的目标列表达式一一对应。 FETCH语句通常用在一个循环结构中,通过循环逐条取出记录处理。 (4)关闭游标,释放结果集占用的缓冲区和其他资源。 EXEC SQL CLOSE<游标名>
查询结果为多条记录的SELECT语句 • 例:查询某个系全体学生的信息。要查询的系名由用户指定,存放在主变量deptname中。 …… EXEC SQL BEGIN DELCARE SECTION; …… /*说明主变量deptname,Hsno,Hsname,Hssex,Hsage等*/ EXEC SQL END DECLARE SECTION; …… gets(deptname); /*为主变量deptname赋值*/ …… EXEC SQL DECLARE SX CURSOR FOR /*说明游标SX,将SX SELECT Sno, Sname, Ssex, Sage 与查询结果集相联系*/ FROM Student WHERE Sdept=:deptname;
查询结果为多条记录的SELECT语句 EXEC SQL OPEN SX WHILE(1) /*用循环结构逐条处理结果集中的记录*/ { EXEC SQL FETCH SX INTO :Hsno, Hsname, :Hssex, :Hsage; if(sqlca.sqlcode<>SUCCESS) break; /*若查询结果处理完或出现错误,则退出循环*/ …… }; EXEC SQL CLOSE SX;
查询结果为多条记录的SELECT语句 • 例:查询某个系全体学生的选课信息。 …… EXEC SQL BEGIN DELCARE SECTION; …… /*说明主变量deptname,Hsno,Hsname,Hssex,Hsage等*/ EXEC SQL END DECLARE SECTION; …… EXEC SQL DECLARE SX CURSOR FOR /*说明游标SX,将SX SELECT Sno, Sname, Ssex, Sage 与查询结果集相联系*/ FROM Student WHERE Sdept=:deptname;
查询结果为多条记录的SELECT语句 WHILE(gets(deptname!=NULL)) /*接收变量deptname的值*/ { /*处理deptname指定系的学生信息,每次循环中deptname可不同*/ EXEC SQL OPEN SX WHILE(1){ EXEC SQL FETCH SX INTO :Hsname, :Hssex, :Hsage; if(sqlca.sqlcode<>SUCCESS) break; /*若查询结果处理完或出现错误,则退出循环*/ …… }; EXEC SQL CLOSE SX; }; ……
CURRENT形式的UPDATE语句和DELETE语句 • UPDATE和DELETE语句都是集合操作,如果只想修改或删除其中的某个记录,则需要用带游标的SELECT语句查出所有满足条件记录,从中进一步找出要修改或删除的记录,然后用CURRENT形式的UPDATE和DELETE语句处理。 • 具体步骤: (1)用DELCARE语句说明游标。如果是为CURRENT形式的UPDATE语句作准备,则SELECT语句中要用 FOR UPDATE OF<列名>用来指明查询出的数据在指定列是可修改的。如果是为CURRENT形式的DELETE语句作准备,则不必使用上述子句。
CURRENT形式的UPDATE语句和DELETE语句 (2)用OPEN语句打开游标,把所有满足条件的记录从指定表中取到缓冲区中; (3)用FETCH语句推进游标,并把当前记录从缓冲区中取出来送至主变量; (4)检查该记录是否为该修改或删除的记录。如果是,则修改或删除之。这时UPDATE和DELETE语句中要用子句 WHERE CURRENT OF<游标名>,表示修改或删除的是最近一次取出的记录,即游标指针指向的记录。 第(3)和(4)步通常用在一个循环结构中,通过循环执行FETCH语句,逐条取出结果集中的记录进行判断和处理。 (5)处理完毕后,用CLOSE语句关闭游标,释放缓冲区和其他资源。
CURRENT形式的UPDATE语句 • 查询某个系全体学生信息,然后根据用户要求修改其中某些记录的年龄字段。 …… EXEC SQL BEGIN DELCARE SECTION; …… /*说明主变量deptname,Hsno,Hsname,Hssex,Hsage, NEWage等*/ EXEC SQL END DECLARE SECTION; …… gets(deptname); EXEC SQL DECLARE SX CURSOR FOR SELECT Sno, Sname, Ssex, Sage FROM Student WHERE Sdept=:deptname; FOR UPDATE OF Sage;
CURRENT形式的UPDATE语句 EXEC SQL OPEN SX WHILE(1){ /*用循环结构逐条处理结果集中的记录*/ EXEC SQL FETCH SX INTO :Hsno, Hsname, :Hssex, :Hsage; if(sqlca.sqlcode<>SUCCESS) break; /*若查询结果处理完或出现错误,则退出循环*/ printf(“%s,%s,%s,%d”, sno, sname, ssex, sage); printf(“UPDATE AGE?”); /*问用户是否需要修改*/ scanf( “%c”, &yn); if(yn=‘y’ or yn=‘Y’) /*需要修改*/ { printf(“INPUT NEW AGE:”); scanf(%d”, &NEWage); EXEC SQL UPDATE Student SET Sage = :NEWage WHERE CURRENT OF SX; }; …… }; EXEC SQL CLOSE SX;
CURRENT形式的UPDATE语句和DELETE语句 • 当游标定义中的SELECT语句带有UNION或ORDER BY子句时,或者该SELECT语句相当于定义一个不可变更的视图时,不能使用CURRENT形式的UPDATE和DELETE语句。