【SQL】宿主语言接口

一般情况下,SQL语句是嵌套在宿主语言(如C语言)中的。有两种嵌套方式:

1.调用层接口(CLI):提供一些库,库中的函数和方法实现SQL的调用

2.直接嵌套SQL:在代码中嵌套SQL语句,提交给预处理器,将SQL语句转换成对宿主语言有意义的内容,如调用库中的函数和方法代替SQL语句

 

阻抗不匹配问题:连接SQL语句与常规编程语言的基本问题。SQL核心使用关系数据模型,而C语言等常规语言则使用整型、实数型、算数型、字符型、指针等类型。

 

 

一、SQL与宿主语言连接

EXEC SQL关键字提示预处理将由SQL代码进入。

共享变量:实现数据库和宿主语言之间的信息交换。

SQL中共享变量要加冒号,宿主语言中不需要。

SQLSTATE:特殊变量,由5个字符的数组组成(在C中分配空间要分配6个,后面有'\0'),存放表示调用过程出现的问题的代码。'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:\n");
    scanf("%s", &studioName);
    printf("please input studio address:\n");
    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\n",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;
}

 

posted @ 2015-10-21 16:03  匡子语  阅读(1697)  评论(0编辑  收藏  举报