作者:张继荣 王举国 谭琦 谢元呈
PowerBuilder是目前最流行的数据库开发工具之一。PowerBuilder提供了在程序代码中加入嵌入式SQL语句的功能来支持对数据库的访问。但这种嵌入式SQL语句只能支持一些固定的标准的SQL语句,即在进行程序代码编译处理时这些SQL语句必须是确定的,例如:对哪张表哪几个字段进行操作在程序代码中是固定写明的,另外这种方式也不能执行像Creat Table,Creat Database等这些数据库定义的语句(DDL)。
因此这种嵌入式SQL语句在实际应用中有一定的局限性。为克服这种方式的局限性,可以使用PowerBuilder提供的动态SQL语句,这种对数据库访问的方式可以完成嵌入式SQL语句无法实现的功能。如建库、建表这一类的数据库定义语句(DDL);其次,由于动态SQL语句允许在执行时才确定到底要执行怎样的SQL语句,因此使用动态SQL语句可以使程序实现参数化设计,具有很大的灵活性和通用性。
一、动态SQL语句的应用分析 PowerBuilder提供了四种格式的动态SQL语句,每种格式都有自己不同的特点和作用。下面我们对各种格式的具体使用方法分别加以说明。
(一)第一种格式
当执行没有输入参数并且没有返回结果集的SQL语句时可以使用这种格式,这种格式使用比较简单,其实现的操作也比较少。
1语法
EXECUTE IMMEDIATE SQLStatement{USING TransactionObject};
其中SQLStatement是要执行的SQL语句,可以直接用引号将要执行的SQL引起来用,或者用字符串变量的形式提供SQL语句。通过使用字符串变量可以在具体执行的时候才指定要执行什么样的SQL语句。TransactionObject是用户所使用的事务对象,缺省为SQLCA。
2应用实例
①建立一张数据库表(base),SQL语句的表述用引号引起来直接使用。
EXECUTE IMMEDIATE‘CREATE TABLE base(code char(6),name char(30))’USING SQLCA;
②执行对数据库记录的操作,在表base中插入一条记录,SQL语句通过字符串变量传递执行语句。
STRING lsSQL
LsSQL=”INSERT INTO TABLE base VALUES(’320201’,’市中区支行’)”
EXECUTE IMMEDIATE:lsSQL;
(二)第二种格式
当执行带输入参数但没有返回结果集时的SQL语句可以采用第二种格式。该格式不仅可以动态地指定要执行的SQL语句,同时还可以动态地确定SQL语句所需要的参数值。
1语法
PREPARE DynamicStagingArea FROM SQLStatement{USING TransactionObject};
EXECUTE DynamicstagingArea{USING Parameterlist};
其中:DynamicstagingArea是PowerBuilder提供的一种数据类型。PowerBuilder本身提供了一个名字为SQLSA的DynamicstagingArea类型的全局变量,用于保存要执行的动态SQL语句信息。
2应用实例
删除base表中的满足一定条件的记录。
STRING lsCode
lsCode=”320101”
PREPARE SQLSA FROM“DELETE base WHERE code=?”;
EXECUTE SQLSA USING:lsCode;
(三)第三种格式
当执行有输入参数并且返回结果集的格式在编译时可以确定的SQL语句时可以使用第三种格式。这种格式语法比较复杂,但要比前面两种功能强,可以返回结果集。在返回结果时由于不知道满足过滤条件的记录到底有多少条,因此第三种格式通常采用游标的形式。
1语法
DECLARE cursor DYNAMIC CURSOR FOR DynamicStagingArea;
PREPARE DynamicStagingArea FROM SQLStatement{USING TransactionObject};
OPEN DYNAMIC cursor {USING Parameterlist};
FETCH cursor INTO VariableList;
CLOSE cursor;
其中cursor是用户所定义的游标的名字。
2应用实例
将表base中的code字段中间两位为”01”的所有记录读取出来并分别进行相应处理。
STRING lsSQL,lsCode,lsName,lsFilter
LsFilter=”01”
LsSQL=”SELECT code,name FROM base WHERE substring(code,3,2)=?”
DECLARE cursor_base DYNAMIC CURSOR FOR SQLSA;
PREPARE SQLSA FROM:lsSQL;
OPEN DYNAMIC cursor_base USING:lsFilter;
FETCH cursor_base INTO:lsCode,:lsName;
DO WHILE SQLCA.SQLCODE=0
. ∥对满足条件的记录根据要求分别进行处理
. FETCH cursor_base INTO:lsCode,:lsName;
LOOP
CLOSE cursor_base;
(四)第四种格式
当执行有输入参数并且返回结果集的格式在编译时无法确定的SQL语句时可以使用第四种格式。有时候我们执行一些SQL语句时,不仅带有参数而且返回的结果集中有多少个字段,每个字段是什么类型的数据都不确定,这时只能使用第四种格式来实现。
1语法
PREPARE DynamicStagingArea FROM SQLStatement{USING TransactionObject};
DESCRIB DynamicStagingArea INTO DynamicDescriptionObject;
DECLARE cursor DYNAMIC CURSOR FOR DynamicDescriptionObject;
OPEN DYNAMIC cursor USING DESCRIPTOR DynamicDescriptionObject;
FETCH cursor USING DESCRIPTOR DynamicDescriptionObject;
CLOSE cursor;
其中:DynamicDescriptionObject是PowerBuilder提供的一个数据类型,在PowerBuilder中提供了一个DynamicDescriptionObject类型的全局数据类型SQLDA,用来存放动态SQL语句的输入输出参数。
2应用实例
将一个表中满足过滤条件的记录的所有字段取出来分别进行处理,表名在程序运行中由字符串变量传递,字段信息是不确定的。这里我们假设通过字符串变量中传递的表名是base。
STRING lsString,lsSQL,lsTable,lsColumn
INT liInt
DATETIME liTime
LsSQL=”SELECT*FROM base WHERE code like?”
PREPARE SQLSA FROM lsSQL;
DESCRIB SQLSA INTO SQLDA;∥SQLDA中含有输入参数的描述
DECLARE cursor_base DYNAMIC CURSOR FOR SQLSA;
SetDynamicparm(SQLDA,1,”32%”)∥传递参数值
OPEN DYNAMIC cursor_base USING DESCRIPTOR SQLDA;
FETCH cursor_base USING DESCRIPTOR SQLDA;
DO WHILE SALCA.SQLCODE=0
FOR liInt=1 TO SQLDA.NumOutPuts
CHOOSE CASE SQLDA.OutParmType[liInt]
CASE Typestring!
lsString=GetDynamicString(SQLDA,liInt)
∥处理该字符型的字段
CASE TypeDateTime
LsDateTime=GetDynamicDateTime(SQLDA,liInt)
∥处理该日期型的字段
... ∥处理其他类型的字段
... END CHOOSE
NEXT
∥将一条记录的所有字段取完后作相应的处理
FETCH cursor_base USING DESCRIPTOR SQLDA;
LOOP
CLOSE cursor_base;
二、结束语
从上面的说明和举例中可以看出动态SQL的特点是功能强,使用灵活,完全可以在程序代码中动态地生成具体要执行的SQL语句,是嵌入式SQL无法比拟的。当然动态SQL语句在运行速度上可能要比嵌入式SQL语句略慢一点,但随着计算机处理速度的大幅度提高,现在来说这一点已不成什么问题。
因此,使用动态SQL语句仍是一种非常可取的处理方法。根据我们在实际编程中的体会,正确使用动态SQL语句可使程序代码简练、灵活、通用,不仅降低了初次编程的工作量,而且也降低以后对代码的维护量,能够取得事半功倍的效果。
【PB】动态SQL语句
所谓动态SQL语句是指部分或者整个SQL语句在运行时才能确定,这样的SQL语句可以用来设计能和用户进行更好交互的界面。因为参数的确定留给用户总比编程时由开发人员规定死要好得多。另外, PowerBuilder不支持有些SQL语句的嵌入执行,例如Create Table,Drop Table等,需要将这些SQL语句交给DBMS执行,而动态SQL语句都是将SQL语句交给DBMS执行的。这些都是要掌握动态SQL语句的原因。
动态SQL语句有四种类型:
l 既无输入参数,也无结果集;
l 有输入参数,但没有结果集;
l 编译时已经知道参数和结果集的列;
l 开发程序时尚不知道参数和结果集。
10.2.1 类型一
这种类型的动态SQL语句经常用来执行DDL或者数据库专用的其他SQL语句。语法格式是:
EXECUTE IMMEDIATE SQLStatement {USING TransactionObject} ;
其中SQLStatement是个字符串,其内容是有效的SQL语句;TransactionObject是事务对象名,大括号表示该子句可以省略,省略时使用SQLCA。下面是个创建删除数据表的例子:
string MySQLMySQL = "drop table employee"
EXECUTE IMMEDIATE :MySQL USING SQLCA;
运行之前已知参数个数并且没有返回值时使用这种类型的动态SQL语句。这种类型的动态SQL语句也能够处理需要在运行时定义参数的数据操作语句。其语法格式为:
PREPARE DynamicStagingArea FROM SQLStatement
{USING TransactionObject} ;
EXECUTE DynamicStagingAreaUSING {ParameterList} ;
其中DynamicStagingArea是个DynamicStagingArea类型的变量,该类型的缺省全局变量是SQLSA;SQLStatement是个String类型的常量或者变量,其内容是有效的SQL语句,SQL语句中使用问号代表所需参数,执行时问号被EXECUTE语句中的USING子句所代表的值取代;TransactionObject是事务对象名,大括号表示该子句可以省略,省略时使用SQLCA;ParameterList是参数列表,可以是变量、常量或者控件的属性,各参数对应于SQLStatement中的问号。动态策略区用于准备SQL语句及所需参数个数,它的属性在运行时应用程序不能访问,SQLSA是缺省的动态策略区变量。下面是这类动态SQL语句的应用示例,它删除编号为56的雇员信息:
Int Emp_id_var = 56
PREPARE SQLSA FROM "DELETE FROM employee WHERE emp_id=?" ;
EXECUTE SQLSA USING :Emp_id_var ;
再如:
Prepare SQLSA from
"Insert inTo employee (emp_id,manager_id) value (?,?)"
execute SQLSA using :ls_EmpId,:sle_manager.text;
这种类型的动态SQL语句的使用频率可能是仅次于第一种类型,它常用来处理参数个数和结果集在编译时已知的情况,又分为游标和存储过程两种情况。使用游标形式的语法与程序中出现的次序为:
DECLARE Cursor DYNAMIC CURSOR FOR DynamicStagingArea ;
PREPARE DynamicStagingArea FROM SQLStatement{USING TransactionObject} ;
OPEN DYNAMIC Cursor{USING ParameterList} ;
FETCH Cursor |INTO HostVariableList} ;
CLOSE Cursor;
使用存储过程的第三类动态SQL语句的格式和次序与上面的语法形式类似,只是使用EXECUTE语句来代替上面的OPEN语句,其语法格式为:
DECLARE Procedure DYNAMIC PROCEDUREFOR DynamicStagingArea ;
PREPARE DynamicStagingArea FROM SQLStatement
{USING TransactionObject} ;
EXECUTE DYNAMIC Procedure{USING ParameterList} ;
FETCH ProcedureINTO HostVariableList} ;
CLOSE Procedure ;
其中,Cursor和Procedure分别是游标名和过程名;DynamicStagingArea是动态策略区变量,通常使用系统预定义的全局变量SQLSA;SQLStatement是个字符串(常量或变量均可,变量时变量名前面加上冒号(:)),其内容是有效的SQL语句,并使用问号代表参数;ParameterList是对应于SQLStatement中问号的参数列表;HostVariableList是PowerScript主变量(即在其前面加上冒号的PowerScript变量);TransactionObject是事务对象名,缺省时使用SQLCA。
DECLARE语句说明动态游标或动态过程,PREPARE语句准备动态策略区,OPEN或EXECUTE语句打开动态游标或执行动态过程,FETCH语句读取一行数据,如果需要读取多行数据,那么需要反复执行FETCH语句。最后,CLOSE语句关闭动态游标或动态过程。FETCH语句和CLOSE语句的用法与上节介绍的方法相同。下面是第三类动态SQL语句的一个应用示例,它得到籍贯是“北京”的雇员:
DECLARE my_cursor DYNAMIC CURSOR FOR SQLSA;
Int Emp_id_var
String SQLstatement,Emp_state_var = "北京"
SQLstatament = "SELECT emp_id FROM employee WHERE emp_state = ?"
PREPARE SQLSA FROM :SQLstatement;
OPEN DYNAMIC my_cursor using :Emp_state_var;
FETCH my_cursor INTO :Emp_id_var;
CLOSE my_cursor;
该示例中省略了必要的SQL语句执行状态检查工作,在实际编写程序时,除DECLARE语句外,执行了其他SQL语句后,都应该检查事务对象的SQLCode属性,以判断SQL语句的执行是否成功。
使用这种类型的动态SQL语句可以创建比较通用的向列表框或者下拉列表框中添加数据的脚本。
//
//函数名称wf_AddItem(dropdownlistbox fo_obj,string fs_SQL)
//参数:fo_obj为下拉列表框,fs_SQL为SQL语句
//返回值:无
//功能:使用参数指定的SQL语句向指定的下拉列表框中添加数据
String ls_item
Declare item_cur Dynamic Cursor For SQLSA; //定义动态光标
Prepare SQLSA From :fs_SQL Using SQLCA;
Open Dynamic item_cur; //打开动态光标
Fetch item_cur InTo :ls_item; //取数据
fo_obj.SetRedraw(False) //禁止下拉列表框刷新
Do While SQLCA.SQLcode = 0
fo_obj.AddItem(ls_item) //向下拉列表框中添加项目
Fetch item_cur inTo :ls_item;
Loop
fo_obj.SetRedraw(True) //刷新下拉列表框
Close item_cur; //关闭动态光标
10.2.4 类型四
第四类动态SQL语句最复杂,功能也最强,它能够处理编程时尚不知道参数和结果集的SQL语句。与第三类动态SQL语句相似,第四类动态SQL语句也有两种形式:一种针对游标处理,另一种则针对存储过程而言,区别在于游标处理时使用OPEN语句而不使用EXECUTE语句,而运用存储过程时则使用EXECUTE语句但不使用OPEN语句。为节约篇幅,下面一并给出第四类动态SQL语句的语法,其中Cursor针对游标,Procedure针对存储过程:
DECLARE Cursor | ProcedureDYNAMIC CURSOR |
PROCEDUREFOR DynamicStagingArea;
PREPARE DynamicStagingArea FROM SQLStatement
{USING TransactionObject};
DESCRIBE DynamicStagingArea INTO DynamicDescriptionArea;
OPEN DYNAMIC CursorUSING DESCRIPTOR DynamicDescriptionArea};
EXECUTE DYNAMIC ProcedureUSING DESCRIPTOR DynamicDescriptionArea;
FETCH Cursor | ProcedureUSING DESCRIPTOR DynamicDescriptionArea;
CLOSE Cursor | Procedure;
其中,Cursor和Procedure分别是游标名和过程名;DynamicStagingArea是动态策略区变量,通常使用系统预定义的全局变量SQLSA;SQLStatement是个字符串(常量或变量均可,变量时变量名前面加上冒号(:)),其内容是有效的SQL语句,并使用问号代表参数;DynamicDescriptionArea是动态描述区变量,这种类型的变量专门用来描述第四类动态SQL语句中的输入和输出参数,通常使用系统预定义的全局变量SQLDA;TransactionObject是事务对象名,缺省时使用SQLCA。第四类动态SQL语句使用了动态描述区对象变量,通过该对象变量的四个属性NumInputs,InParmType,NumOutputs和OutParmType能够得到输入参数个数、输入参数类型、输出参数个数和输出参数类型的信息,其中InParmType是个数组,每个元素依次对应于SQL语句中的一个问号;OutParmType也是个数组,每个元素对应于一个输出参数。InParmType和OutParmType的数据类型是枚举类型ParmType,其取值范围为表10-6中“适用的参数类型”栏列出的各个值。
表10-6 得到第四类动态SQL输出参数的函数
函 数 名 | 适用的参数类型 |
GetDynamicNumber() | TypeInteger! TypeDecimal! TypeDouble! TypeLong!TypeReal! TypeBoolean! TypeUnsignedInteger! TypeUnsignedLong! |
GetDynamicString() | TypeString! |
GetDynamicDate() | TypeDate! |
GetDynamicTime() | TypeTime! |
GetDynamicDateTime() | TypeDateTime! |
通过使用该对象变量的函数SetDynamicParm()设置具体的输入参数值。通过使用表10-6中的对象函数得到输出参数(实际上就是SQL语句的返回数据)的值,每个函数都针对特定的数据类型。
第四类动态SQL语句语法格式中各语句的执行次序十分重要,只有在前一条语句执行成功时,后一条语句的执行才有意义,因此,除DECLARE语句外,其他语句执行后都应该检查事务对象的SQLCode属性,以判断当前SQL语句的执行是否成功。通过多次调用FETCH语句,能够读取多条数据,每读出一条数据后,通常在循环语句中使用CHOOSE CASE确定
输出参数的类型后再用表10-6中的对象函数得到其值。下面是第四类动态SQL语句的一个应用示例,其中省略了实际编程中必须具备的检查事务对象SQLCode属性的过程(即检查SQL语句执行是否成功):
String , ls_SqlStatement
Int,li_count = 0
ls_SqlStatement = "SELECT emp_id FROM employee"
Prepare SQLSA From : ls_SqlStatement ;
Describe SQLSA InTo SQLDA ;
Declare my_cursor Dynamic Cursor For SQLSA ;
Open Dynamic my_cursor Using DescripTor SQLDA ;
Fetch my_cursor Using DescripTor SQLDA ;
If SQLCA.Sqlcode = 100 Then
MesasgeBox("提示", "没有找到指定的数据! ")
Close my_cur;
Return
End If
Do
Li_count ++
//当FETCH语句执行成功时,动态描述区SQLDA中包含了结果集的
//第一行数据,反复执行FETCH语句即可得到其余数据。
// SQLDA.NumOutputs中包含了输出参数的个数。
// SQLDA.OutParmType数组中包含了各参数的数据类型,
//例如TypeInteger!, 或 TypeString!等
//使用CHOOSE CASE语句针对不同的输出参数类型调用不同的对象函数
//得到相应参数的值。
Choose Case SQLDA.OutParmType[1]
Case TypeString!
Stringvar = GetDynamicString(SQLDA, 1)
Case TypeInteger!
Intvar = GetDynamicNumber(SQLDA, 1)
End Choose
Loop While li_count <> SQLDA.NumOutPuts
Close my_cursor ;