一般情况下,SQL语句是嵌套在宿主语言(如C语言)中的。有两种嵌套方式:
1.调用层接口(CLI):提供一些库,库中的函数和方法实现SQL的调用
2.直接嵌套SQL:在代码中嵌套SQL语句,提交给预处理器,将SQL语句转换成对宿主语言有意义的内容,如调用库中的函数和方法代替SQL语句
阻抗不匹配问题:连接SQL语句与常规编程语言的基本问题。SQL核心使用关系数据模型,而C语言等常规语言则使用整型、实数型、算数型、字符型、指针等类型。
一、SQL与宿主语言连接
EXEC SQL关键字提示预处理将由SQL代码进入。
共享变量:实现数据库和宿主语言之间的信息交换。
SQL中共享变量要加冒号,宿主语言中不需要。
SQLSTATE:特殊变量,由5个字符的数组组成(在C中分配空间要分配6个,后面有' '),存放表示调用过程出现的问题的代码。'00000'表示没有错误。
1.1 DECLARE节
声明共享变量要在声明节中。
EXEC SQL BEGIN DECLARE SECTION;
...
EXEC SQL END DECLARE SECTION;
EXEC SQL BEGIN DECLARE SECTION; char studioName[50], studioAddr[256]; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION;
1.2 使用共享变量
任何不含返回结果的SQL语句,都可以用EXEC SQL为前缀嵌入宿主语言。
void getStudio(){ EXEC SQL BEGIN DECLARE SECTION; char studioName[50], studioAddr[256]; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; printf("please input studio name: "); scanf("%s", &studioName); printf("please input studio address: "); scanf("%s", &studioAddr); EXEC SQL INSERT INTO Studio(name, address) VALUES (:studioName, :studioAddr);
1.3 连接SQL返回结果到宿主语言
因为阻抗不匹配,使得返回结果无法直接返回到宿主语言中,必须使用下面两种机制中的一个。
单元组选择语句:只有一个结果的查询语句,将该元组存储到共享变量中。
游标:为查询声明一个游标,游标范围覆盖结果关系中的所有元组,每个元组一次被提取到共享变量,由宿主语言处理。
1.4单元组选择语句
类似于普通的select-from-where语句,只是SELECT子句后面紧跟着关键字INTO和一连串的共享变量。如果结果少于或多于一个元组,则共享变量中不会有值,且SQLSTATE中存入一个错误码。
void printNetWorth(){ EXEC SQL BEGIN DECLARE SECTION; char studioName[50]; int presNetWorth; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; /*输入studioName的代码*/ EXEC SQL SELECT netWorth INTO :presNetWorth FROM Studio, MovieExec WHERE presC# = cert# AND Studio.name = :studioName; /*检查SQLSTATE中的代码是否为'00000'*/ }
1.5 游标
游标声明:
EXEC SQL DECLARE 游标名称 CURSOR FOR 查询
初始化游标的位置:使之指向第一个元组
EXEC SQL OPEN 游标名称
得到下一个元组:
EXEC SQL FETCH FROM 游标名称 INTO 变量列表
变量列表中存放获取的结果,如果已经遍历结束则SQLSTATE中返回'02000'
关闭游标:
EXEC SQL CLOSE 游标名称
void worthRanges(){ int i, digits, counts[15]; EXEC SQL BEGIN DECLARE SECTION; int worth; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE execCursor CURSOR FOR SELECT netWorth FROM MovieExec; EXEC SQL OPEN execCursor; for(i = 1; i < 15; i++) counts[i] = 0; while(1){ EXEC SQL FETCH FROM execCursor INTO :worth; if(NO_MORE_TUPLES) break; digits = 1; while((worth /= 10) > 0) digits++; if(digits <= 14) counts[digits]++; } EXEC SQL CLOSE execCursor; for(i = 1; i < 15; i++) printf("digits = %d: number of execs = %d ",i, counts[i]); }
1.6 游标更新
可以通过游标删除或修改当前的元组。
WHERE后只能是 WHERE CURRENT OF
下面的例子将资产少于1000的删除,多余1000的翻倍。
#define NO_MORE_TUPLES !(strcmp(SQLSTATE,"02000")) void changeWorth(){ EXEC SQL BEGIN DECLARE SECTION; int certNo, worth; char execName[31], execAddr[256], SQLSTATE[6]; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE execCursor CURSOR FOR MovieExec; EXEC SQL OPEN execCursor; while(1){ EXEC SQL FETCH FROM execCursor INTO :execName, :execAddr, :certNo, :worth; if(NO_MORE_TUPLES) break; if(worth < 1000) EXEC SQL DELETE FROM MovieExec WHERE CURRENT OF execCursor; else EXEC SQL UPDATE MovieExec SET netWorth = 2 * netWorth WHERE CURRENT OF execCursor; } EXEC SQL CLOSE execCursor; }
1.7 避免并发修改
如果在游标获取到元组到游标关闭前,不希望元组发生变化:
EXEC SQL DECLARE 游标名 INSENSITIVE CURSOR FOR 查询;
不希望通过游标修改元组
EXEC SQL DECLARE 游标名 CURSOR FOR 查询 FOR READ ONLY;
1.8动态SQL
在运行时,通过输入SQL语句实现查询需要动态SQL语句支持。
EXEC SQL PREPARE V FROM 表达式;
EXEC SQL EXECUTE V; //这句可以多次执行 表明表达式V执行了多次
上面两句合并后可以写成:
EXEC SQL EXECUTE IMMEDIATE 表达式;
void readQuery(){ EXEC SQL BEGIN DECLARE SECTION; char * query; EXEC SQL END DECLARE SECTION; /*输入SQL语句,放入query中*/ EXEC SQL PREPARE SQLquery FROM :query; EXEC SQL EXECUTE SQLquery; }