您的位置:首页 > 编程语言 > C语言/C++

Proc *C/C++入门之动态SQL

2017-01-11 23:02 369 查看

基本概念

在有些情况下, 在编码时 SQL 语句还不能完整地写出来, 而是在程序执行时才能

构造出来,这种在程序执行临时生成的 SQL 语句叫动态 SQL 语句. 利用动态 SQL 来

编写 Pro*C 程序的方法叫动态 SQL 技术!

目的:加强应用程序的功能和灵活

静态SQL —- 在编写应用程序时,使用EXEC SQL关键字直接嵌入的SQL语句;在proc编译应用程序生成c语言的时,都已经确定

动态SQL —- 在运行应用程序时,由用户动态输入的SQL语句。

在下列情况之一不知道时, 使用动态 SQL 技术:

SQL 语句的文本.

宿主变量的个数。

宿主变量的数据类型

引用的数据库对象, 如列, 索引, 序列, 表, 用户名和视图.

Oracle 中动态 SQL 可用以下两种方法实现:一个是 Oracle 自己的方法, 一个是 ANSI 的方法. 一般建议使用 Oracle 的方法,

但对于较复杂的应用, 可以使用 ANSI 的方法, 因为这样可以使自己的程序简化。

动态SQL1

语法:

EXEC SQL EXECUTE IMMEDIATE :host_string


host_string 字符串

限制

不能执行select语言,仅适用于非select语句

在sqlplus上运行的命令,都可以拿过来来执行

语句中不包含输入宿主变量–一个宿主指针变量指向一块内存空间(存有用户输入的SQL语句)

常用于仅执行一次的动态语句

对重复执行多次的动态SQL语句,降低执行效率

nt main01()
{
int     ret = 0;
int     i = 0;
char    choosechar;

memset(mySql, 0, sizeof(mySql));
pSql = NULL;

EXEC SQL WHENEVER SQLERROR DO sqlerr02();
connet();
pSql = mySql;
//循环处理sql语言
for(;;)
{
printf("\nplease enter sql(not select ): ");
gets(mySql);
//scanf("%s", mySql); --空格截断
printf("mysql:%s\n", mySql);
printf("任意键继续....\n");
getchar();
EXEC SQL EXECUTE IMMEDIATE :pSql;

EXEC SQL COMMIT;
printf("继续执行吗?\n");
scanf("%c", &choosechar);
fflush(stdin);

if (choosechar=='n' || choosechar=='N')
{
break;
}
}

EXEC SQL COMMIT WORK RELEASE;
printf("return ok...\n");

return ret ;
}


动态SQL2

使用PREPARE命令准备SQL语句

EXEC SQL PREPARE statement_name FROM :host_string;


statement_name: 标识符,

host_string:含SQL语句的字符串

使用EXECUTE命令执行SQL语句

EXEC SQL EXECUTE statement_name [USING :host_variable]


如果SQL语句要通过宿主变量赋值,输入SQL语句时要用占位符

仅适用于非select语句

可包含虚拟输入宿主变量和指示器变量,其个数和类型在预编译时已知

int main02()
{
int     ret = 0;
int     i = 0;
char    choosechar;

memset(mySql, 0, sizeof(mySql));
pSql = NULL;

EXEC SQL WHENEVER SQLERROR DO sqlerr02();
connet();

pSql = mySql;
//循环处理sql语言
for(;;)
{
printf("\n请输入要更新部门编号 ");
scanf("%d", &deptno);

printf("\n请输入要新loc值 ");
scanf("%s", loc);

//准备动态sql
EXEC SQL PREPARE my_pre_sql FROM 'update dept set loc = :a where deptno = :b';
//执行动态sql
EXEC SQL EXECUTE my_pre_sql USING :loc, :deptno;

EXEC SQL COMMIT;

printf("\n 按任意键继续? ");
getchar();
printf("\n退出键入n, 其他继续? ");
scanf("%c", &choosechar);
fflush(stdin);

if (choosechar=='n' || choosechar=='N')
{
break;
}
}

EXEC SQL COMMIT WORK RELEASE;
printf("return ok...\n");

return ret ;
}


动态SQL3

使用PREPARE命令准备SQL语句

EXEC SQL PREPARE statement_name FROM :host_string;


statement_name: 标识符,

host_string:含SQL语句的字符串

使用DECLARE命令定义游标,可以结合游标一块使用

EXEC SQL DECLARE cursor_name CURSOR FOR statement_name;
EXEC SQL OPEN cursor_name [using host_variable_list]
EXEC SQL FETCH cursor_name INTO host_variable_list
EXEC SQL CLOSE cursor_name


查询部门号大于10的所有部门信息

动态sql3 处理选择列表项(select查询出来的结果列数固定) 和 输入宿主变量个数一定

本质:

输入宿主变量个数固定 查询条件固定

输出宿主变量个数固定 返回结果固定

//可以结合游标一块使用

int main()
{
int     ret = 0;
int     i = 0;
char    choosechar;

memset(mySql, 0, sizeof(mySql));
pSql = NULL;

EXEC SQL WHENEVER SQLERROR DO sqlerr02();
connet();
//EXEC SQL WHENEVER NOT FOUND DO nodata();

//循环处理sql语言
for(;;)
{
printf("\n请输入部门编号 ");
scanf("%d", &deptno);

//准备动态sql
EXEC SQL PREPARE my_pre_sql3 FROM 'select deptno, dname, loc from dept where deptno > :a';

//定义游标
EXEC SQL DECLARE c1 CURSOR FOR my_pre_sql3;

//打开游标
EXEC SQL OPEN c1 USING :deptno;

//提取数据
EXEC SQL WHENEVER NOT FOUND DO break;

for (;;)
{
EXEC SQL FETCH c1 INTO :deptno, :dname,:loc:loc_ind;
printf("%d\t %s\t %s \n", deptno, dname, loc);

}
EXEC SQL CLOSE c1;

EXEC SQL COMMIT;

printf("\n 按任意键继续? ");
getchar();
printf("\n键入 n 退出, 其他继续? ");
scanf("%c", &choosechar);
fflush(stdin);

if (choosechar=='n' || choosechar=='N')
{
break;
}
}

EXEC SQL COMMIT WORK RELEASE;
printf("return ok...\n");

return ret ;
}


动态SQL4

既适用于SELECT语句,也适用于非SELECT语句、

工程中主要使用该模式

与前面的方法相比有两个突出的不同点:

不但包含选择表项或虚拟输入宿主变量,而且它们的个数或数据类型在编译时还不知道

在其它方法中,ORACLE和C之间的数据类型转换是自动实现的。而在方法4中,由于动态语句中的宿主变量个数和类型在编译时还不知道,因此不能实现自动转换,必须由程序来控制数据类型之间的转换

主要信息:

选择表项和实输入宿主变量的个数

每一个选择表项和实输入宿主变量的成年高度

每一个选择表项和实输入宿主变量的数据类型

每一个输出宿主变量和实输入宿主变量的内存单元地址

ANSI模式

/* 包含C头文件 */
#include <stdio.h>
#include <string.h>

/* 包含SQLCA头文件 */
#include "sqlca.h"

/* 定义绑定变量值和选择列表项值的最大长度 */
#define MAX_VAR_LEN     30

/* 定义选择列表项名的最大长度 */
#define MAX_NAME_LEN    31

/* 定义宿主变量 */
exec sql begin declare section;
char    *usrname = "scott";
char    *passwd = "tiger";
char    *serverid = "orcl";
char sql_stat[100];
char current_date[20];
exec sql end declare section;

void sql_error();
void connet();
void process_input();
void process_output();

int main()
{
/* 安装错误处理句柄 */
exec sql whenever sqlerror do sql_error();

/* 连接到数据库 */
connet();

/* 分配输入描述区和输出描述区 */
exec sql allocate descriptor 'input_descriptor';
exec sql allocate descriptor 'output_descriptor';

for( ; ; )
{
printf("\n请输入动态SQL语句(EXIT:退出):\n");
gets(sql_stat);

/* EXIT(exit)->退出 */
if(0 == strncmp(sql_stat , "EXIT" , 4) || 0 == strncmp(sql_stat , "exit" , 4))
break;

/* 准备动态SQL语句 */
exec sql prepare s from :sql_stat;

/* 定义游标 */
exec sql declare c cursor for s;

/* 处理绑定变量 */
process_input();

/* 打开游标
* select语句:处理查询结果
* 其他SQL语句:执行
*/
exec sql open c using descriptor 'input_descriptor';
if(0 == strncmp(sql_stat , "SELECT" , 6) , 0 == strncmp(sql_stat , "select" , 6))
{
process_output();
}
/* 关闭游标 */
exec sql close c;
}

/* 释放输入描述区和输出描述区 */
exec sql deallocate descriptor 'input_descriptor';
exec sql deallocate descriptor 'output_descriptor';

/* 提交事务,断开连接 */
exec sql commit work release;
puts("谢谢使用ANSI动态SQL!\n");

return 0;
}

void sql_error()
{
/* 显示SQL错误 */
printf("%.*s\n" , sqlca.sqlerrm.sqlerrml , sqlca.sqlerrm.sqlerrmc);
exit(0);
}

void process_input()
{
int i;

/* 定义宿主变量 */
exec sql begin declare section;
int input_count;
int input_type ;
int input_len;
char input_buffer[MAX_VAR_LEN];
char name[MAX_NAME_LEN];
int occurs;
exec sql end declare section;

/* 绑定变量->输入描述区 */
exec sql describe input s using descriptor 'input_descriptor';

/* 取得绑定变量个数 */
exec sql get descriptor 'input_descriptor' :input_count = count;

/* 循环处理绑定变量名 */
for(i = 0 ; i != input_count ; ++i)
{
occurs = i + 1;

/* 取得绑定变量名 */
exec sql get descriptor 'input_descriptor' value :occurs :name = name;
printf("请输入%s的值:" , name);
gets(input_buffer);

/* 以NULL结尾 */
input_len = strlen(input_buffer);
input_buffer[input_len] = '\0';

/* 设置绑定变量类型、长度和值 */
input_type = 1;
exec sql set descriptor 'input_descriptor' value :occurs
type = :input_type , length = :input_len , data = :input_buffer;
}
}

void process_output()
{
int i;

// 定义宿主变量
EXEC SQL BEGIN DECLARE SECTION ;

int output_count;
int output_type;
int output_len;
char output_buffer[MAX_VAR_LEN];
short  output_indicator;
char name[MAX_NAME_LEN];
int occurs;
EXEC SQL END DECLARE SECTION ;

// 选择列表项, 输出描述区
exec sql describe output s using descriptor 'output_descriptor';

//取得选择列表项个数
exec sql get descriptor 'output_descriptor' :output_count = count;

//循环处理选择列表项

output_type = 12;  //note //设置类型为变长字符串
//output_type = 1;  //note
for(i = 0 ; i != output_count ; ++i)
{
occurs = i + 1;

output_len = MAX_VAR_LEN;

//printf("22222222222222:%d \n", i);
// 设置选择列表项的类型和长度
exec sql set descriptor 'output_descriptor' value :occurs
type = :output_type , length = :output_len;

//取得选择列表项的名称并输出
exec sql get descriptor 'output_descriptor' value :occurs :name = name;

//显示选择列表项名称
printf("\t%s" , name);
}
printf("\n");

// 提取数据完毕->退出循环
exec sql whenever not found do break;

// 循环处理选择列表项数据
for( ; ; )
{
// 行数据->输出描述区
exec sql fetch c into descriptor 'output_descriptor';

// 循环处理每列数据
for(i = 0 ; i < output_count ; ++i)
{
occurs = i +1;

// 取得列数据和指示变量值
exec sql get descriptor 'output_descriptor' VALUE :occurs
:output_buffer = DATA , :output_indicator = INDICATOR;

//输出列数据
if(-1 == output_indicator)
printf("\t%s", "   ");
else
printf("\t%s" , output_buffer);
}

printf("\n");
}

}

void connet()
{
int ret = 0;
//连接数据库
EXEC SQL CONNECT:usrname IDENTIFIED BY:passwd USING:serverid ;
if (sqlca.sqlcode != 0)
{
ret = sqlca.sqlcode;
printf("sqlca.sqlcode: err:%d \n", sqlca.sqlcode);
return ;
}
else
{
printf("connect ok...\n");
}
}


ORACLE模式

关于SQLDA

为了保存执行动态SQL语句所需要的信息,系统提供一个称之为SQL描述区的程序数据结构。把ORACLE所需要的全部有关选择表项或虚拟输入宿主变量的信息,除了值和名字外,都存储在SQLDA中

SQLDA中所包含的信息主要通过以下三种方法写入:

sqlald()函数:在分配描述区和缓冲区的同时,还把SLI或P的名字在缓冲区中的地址和长度写入SQLDA中

应用程序:通过程序把SLI或BV值的缓冲区地址、长度既数据类型写入SQLDA

DESCRIBE语句:检查每一个选择表项,确定它的名字、数据类型、约束、长度、定标和精度,然后把这方面的信息存储在选择SQLDA和输出缓冲区中,以供用户使用



说明的方式有如下三种:

直接把商标中所示的代码编写到程序中

用INCLUDE语句:
EXEC SQL INCLUDE sqlda;


使用指针:
EXEC SQL INCLUDE sqlda; sqlda *bind_dp;sqlda *select_dp;bind_dp=sqlald(…);select_dp=sqlald(…);


sqlald(max_vars,max_name,max_ind_name)
为SQLDA分配控件,并分配相应的缓冲区,并把缓冲区的地址等填入SQLDA中

/* 包含C头文件 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <setjmp.h>
#include <sqlcpr.h>

/* 包含SQLDA和SQLCA结构 */
#include <sqlda.h>
#include <sqlca.h>

/* 定义绑定变量和选择列表项的最大个数 */
#define MAX_ITEMS       40

/* 定义绑定变量和选择列表项名称的最大长度 */
#define MAX_VNAME_LEN   30

/* 定义指示变量名称的最大长度 */
#define MAX_INAME_LEN   30

void connect();
void sql_error();
void alloc_descriptors(int , int , int);
void dealloc_descriptors();
void set_bind_variables();
void process_select_list();

/* 定义绑定描述区和选择描述区 */
SQLDA* bind_dp;
SQLDA* select_dp;

/* 定义输入宿主变量:存放动态SQL语句 */
char sql_stat[100];
char current_date[20];

int main()
{
/* 安装错误处理句柄 */
exec sql whenever sqlerror do sql_error();

/* 连接到数据库 */
connect2();

/* 分配绑定描述区和选择描述区 */
alloc_descriptors(MAX_ITEMS , MAX_VNAME_LEN , MAX_INAME_LEN);

for( ; ; )
{
printf("请输入动态SQL语句(exit:退出):");
gets(sql_stat);

/* EXIT(exit):退出 */
if(0 == strncmp(sql_stat , "exit" , 4) || 0 == strncmp(sql_stat , "EXIT" , 4))
break;

/* 准备动态SQL语句 */
exec sql prepare s from :sql_stat;

/* 定义游标 */
exec sql declare c cursor for s;

/* 出错,继续下一循环 */
if(0 != sqlca.sqlcode)
continue;

/* 设置绑定变量 */
set_bind_variables();

/*
* 打开游标
* select语句:生成结果集
* 其他SQL语句:执行语句
*/
exec sql open c using descriptor bind_dp;

/*
* select语句
*/
if(0 == strncmp(sql_stat , "select" , 6) || 0 == strncmp(sql_stat , "SELECT" , 6))
{
process_select_list();
}
/* 关闭游标 */
exec sql close c;
}

/* 释放选择描述区和选择描述区 */
dealloc_descriptors();

/* 提交事务,断开连接 */
exec sql commit work release;
puts("谢谢使用Oracle动态SQL方法四!\n");

return 0;
}

void connect2()
{
/* 定义宿主变量 */
char username[20] , password[20] , server[20];

/* 输入用户名、口令和网络服务名 */
printf("输入用户名:");
gets(username);

printf("输入口令:");
gets(password);

printf("输入网络服务名:");
gets(server);

/* 连接到数据库 */
EXEC SQL CONNECT :username identified by :password using :server;
}

void sql_error()
{
/* 显示SQL错误信息 */
printf("%.*s\n" , sqlca.sqlerrm.sqlerrml , sqlca.sqlerrm.sqlerrmc);
}

void alloc_descriptors(int size , int max_vname_len , int max_iname_len)
{

int i;

/* 分配绑定描述区和选择描述区 */
bind_dp = SQLSQLDAAlloc(0 , size , MAX_VNAME_LEN , MAX_INAME_LEN);
select_dp = SQLSQLDAAlloc(0 , size , MAX_VNAME_LEN , MAX_INAME_LEN);

/* 为指示变量、绑定变量和选择列表项分配内存 */
for(i = 0 ; i != MAX_ITEMS ; ++i)
{
bind_dp->I[i] = (short*)malloc(sizeof(short));
select_dp->I[i] = (short*)malloc(sizeof(short));

bind_dp->V[i] = (char*)malloc(1);
select_dp->V[i] = (char*)malloc(1);
}
}

void dealloc_descriptors()
{
int i;

/* 释放指示变量、绑定变量和选择列表项占用的内存 */
for(i = 0 ; i != MAX_ITEMS ; ++i)
{
if(bind_dp->V[i] != (char*)0)
free(bind_dp->V[i]);
free(bind_dp->I[i]);

if(select_dp->V[i] != (char*)0)
free(select_dp->V[i]);
free(select_dp->I[i]);
}

/* 释放绑定描述区和选择描述区 */
SQLSQLDAFree(0 , bind_dp);
SQLSQLDAFree(0 , select_dp);
}

void set_bind_variables()
{
int i;
char bind_var[64];

/* 设置绑定变量最大个数 */
bind_dp->N = MAX_ITEMS;

/* 绑定变量名称: 绑定描述区 */
exec sql describe bind variables for s into bind_dp;

/* 设置绑定变量实际个数 */
bind_dp->N = bind_dp->F;

/* 循环处理绑定变量 */
for(i = 0 ; i != bind_dp->F ; ++i)
{
/* 显示绑定变量名 */
printf("请输入绑定变量%.*s的值:" , (int)bind_dp->C[i] , bind_dp->S[i]);

/* 输入绑定变量的值 */
gets(bind_var);

/* 设置绑定变量的长度成员 */
bind_dp->L[i] = strlen(bind_var);

/* 为绑定变量数据缓冲区重新分配内存(多一位,留给'\0') */
bind_dp->V[i] = (char*)realloc(bind_dp->V[i] , bind_dp->L[i] + 1);

/* 绑定变量数据: 数据缓冲区 */
strcpy(bind_dp->V[i] , bind_var);

/* 设置指示变量,处理NULL */
if(0 == strncmp(bind_var , "NULL" , 4) || 0 == strncmp(bind_var , "null" , 4))
*bind_dp->I[i] = -1;
else
*bind_dp->I[i] = 0;

/* 设置数据缓冲区数据类型代码->char */
bind_dp->T[i] = 1;
}
}

void process_select_list()
{
int i , null_ok , precision , scale;
char title[MAX_VNAME_LEN];

/* 设置选择列表项的最大个数 */
select_dp->N = MAX_ITEMS;

/* 选择列表项: 选择描述区 */
exec sql describe select list for s into select_dp;

/* 设置选择列表项的实际个数 */
select_dp->N = select_dp->F;

/* 循环处理选择列表项 */
for(i = 0 ; i != select_dp->F ; ++i)
{
/* 清除select_dp->T[i]的高位->null */
SQLColumnNullCheck(0 , (unsigned short*)&select_dp->T[i]
, (unsigned short*)&select_dp->T[i] , &null_ok);

/* 根据内部数据类型确定外部类型数据长度(显示长度) */
switch(select_dp->T[i])
{
case 2:
/* number类型,取得精度与标度 */
//SQLNumberPrecV6(0 , (unsigned short*)&select_dp->T[i] , &precision , &scale);
SQLNumberPrecV6(0 , (unsigned long *)&select_dp->L[i] , &precision , &scale);  //wangbaoming modify 201409

if(scale > 0)
/* 实数: 显示长度:float  */
select_dp->L[i] = sizeof(float);
else
/* 整数: 显示长度 int */
select_dp->L[i] = sizeof(int);
break;
case 12:
/* DATA数据类型(DD-MON-YY) */
select_dp->L[i] = 9;
break;
}

/* 根据变量长度,重新为选择列表项数据缓冲区分配内存 */
if(2 != select_dp->T[i])
/* 其他类型 */
select_dp->V[i] = (char*)realloc(select_dp->V[i] , select_dp->L[i] + 1);
else
/* number类型 */
select_dp->V[i] = (char*)realloc(select_dp->V[i] , select_dp->L[i]);

/* 初始化title */
memset(title , ' ' , MAX_VNAME_LEN);

/* 选择列表项名称: title */
strncpy(title , select_dp->S[i] , select_dp->C[i]);

/* 显示列名 */
if(2 == select_dp->T[i])
if(scale > 0)
printf("\t%.*s" , select_dp->L[i] + 3, title);
else
printf("\t%.*s" , select_dp->L[i] , title);
else
printf("\t%-.*s" , select_dp->L[i] , title);

/* 根据Oracle内部类型确定外部数据类型(显示类型) */
if( 2 == select_dp->T[i])
{
/* number 类型*/
if(scale > 0)
/* float */
select_dp->T[i] = 4;
else
/* int */
select_dp->T[i] = 3;
}
else
/* char */
select_dp->T[i] = 1;
}

printf("\n");

/* 提取数据完毕->结束循环 */
exec sql whenever not found do break;

/* 循环处理选择列表数据 */
for( ; ; )
{
/* 数据->选择描述区 */
exec sql fetch c using descriptor select_dp;

/* 显示数据 */
for( i = 0 ; i != select_dp->F ; ++i)
{
if(*select_dp->I[i] < 0){
/* 处理NULL */
printf("\tNULL");
}else{
if(3 == select_dp->T[i]){
/* int */
printf("\t%d" , *(int*)select_dp->V[i]);
}else if(4 == select_dp->T[i]){
/* float */
printf("\t%8.2f" , *(float*)select_dp->V[i]);
}else{
/* char */
printf("\t%.*s" , select_dp->L[i] , select_dp->V[i]);
}
}
}
printf("\n");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言