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"); } }
相关文章推荐
- Proc *C/C++入门之常用嵌入式SQL语句
- PRO *C/C++ 动态SQL
- 动态SQL入门
- MyBatis入门07--实现sql动态模糊查询
- 源码-PL/SQL从入门到精通-第十六章-动态SQL语句-Part 1
- mybatis入门基础(五)----动态SQL
- ProC动态SQL示例(第1,2,3种方法)
- mybatis入门基础----动态SQL
- mybatis入门基础(五)----动态SQL
- mybatis入门基础(五)----动态SQL
- mybatis入门基础(五)----动态SQL
- c++ builder adoquery sql语句的动态增加
- Proc 动态SQL实现SQLPlus功能源代码
- 动态数组 - C++快速入门34
- MyBatis入门06 动态sql语句
- oracle pl/sql 入门+ 数组使用+游标+动态SQL
- 动态数组 - C++快速入门34
- oracle pl/sql 入门+ 数组使用+游标+动态SQL
- MyBatis入门(四)---动态SQL
- Mybatis入门教程(四)之mybatis动态sql