Tivoli 服务台 6.0 开发工具包脚本程序设计指南
开发工具包专为简化客户机/服务器数据库应用程序的建立过程而设计。因此,TSD 脚本语言包含一系列用于与 SQL 数据库进行交互处理的语句。
TSD 脚本 SQL 接口是语言的固有组成部分。SQL 接口灵活地调节 TSD 脚本 RECORD 和 LIST 类型。此外,TSD 脚本 SQL 语句提供内置的被动一致性,以开发可靠的多用户应用程序。
另一非常有用的特性是对 Oracle 进行数组提取的能力。数组提取数据显著地减少了查询数据库的时间。
SQL, 或结构化查询语言,是一种工业标准语言,用于:
因为本书不准备用于 SQL 指导,所以在阅读本章之前,如能熟悉 SQL 的语法,将大有帮助。
开发工具包支持下列数据库 (也叫源):
开发工具包和 TSD 脚本语言支持各种 SQL 语句和数据库,用于开发客户机/服务器应用程序。
有一些 SQL 语句不能由 Oracle, Sybase/SQLServer 和 Informix 的驱动程序直接支持。下表列出了这些语句。
语句... | 仅由下列数据库支持... |
SQLDeleteCurrent | DB2/2 |
SQLGetCursorName | DB2/2 |
SQLUpdateCurrent | DB2/2 |
注: SQLUpdateCurrent 和 SQLDeleteCurrent 要求使用嵌套语句,并应尽可能地少用。通常认为这些语句已过时,不被广泛支持。
本节说明了当您配置数据源时可用的属性。属性控制着开发工具包中数据库层的行为。
注:当您使用 SQL 配置编辑器来配置数据源时,这些属性并非完全必要。请参阅本章稍后出现的把应用程序连接到数据库一节。
数据源是由特定名称指定的信息集合,用于定义数据库和数据库的连接。
注:由您来选择数据源的名称。TSD 脚本不要求,也不认别任何预先设定的名称。TSD 脚本要求数据源的名称不能以数字开始。但是,用 TSD 脚本编写的应用程序可能会要求某些特定的源名。例如,Tivoli 问题管理要求以"ADVISOR"作为数据源名称。要了解这类要求,请查阅应用程序文档。
当数据源配置好以后,该数据源的属性(或特性),可用 ATTRIB=VALUE 语句列出来。下面是一些可能的属性:
下面的属性通常不使用:
TSD 脚本对它所支持的每个 BMS 自动采用缺省值。仅在特殊情况下,例如在区分大小写时,才使用这些属性来替换缺省值。
在大多数情况下,您无须替换缺省设置。许多属性影响数据库的高级特性,应仅由数据库管理员来更改。
如果您使用了区分大小写的对象名称,在查询时就应当用正确的大小写进行编码。如果您全部采用大写或全部采用小写建立数据库的所有对象,TSD 脚本可以替您把所有的对象名称转换到您要求的那种大/小写格式。这一属性条目的值可能有:UPPER, LOWER 或 NONE。如果本条目不存在,那么缺省值是 NONE。
注: TSD 脚本总保留查询中引用的字符串的原有大小写格式。
本条目指您的数据库管理系统( DBMS ) 处理有关数据库对象的大小写的方式。数据库对象是指表名,列名等等。如果此值是 TRUE, 那么 TSD 脚本保留所有对象名的原有大小写格式 (除非设置了 CASE_CONVERSION )。如果 CASE_SENSITIVE 条目不存在,则缺省值是 FALSE。
下列条目可用于替换 TSD 脚本在缺省列目录视图中所要求的列名。
列 | 缺省值 |
COL_NAME_COLUMN | NAME |
COL_REMARKS_COLUMN | REMARKS |
COL_LENGTH_COLUMN | LENGTH |
COL_SCALE_COLUMN | SCALE |
COL_TYPE_COLUMN | COLTYPE |
COL_NULLS_COLUMN | NULLS |
COL_TBNAME_COLUMN | TBNAME |
COL_TBCREATOR_COLUMN | TBCREATOR |
注: TSD 脚本对它支持的 DBMS 自动采用缺省值。因此,在通常状况下不使用本条目。本条目仅用于要替换缺省值的特殊情况。它包含了列字典/目录表或视图名。因为 Tivoli 产品为保持一致性,提供了"包装"视图,所以缺省值总是 SAI_SYSCOLUMNS。
本条目仅在 MANUAL_COMMITS 设置为 TRUE 时可用。如果可用,那么缺省 COMMIT_SELECTS 设置是 TRUE。
如果您想在选择后自动承诺,则把 COMMIT_SELECTS 项设置为 TRUE。
DATE_FORMAT 是数据源 (DBMS) 所要求的日期格式。对于大多数系统,缺省的日期格式是:月/日/年( mm/dd/yyyy)。Oracle 的缺省日期格式是日/月/年(dd-MON-yy)。
您可以通过指定日、月、年和分隔符来定制日期格式。虽然大多数项目的组合是支持的,但是也有少数例外。例如,支持 yyyymmdd,但不支持 yyyyddmm。
您可以去掉日和月前面打头的零。如要去掉日期前打头的零,在日期中仅指定一个"d"。
注: 如果您去掉了日前的零,那么必须也去掉月前的零。例如,mm/d/yyyy 是不允许的。
实例 | 结果 |
mm/dd/yyyy | 07/08/1999 |
m/d/yyyy | 7/8/1999 |
您可以通过指定日历中月份的数字(1 = 一月) 或月份的名称来指定月份。要用数字来指定月份,用"MM".。要去掉月份数字前的零,用"M".。要用月份名或简写来指定月份,用"Month"或"Mon."
月份名称说明符 (Month 和 Mon) 都是区分大小写的。月份数字说明符(MM 和 M) 不区分大小写。
注:如果去掉了月前的零,必须也去掉日前的零。
实例 | 结果 |
MM | 08 |
M | 8 |
Month | August |
Mon | Aug |
MONTH | AUGUST |
MON | AUG |
指定年的时侯,您可以使用全部数字或只使用年的最后两位数字。
注意: Tivoli 建议,为防止两千年问题,最好使用四位数字。
实例 | 结果 |
mm/dd/yyyy | 07/08/1999 |
mm/dd/yy | 07/08/99 |
日期格式可采用下列分隔符:
分隔符类型 | 实例 | 结果 |
无 | yyyymmdd | 19990626 |
句点 | mm.dd.yyyy | 06.26.1999 |
连字符 | dd-mm-yyyy | 26-06-1999 |
正斜杠 | mm/dd/yyyy | 06/26/1999 |
对于 ODBC 驱动程序,在 SQL 配置文件中需要 DBMS 列表。可被 BMS= 条目列出的数据库如下:
如果此值为 TRUE,则在连接时视此数据源为缺省数据源。在每个 SQL 配置文件中,仅允许有一个 DEFAULT 项。如果本条目不存在,那么缺省值是 FALSE。
这是 DBMS 驱动程序名,是连接时所要求的。
此属性为 SQL 配置文件(仅 ODBC)列出了数据源名称。
本条目对直接接口可用;如果您想在 TSD 开发工具包级别上处理事务控制,应将此项设为 TRUE。
如果 DBMS 支持多层、同时连接,应将此项设为 TRUE。
如果 DBMS 要求多层连接,以同时执行多个打开语句(例如,嵌套式选择/读取循环)时,设置此项为 TRUE。对于 Sybase 和 SQLServer,缺省值为 TRUE。对于其它所有数据库,缺省值为 FALSE。
注:对于需要时,TSD 脚本可为您打开附加连接。
这是用于表存取的限定符。SQL 在表定义中存储了表的建立者/所有者。建立者/所有者的名称即称为表的限定符。在数据库中,必须组合使用限定符和表名称来唯一标识一个表。
此条目包含存取系统目录表/视图所需限定符的名称。因为所有的 Tivoli 产品为保持一致性提供了"环绕"视图,所以缺省值与当前的限定符一致(在缺省连接字符串中)。
本条目包含表字典/目录表或视图名。因为所有的 Tivoli 产品为保持一致性提供了"包装"视图,所以缺省值总是 SAI_SYSCOLUMNS。
下列五个条目可用于替换 TSD 脚本在缺省表目录视图中所要求的列名。
TAB_NAME_COLUMN | NAME |
TAB_REMARKS_COLUMN | REMARKS |
TAB_TYPE_COLUMN | TYPE |
TAB_CREATOR_COLUMN | CREATOR |
TAB_COLCOUNT_COLUMN | COLCOUNT (用于 DB2/2 和 SQLBase) |
本条目是数据源所要求的时间格式。所有 DBMS 条目的缺省值是 hh:mm:ss。尽管大多数时间格式说明符是有效的,但也有一些是无效的。例如,支持 hh:mm:ss,而不支持 mm:hh:ss。
参见在 TSD 6.0 脚本语言参考中有关时间格式语句一节,以获得关于有效时间格式的详细信息。
在缺省设置下,时间用军用时制(24 小时制)来指定。若要指定 12 小时制,则需加上 AM_PM 的后缀:
实例 | 结果 |
hh:mmAM_PM | 09:08 PM |
hh:mm | 21:08 |
格式说明符是不区分大小写的。例如 hh:mm:ss 等价于 HH:MM:SS
要去掉前导零,则仅指定一个 h、m 和 s。例如:
实例 | 结果 |
hh:mm:ss | 08:35:09 |
h:m:s | 8:35:9 |
注:如果您选择去前导的零,那么您就必须把小时、分钟和秒前头的零都去掉。
要指定秒,需要加上 ss 标记。您也可选择忽略秒。
实例 | 结果 |
hh:mm:ss | 08:35:09 |
h:m | 8:35 |
您可以选择冒号或句点来作为时间说明符的格式。
分隔符类型 | 实例 | 结果 |
无 | hhmmss | 121604 |
句点 | hh.mm.ss | 12.16.04 |
冒号 | hh:mm:ss | 12:16:04 |
本条目包含 DBMS 功能名称,用于把查询的列转换为大写方式(由 SQLCreateSearchString 使用)。缺省值是:
对于所有的 SQL DBMS 来说,添加或连接一个数据库是常见的。这意味着根据所用的数据库不同,连接也不同。例如:
为了屏蔽这些不同之处,TSD 脚本提供了一系列使用 SQLCommand 语句的连接服务。SQLCommand 允许外部命令可用于 SQL 配置文件中的 DBMS 特定连接信息。这样可以简化应用程序代码,并使之具有可移植性。
开发工具包 SQL 的配置文件的缺省名是 sai_sql.cfg。开发工具包在 SAI 根目录下的 CFG 子目录中查找一个名为 sai_sql.cfg 的文件。
您可以通过设置一个名为 SAISQLCFG 的环境变量,使其包含一个不同的文件名来替换 TSD 脚本寻找的文件名。如果两者都设的话,SAISQLCFG 环境变量中的值优先级别高于来自 SAI_ROOT 的路径。如果文件没有找到,将返回一个出错信息。
SQL 配置文件分成几个部分来描述数据源。将您正在使用的数据源看作 DBMS。通常,数据源对应于物理数据库服务器,尽管这一概念可引申到包含服务器中的逻辑源(例如,一个数据库一个源)。
开发工具包提供了一些您可以直接使用的基于数据库要求的数据库接口。这些直接数据库接口是开发工具包的缺省数据库的连接方法。
如果您想使用 ODBC, 而不是使用直接数据库接口,则可参阅 Tivoli 服务台 6.0 安装指南中的附录 A "ODBC 配置"以获得有关 ODBC 驱动程序的详细信息。
如果您使用开发工具包直接数据库接口,请参阅 Tivoli 服务台 6.0 安装指南中的"配置进程",以获得全面的配置和测试说明。
按照下列步骤初次添加一个数据库客户机:
SQL 配置编辑器对话框中有下列组成部分:
- 文件 - 用于写入建立数据库客户机连接的配置文件名称的文本框。这个字段通常包含您使用的文件名。
- 源 - 包含数据源名称的列表框。
- 选项 - 包含所选数据源选项的列表框。
- 配置 - 用于配置所选数据源选项的按钮。
- 添加 - 用于建立一个新的数据源项的按钮。
- 删除 - 用于删除所选数据源项的按钮。
- 复制 - 用于复制所选数据源内容的按钮。当复制数据源时,它包含与原文同样的数据,但名称是新的。
- 重命名 - 用于重新命名所选数据源的按钮。这一功能重新命名现存数据源,而不复制。
- 设置为缺省 - 使所选数据源成为缺省设置的按钮。
- 连接测试 - 对配置好的数据库进行连接试验,并返回成功或错误信息的按钮。
- 建立跟踪 - 跟踪数据库活动,为调试提供信息报告的按钮。
- 口令安全 -
- 限定符 - 因为 SQL 允许在一个数据库中有多个同样名称的表,SQL 在表定义中存储了表的建立者/所有者的名称。这个建立者/所有者的名称用作表的限定符。例如,EXAV 可在建立源的对话框中用作限定符。
- 服务器 - 客户机向其请求和发送信息的服务器名称。
- 数据库 - 在您的位置的数据库名称。
- 驱动器 - 您用来连接到服务器的驱动器名称。
- ODBC DSN - 如果您在驱动程序选择对话框中选择了 ODBC 驱动程序选项,那么需要在此输入 ODBC 数据源名称。如果您选择了 ODBC 选项,请参阅 Tivoli 服务台 6.0 安装指南中的附录 A: ODBC 配置,来获取完整信息。
- 标识符(ID) - 用户用于访问数据库的标识符(ID)。(任选项)
- 口令(PWD) - 用于访问数据库的口令。(任选项)
- 缺省项复选框 - 如果您想把所选的用于连接数据库的选项设置为缺省配置,则选本框。
结果:如果您选择了确认,则过程完成。如果您在设置源对话框中选择了高级,则出现高级选项设置对话框。
在添加了新的数据库连接后,应测试一下连接,以确保它工作正常。完成下列步骤,检测数据库连接:
如果您在 SQL 配置文件中保存用户标识和口令,就会有潜在的安全问题。如果您为一个以上用户保存特定连接信息,可能会有困难,因为文件可以被所有的用户容易地访问,而且在缺省条件下,所储存的口令是不加密的。
用户必须使用 SQL 配置编辑器来选择加密。参阅本指南的数据库配置实例来获得更多信息。
TSD 脚本提供了一种简单的方法来用连接语句自身提供连接特定信息。
ret := SQLCommand('CONNECT SOURCE=DB2_TEST; UID=FRED; PWD=DERF;');
当 TSD 脚本遇到这种连接请求时,会出现以下情况:
找到 DB2_TEST 的缺省连接字符串 (DB2_TEST 必须是 SQL 配置文件中的一个源)。如果没有找到 SOURCE= 项,那么 TSD 脚本使用缺省值。
注:如果不存在其它属性,那么需要在数据源名之前加上 SOURCE=。
可以用如下方法来形成一个新的连接字符串:将剩余的 ATTRIBUTE=VALUE 语句对覆盖到缺省连接字符串中相应的项中,或插入缺省连接字符串中没有的新语句对。
您可以覆盖除了驱动器(DRV=)之外的任何缺省连接字符串的项。这就允许您拥有了可以按要求进行覆盖的缺省连接信息(数据库、表限定符等等)。您也可以通过建立新的数据源(例如一个数据库一个源)来获得一些类似效果。
可以为 SQLcommand 提供所有的连接信息,并取消 SQL 配置文件。但是,您必须愿意接受所有缺省源的值(例如 MULTI_CONNECT 等等)。
把 DBMS 特定信息外部化通常一个是好的办法。但是,有时您可能需要取消 SQL 配置文件。为了处理这种情况,TSD 脚本允许您连接并注册一个不在 SQL 配置文件中的新源。
ret := SQLCommand('CONNECT NEWSOURCE=MYAPP; DRV=XOORA;SRVR=X:ORASERV; UID=MARY; PWD=');
本章剩余部分参考一个实例数据库,名为 COMPANY。此数据库由以下表和视图组成。列属性在每个表或视图名下面列出。
Table DEPARTMENT
Table EMPLOYEE
Table MANAGER
SQLSelectInto 语句提供了在 SQL 表或视图中检索信息的简单方法。它在自变量中取一个变量数值。
第一个自变量总是一条字符串表达式,代表 SQLSelect 语句,例如:
SELECT * FROM EMPLOYEE WHERE EMPLOYEE_ID='305-83-3811'.
在大多数情况下,SQLSelectInto 用于从表中检索一个唯一的行。这一选择表达式作为第一个自变量,通常包括下列格式的从句:
WHERE <primary-key>=<value>
通常,选择语句的格式是:
SELECT <column-list> FROM <table-or-view name> WHERE <condition>.
<column-list> 是用逗号分隔的一个或多个列名的列表(例如"FIRST_NAME, LAST_NAME")。如果想从表中获得所有的列,可以用星号(*)来代替。
<condition> 是真/假(TRUE/FALSE)条件,通常一个列名、一个关系运算操作符 (=, >, <, >=, <=)和一个值(例如 "SMITH")组成。
下列是有效的选择语句实例:
SELECT EMPLOYEE_ID,LAST_NAME FROM EMPLOYEE WHERE DEPARTMENT_ID='SALES' SELECT FIRST_NAME,LAST_NAME,PHONE FROM EMPLOYEE SELECT * FROM MANAGER
SQLSelectInto 的第二个(后续)自变量是 TSD 脚本变量。这些变量名称必须与正在访问的 SQL 表或视图中的列名匹配,如下例所示:
VARIABLES last_name, first_name: String; ACTIONS SQLSelectInto('SELECT LAST_NAME,FIRST_NAME FROM EMPLOYEE WHERE EMPLOYEE_ID=123', last_name,first_name);
TSD 脚本变量是否区分大小写在处理这些例子时不重要。LAST_NAME 列中的值读到 last_name 字符串变量中去。FIRST_NAME 列中的值读到 first_name 列中。
从 SQL 表中获得信息的一个简单方法是使用 TSD 脚本记录变量。在 SQL 表中字段与列名匹配的地方定义一个记录类型。然后可以把这个类型的记录传送到 SQLSelectInto,如下所示:
TYPES EmployeeRec IS RECORD employee_ID: Integer; last_name: String; first_name: String; birth_date: Date; salary: Real; END; VARIABLES r: EmployeeRec; SQLSelectInto('SELECT LAST_NAME,FIRST_NAME FROM EMPLOYEE WHERE EMPLOYEE_ID=123',r);
一般来说,您定义某个记录类型时,其名称应与数据库中的每个表或视图相同。使用 TSD 脚本的名称绑定功能,使您能够使用那种类型的记录从数据库中提取信息,或者把信息添加到数据库。
在之前的实例中,定义了 EmployeeRec 记录类型,以使字段名和类型与 EMPLOYEE SQL 表中的列名和类型相匹配。当您把一个这种类型的记录(r)传送到 SQLSelectInto 中时,开发工具包自动把信息从要求的行复制到匹配的字段中。在执行 SQLSelectInto 后,r 具有下列值:
r.employee_ID = $Unknown r.last_name = 'Brown' r.first_name = 'Robert' r.birth_date = $Unknown r.salary = $Unknown
请注意仅 r.LAST_NAME 和 r.FIRST_NAME 列有了值。记录中所有的字段以值 $Unknown 开始。因为 SQLSelectInto 语句选择了LAST_NAME 和 FIRST_NAME 列,仅 r.LAST_NAME 和 r.FIRST_NAME 被赋予了值。如果使用了下列语句,所有的 r 字段应已完成:
SQLSelectInto('SELECT * FROM EMPLOYEE WHERE EMPLOYEE_ID=123',r);
TSD 脚本记录类型与 SQL 表之间的映射是一个直接的过程。TSD 脚本允许您通过引入语句在表、视图和记录型之间建立自动联系。
这意味着您无须手动为表或视图的每列说明记录字段。请看下面的实例:
TYPES EmployeeRec IS RECORD IMPORT('EMPLOYEE'); END;
引入语句可在记录说明之内使用,以从 SQL 表或视图中的名称和类型直接建立字段说明。
如果表或视图改变了,记录说明自动跟踪变化。也就是说,如果您把新的 ADD
RESS 列添加到 EMPLOYEE 表中,则不需要在添加新的
address: String;
字段到 EmployeeRec 中。IMPORT 是一个时间分析语句。任何时候重建包含 IMPORT 语句的程序块(分析好的),就会读取所指示的 SQL 表,且它们的列被用来建立字段说明。
引入语句有一个与表名称一起指定的连接字符串。这个连接字符串代替在 sai_sql.cfg 文件中指定的缺省连接字符串。为了能够向下兼容,您应当以 'DATABASE xxx'的格式来指定数据库名称, 其中 xxx 是数据库名。
分析时,应当把 /S 选项传送给 TSD 脚本分析程序,来代替硬编码的数据库或引入语句中的连接信息。
/S 选项允许您在分析时指定数据库,这样就没有必要把数据源名称硬编码。它使您在命令提示符下运行 TSD 脚本分析器时指定用户标识和口令。这些值替换了 sai_sql.cfg 文件中的缺省值。
使用 /S 选项的较好方法是在 sai_sql.cfg 文件中指定除了用户标识和口令之外的所有值。然后,当您分析时,这些值可以被传送进来。例如:
/S"UID=UserId;PWD=Password"
(这里 UserId 是指用户标识,Password 是用户的唯一口令。)
当您使用 IMPORT 时,您可以使用任选的关键字来使 IMPORT 的内容排序清晰化。关键字是:
如果您不指定任何关键字,引入字符串的排序就默认为是表名称在前,随后是连接字符串。
使用空白和/或逗号来分隔表名称和连接信息。
注:您可以使用 TABLE 或 VIEW 关键字中的任何一个来指定表名称。处理时视为等同。
在本书中,关键字以大写出现,它们是不区分大小写的。但是,表名和连接信息可能是区分大小写的,取决于您 DBMS 的安装情况。请咨询局字段网管理员或数据库管理员来获得有关信息。您也可以参考 DBMS 所附的手册来确定到底有没有区分大小写的问题。
有许多种方法来建立有效的引入字符串。下面的例子可提供给您一系列的选择。但是,建议您选择一种类型,而后始终使用。
使用 IMPORT 的较好方法如下所示:
Import('ALARMS');
SA-ASE 4.1 支持下列语法:
Import('DATABASE ADVISOR TABLE ALARMS');
对于 SA-ASE 4.2 和其后续版本,语法是:
'CONNECT DB=ADVISOR'
下面的例子也等同于前面的例子:
Import ('CONNECT DB=ADVISOR TABLE ALARMS');
Import('TABLE ALARMS CONNECT DB=ADVISOR');
Import('ALARMS, CONNECT');
Import('TABLE ALARMS');
Import('VIEW PROBLEM_VIEW');
在下面的例子中,请注意在连接字符串中没有空格:
Import('ALARMS, SRVR=X:ORASRV;DB=ADVISOR');
下面也是正确的:
Import('TABLE ALARMS, CONNECT SRVR=X:ORASRV; DB=ADVISOR);
在下面的例子中,"'"是任选项。
Import('TABLE ALARMS CONNECT SRVR=X:ORASRV; DB=ADVISOR');
下面也是正确的:
Import('CONNECT SOURCE=ADVTEST;SRVR=X:ORASRV; DB=ADVISOR, TABLE ALARMS);
注意下面的语法是不正确的,因为字符串 'ADVISOR' 不是有效的连接字符串( 'DB=ADVISOR' 是有效的连接字符串)。
Import('ALARMS,ADVISOR'); (*this is WRONG!*)
TSD 脚本有六种简单的数据类型:
IMPORT 语句采用类属接近方法,按照数据类型把列与字段映射对应。
列 | 映射到... |
CHAR, VARCHAR, and LONG VARCHAR | TSD 脚本 STRINGS |
INTEGER | TSD 脚本 INTEGERS |
DATE | TSD 脚本 DATES |
TIME | TSD 脚本 TIMES |
SQL 类型,包含小数点(定点表示法或浮点表示法) | TSD 脚本 REALS |
大多数 SQL 执行时不支持 BOOLEAN 类型。但是,如果您使用 Oracle 或 IBM 的 DB2/2, 可以在列注释中用 $ASETYPE=BOOLEAN 来生成一个带有引入语句的 Boolean 字段。对于 SQLServer, 开发工具包建立一个用户定义的 BOOLEAN 类型。
开发工具包建立类型表,连接到 Informix 的 SYSCOLUMNS 视图。
SQLSelectInto 是一种从表或视图中检索唯一行的简单方法。SQLSelectInto 可以用于查找一行,但不能搜索整个表。
TSD 脚本提供了光标变量,用来检索循环中的数据库多项。光标跟踪检索表中的当前位置。
检索多行的基本进程如下:
为了允许检测多行,TSD 脚本提供三条语句:
TSD 6.0 开发工具包脚本语言参考提供了有关上述语句的附加信息。
这些语句用在下面的例子中:
VARIABLES cursor: SQLCursor; r: EmployeeRec; ACTIONS IF SQLSelect(cursor,'SELECT * FROM EMPLOYEE WHERE SALARY>>0000.00') > 0 THEN WHILE SQLFetch(cursor,r) > 0 DO ProcessEmployee(r); END; SQLCloseCursor(cursor); END;
这个例子完成下列操作:
SQLCloseCursor 操作是很重要的。当打开 SQL 光标时,大多数 SQL 执行结果锁定所有结果表中的行。这些行直到处理了 SQLCloseCursor 语句才解锁。
注: 一般说来,不会想要在 SQLSelect 和 SQLCloseCursor语句之间执行任何用户界面的操作。此外,TSD 脚本提供了有限数目的可同时打开的光标。如果您忘记关闭光标,最终会超过这个限度。
SQL 允许在一个数据库中有多个同样名称的表。为了区分这些表,SQL 在表定义中存储了表的建立者/所有者。建立者/所有者的名称就被用作表的限定符。在数据库中,必须组合使用限定符和表名称来唯一标识一个表。例如:
mary.address
如果没有指定,则大多数 SQL 数据库管理员使用当前用户的标识作为限定符。如果您没有以建立数据库的用户身份注册,而且没有提供限定符,一些 SQL 执行时将以数据库所有者作为限定符来继续搜索数据库。在其它的 SQL 执行中 (例如 DB2/2), 则每次都要求您提供限定符。
只要您在连接字符串中提供了 QUAL= 项,TSD 脚本就会执行相应的限定符替代。
除了下列语句外,TSD 脚本在所有的语句中都可执行替代,:
SQLPrepare SQLExecuteImmediate
如果使用包含相关名的复杂查询,替代可能不会正确执行。为了处理这种情况,TSD 脚本提供了一种伪限定符 $QUAL, 用来替代当前的限定符(即使用连接字符串的 QUAL= 项定义)。
例如,为了避免对限定符进行硬编码,下列代码段使用 $QUAL 作为限定符:
ret := SQLExecuteImmediate('DROP TABLE $QUAL.ADDRESS');
在 TSD 脚本中,所有的字符串都用单引号(或撇号)引起来。在 SQL 中,字符、日期和时间文字都必须用单引号引起来。例如,要发出如下 SQL 查询,
SELECT * FROM EMPLOYEE WHERE LAST_NAME='BROWN'
您需要使用下列 TSD 脚本语句:
SQLSelectInto('SELECT * FROM EMPLOYEE WHERE LAST_NAME=''BROWN''',r);
参照前面的例子 SQLSelectInto,可以看到"BROWN"中的"B"左边的两个单引号的结果是一个单引号。在"BROWN"中的" N"后有两个单引号。结果是在这个位置插入一个单引号。在"BROWN"中的"N"之后的第三个单引号是用来终止字符串的。
为了在 TSD 脚本字符串文字中包括一个单引号,必须使用两个单引号。例如,为了把字符串"Tom's Place"分配给 TSD 脚本变量,必须使用下列语句:
s := 'Tom''s Place';
当连接带引号的字符串时,就更为复杂。在本例中,您想要查找的姓储存在一个叫做 last_name 的字符串变量中:
last_name := 'BROWN';
SQLSelectInto('SELECT * FROM EMPLOYEE WHERE LAST_NAME=''' & last_name & '''',r);
当您建立一个使用 TSD 脚本的连接运算符(&)的选择字符串时,要注意把单引号放在您想查询的文字值的两边。生成的效果如下所示:
SELECT * FROM EMPLOYEE WHERE LAST_NAME = 'BROWN'
在姓的左边的单引号由"LAST_NAME=''之后的三个单引号完成。
前两个用于插入所需的单引号,第三个用于终止第一个字符串常量'SELECT * ... ='。
第二个必需的单引号通过将文字 '''' 连接到整个表达式的末尾来完成。
当被搜索的值是字符串、日期或时间时,您建立搜索表达式时,在包含搜索值的变量之前需要包含三个引号,之后需要四个引号。
当搜索其它类型的数据时,不需要引号,如下所示:
SQLSelectInto('SELECT * FROM EMPLOYEE WHERE AGE=30',r);
在 30 周围不需要引号,因为它是整数值。如果 30 包含在另一个整数变量中,语句就可能是这样:
age := 30; SQLSelectInto('SELECT * FROM EMPLOYEE WHERE AGE=' & age,r);
SQLFormat 语句是一种 TSD 脚本语言,用于 SQL 处理。SQLFormat 把数据值转换为字符串,与您正在使用的 DBMS 数据格式相匹配。
SQLFormat 语句对于数据和时间变量十分有用,它使用下列语法:
SQLFormat(value: SIMPLE EXPRESSION): STRING;
注: 您的 DBMS 可能会要求与开发工具包缺省显示格式不同的日期与时间格式。例如,缺省的 Oracle 日期格式是 DD-MON-YY,而开发工具包的缺省日期格式是 MM/DD/YYYY。
自变量 SQLFormat 取的值必须是简单类型,例如 DATE 或 STRING。SQLFormat 返回格式化的字符串,而不是返回代码,来表示操作是否成功。如果传送的值是 $Unknown,则返回字符串 'NULL'。字符串的特定格式由字符串类型来决定:
一个使用 SQLFormat 的知识库实例如下所示:
KNOWLEDGEBASE Example;
--PUBLIC ROUTINES FUNCTION GetCount(VAL d: DATE): INTEGER;
PRIVATE ROUTINES FUNCTION GetCount(VAL d: DATE): INTEGER IS VARIABLES retCd : INTEGER; cmd : STRING; $SQLColumn_1 : INTEGER; ACTIONS cmd := 'SELECT COUNT(*) FROM COMPANIES WHERE name = ' & SQLFormat('Joe''s place') & ' AND founded_date='& SQLFormat(d); retCd := SQLSelectInto(cmd, $SQLColumn_1); IF retCd < 0 THEN Exit( retCd ); ELSE Exit( $SQLColumn_1 ); END; END;
如果要获得关于在格式化数据中使用的 TSD 脚本语句的详细信息,请参阅 TSD 6.0 开发工具包脚本语言参考。
TSD 脚本使用与变量名和列名等同的名称来使数据检索简单化。但有些时候,基于名称的映射也不方便或是不能使用。
请看下列的 SQL 查询,可以被用来确定 EMPLOYEE 表中的职员数目(行数):
SELECT COUNT(*) FROM EMPLOYEE
TSD 脚本提供了一种简单的方法从 SQL 的列中检索数据,不需要依赖匹配名。您要做的只是说明一个名为 $SQLCOLUMN_n 的变量,其中,"n"是您想检索的列的编号。这一变量的类型必须与被检索的列的类型对应。
为了检索 EMPLOYEE 表中职员的数目(行数),应使用如下编码:
VARIABLES $SQLCOLUMN_1: INTEGER; ACTIONS SQLSelectInto('SELECT COUNT(*) FROM EMPLOYEE',$SQLCOLUMN_1);
$SQLCOLUMN_1 是常规变量。您可以对它进行赋值、测试,把它用在表达式中,等等。
本节介绍了如何把数据插入到 SQL 数据库中,如何从 SQL 数据库中删除数据,以及如何在 SQL 数据库中更改数据。
SQLInsert 语句可以用来把新的行插入到表中。您要做的只是提供一个表名,其后跟包含所要插入的数据的变量。类似检索功能,TSD 脚本在 TSD 脚本变量名和 SQL 列名之间建立映射。
在下面的例子中,值被分配到 EmployeeRecord 类型的变量的各个字段中。为了把此信息插入到 EMPLOYEE 表中,应使用目标表和变量来调用 SQLInsert。
VARIABLE r: EmployeeRecord; ACTIONS r.employee_ID:'312-34-3444'; r.last_name:'Brown' r.first_name:'Robert' r.birth_date:'08/05/1964'; r.salary: $Unknown SQLInsert('EMPLOYEE',r);
SQLInsert, 象 SQLSelectInto 和 SQLSelect 一样, 在自变量中取一个变量数值。第一个自变量是表的名称。其余的自变量代表要放到新行中的每一列的值。您可以传送单个记录或几个简单变量,如下面的例子所示。
VARIABLE employee_ID: STRING; last_name: STRING; first_name: STRING; birth_date: DATE; salary: REAL; ACTIONS employee_ID:'312-34-3444'; last_name:'Brown'; first_name:'Robert'; birth_date:'08/05/1964'; salary:$Unknown; SQLInsert('EMPLOYEE',employee_ID,last_name, first_name,birth_date,salary);
SQLDelete 可以用来从表中删除一行或多行。它使用两个自变量:表名称和一个 SQL WHERE 从句,用于指定要删除的行。从句中的字 WHERE 是任选项。
下例删除所有姓为"Smith."的职员。
SQLDelete('EMPLOYEE','LAST_NAME=''Smith''');
下例删除具有指定职员标识号的职员。
SQLDelete('EMPLOYEE','EMPLOYEE_ID=''123-45- 6789''');
SQLUpdate 可以被用来改变储存在表的当前行中的信息。该语句使用下面的自变量:
例如,假设您的公司有一位标识为 345-67-8901 的职员得到了 10% 的加薪。您可以通过更新数据库来反映这一事件,如下例所示:
VARIABLES salary: REAL; ACTIONS IF SQLSelectInto('SELECT SALARY FROM EMPLOYEE WHERE EMPLOYEE_ID= ''345-67-8901''', salary) > 0 THEN salary := salary * 1.10; SQLUpdate('EMPLOYEE','EMPLOYEE_ID=' '345-67- 8901''',salary); END;
在下一个例子中,公司决定给每个人 10% 的加薪。这个例子演示了如何按这一信息来更新数据库:
VARIABLES salary: REAL; cursor: SQLCursor; ACTIONS IF SQLSelect(cursor, 'SELECT SALARY FROM EMPLOYEE ') > 0 THEN WHILE SQLFetch(cursor,salary) > 0 DO salary := salary * 1.10; SQLUpdateCurrent(cursor,salary); END; SQLCloseCursor(cursor); END;
SQLUpdateCurrent 是 SQLUpdate 的特殊版本,可以用来更新光标最新获取的行。它使用光标变量和待更新的值作为自变量。
最后,SQLUpdate (和 SQLUpdateCurrent) 可以获得一个记录而不是一系列的简单变量。下面的例子假定:
VARIABLE r: EmployeeRecord; ACTIONS r.employee_ID: = '312-34-3444'; IF SQLSelectInto('SELECT * FROM EMPLOYEE WHERE EMPLOYEE_ID=''' & r.employee_ID & '''',r) > 0 THEN EditEmployee(r); SQLUpdate('EMPLOYEE', 'EMPLOYEE_ID=''' & r.employee_ID & '''',r); END;
概括地说,在本例中执行了下列步骤:
您可能已经注意到例子中的记录没有被锁定,也没有采取任何措施来防止多个用户访问同一个记录。
开发工具包的被动一致特性可对多用户应用程序中面临的典型一致性问题进行处理,现总结如下:
出错消息有一个负的错误代码。在某些情况下,您可能希望测试这个出错代码,然后按照它的值来执行不同的操作。
例如,如果一节代码取决于插入操作是否成功,您应当围绕这个代码来测试从 SQLInsert 来的正返回值。
开发工具包给第二个要进行删除的用户生成出错消息。
注:为了实现被动一致,应检查是否包括附加给 SQLUpdate 的自变量。
请看下面的例子:
VARIABLE r, oldR: EmployeeRecord; ACTIONS r.employee_ID: = '312-34-3444'; IF SQLSelectInto('SELECT * FROM EMPLOYEE WHERE EMPLOYEE_ID=''' & r.employee_ID & '''', r, oldR) > 0 THEN EditEmployee(r); SQLUpdate('EMPLOYEE', 'EMPLOYEE_ID=''' & r.employee_ID & '''',r,oldR); END;
在本例中,发生了下列事件:
当 TSD 脚本开始处理 SQLUpdate 语句时,它检测到附加的记录变量 oldR。这就使它:
如果检测到任何不同,则表示有其他人改变了那一行,更新被取消。 SQLUpdate 同时在屏幕上返回一个负的出错代码,显示一个用户级别的出错信息。此外,该行的当前值被放入 oldR中(这样该用户就不必检索该记录的当前版本)。
被动一致代表了一种简单而有效的方法来确保多用户环境下的正常操作。它会有一点额外的系统开销,因为要第二次检索待更新的行。但是,比起它提供的额外保护来说这是很值得的。
注:一般说来,应当总使用 SQLUpdate 的被动一致形式,除非您确信没有丢失更新的可能性。
大多数 SQL 数据库引擎都提供一种功能,允许您把 SQL 操作组成工作单元。然后您可以使用这一内置的工具来确保每个工作单元整体成功或者整体失败。
在通常的处理中,所有的变更都在一发生时就提交给数据库。在面向事务的处理中,数据变更的处理方式不同。
SQLBeginWork 语句指明了工作单元的开始。 SQLBeginWork 向 SQL 数据库引擎说明,它不会调用 SQLCommit 或 SQLRollback,除非您通知它这样做。
SQLCommit 语句说明,从最后一次 SQLBeginWork 以来对数据库进行的所有更改都应当应用到数据库中,并且永久应用。
SQLRollback 操作在对工作单元进行的处理之一失败时发生。SQLRollback 语句说明,从最后一次 SQLBeginWork 以来对数据库进行的所有更改都应当被撤消。
例如,在一个银行应用程序中,可能要从客户的储蓄帐户中转帐到支票帐户中去。这种转帐可能涉及一个表(SAVINGS_ACCOUNTS)中的节余列的减少和另一个表(CHECKING_ACCOUNTS)节余的增加。如果一个操作成功而另一个失败,就会出现问题。为了防止这种可能性,面向事务的应用程序就执行下面的操作:
随后的代码段说明了先前所讨论的原理。如所说明的,这是一种在承诺数据库更改之前几个 SQL 操作都成功的情况。具体地说,如果第二个更新失败,您就不会在第一个更新之后再承诺,因为客户的储蓄节余减少了,而在支票节余中没有相应的补偿。
PROCEDURE SavingsToCheckingTransfer( VAL savings_ID: STRING, VAL checking_ID: STRING, VAL amount: REAL) IS VARIABLES debited, credited: BOOLEAN; balance: REAL; ACTIONS SQLBeginWork; debited := FALSE; IF SQLSelectInto('SELECT BALANCE FROM SAVINGS WHERE ACCOUNT_ID=''' & savings_ID & '''' ,balance) > 0 THEN balance := balance - amount; IF SQLUpdate('SAVINGS', 'ACCOUNT_ID=''' & savings_ID & '''', balance) > 0 THEN debited := TRUE; END; END; IF debited THEN credited := FALSE; IF SQLSelectInto('SELECT BALANCE FROM CHECKING WHERE ACCOUNT_ID=''' & checking_ID & '''',balance) > 0 THEN balance := balance + amount; IF SQLUpdate('CHECKING', 'ACCOUNT_ID=''' & checking_ID & '''' , balance) > 0 THEN credited := TRUE; END; END; IF credited THEN SQLCommit; ELSE SQLRollback; END; END; END;
当您定义一个数据库表时,您可以指定每列是否可以有空值。说明某个列可以有空值意味着该列不是必须赋值。(这与有一个空的字符串值不同。)
注: SQL 的 NULL 能很好地映射到 TSD 脚本支持的未知值。在 TSD 脚本中,每个变量以值 $Unknown 开始。这种情况的例外是记录变量:记录变量的所有字段都以值 $Unknown 开始。
当各种值在 TSD 脚本和 SQL 之间交换时,保留在 $Unknown 和 NULL 之间的映射。例如,考虑以下例子:
SQLInsert('EMPLOYEE',r);
在这个例子中,如果 r.salary 是 $Unknown, 那么用于插入行的 SALARY 列将包含一个 NULL 值。
相同地,如果对于给定的行来说,PHONE 列 NULL,那么使用 SQLSelectInto 或 SQLSelect/SQLFetch 对记录变量中的行进行检索,结果是使该变量的 PHONE 字段为 $Unknown。
在系统的开发工具包中,也支持空值。如果用户把对话框字段留为空白,相应的记录字段就设置为 $Unknown。
注:如果您试图把 $Unknown 值插入到还没有说明过可以接受空值的列中,将会收到出错消息。这种情况仅在窗体字段被留为空白时发生。
在商业应用程序中,写有大量的代码来处理出错情况。代码一旦写出,就必须经过测试。这通常意味着对每条语句返回的代码做出评价。
开发工具包有一种自动错误处理的机制,来减少错误处理的负担。这意味着:
按照缺省值,开发工具包检测不成功的 SQL 操作并为最终用户显示出错信息。
开发工具包尽可能多地显示上下文信息,来识别导致错误的原因 (复制列错误、输入/输出错误、磁盘已满,等等)。如果某操作的成功或失败没有意义,则不必检查返回值。在本例中,向 EMPLOYEE 表中插入了一个新的职员:
SQLInsert('EMPLOYEE',r);
可能有时您常常想暂时禁用自动报错功能。$ErrorFilter 是一种系统整体功能,允许您指定一个错误级别的阈值。级别低于此指定阈值的错误不会产生出错信息。
下列的严重性级别会产生自动错误代码信息:
错误严重性代码 | 说明 |
0 | 致命错误 |
1 | 非致命错误 |
2 | 警告消息 |
3 | 信息性消息 |
例如,设定 $ErrorFilter(3)表示仅在严重性级别为 3 或低于 3 的错误和警告才生成自动信息。更高严重性代码级别的错误不显示出错信息,仅通过分析语句返回代码才能分辨出来。
随着严重性的增加,严重性代码级别下降。所以,可以调用 ErrorFilter(0)来禁用除了致命错误之外的所有出错信息。
如果您想在一个循环中多次执行一个 SQL 命令,更为有效的方法是在循环外准备(预先编辑)一次该命令,然后在循环中执行准备好的格式。
开发工具包允许您准备除了 Select 语句和不能动态准备的命令之外的大多数 SQL 命令。参阅您的 SQL 文档来获得关于不能被动态准备的具体命令。
开发工具包提供了两条准备 SQL 命令的命令:
注: SQLPrepare 和 SQLExecute 仅由 DB2/2、DB2/6000 和 Oracle 支持。
使用 SQLPrepare 和 SQLExecute 就象您使用 SQLExecuteImmediate 一样,不同之处是要把执行推迟到您需要的时候。下面的例子演示了怎样使用 SQLPrepare/SQLExecute 组合把新用户列表插入到 USER 表中:
FUNCTION InsertNewUsers(REF users: LIST OF STRING):INTEGER IS VARIABLES stmt: SQLStatement; retCd: INTEGER; ACTIONS (* insert the user names passed in *) retCd := SQLPrepare(stmt,'INSERT INTO USERS VALUES (?)'); IF retCd <= 0 THEN EXIT retCd; END;
(* perform the insertions *) FOR users DO retCd := SQLExecute(stmt,users[$current]); IF retCd <= 0 THEN SQLRollback; (* reverse changes and release locks *) EXIT retCd; END; END; (* for *)
(* release the resources used by this Prepared *) SQLCloseStatement(stmt); END;
注:因为 TSD 脚本对同时使用的准备语句的资源有限,故您需要确保调用 SQLCloseStatement,来关闭准备语句。
参数标志符替换对于使用准备语句的进程是不可分割的部分。参数标志符,用问号(?)表示,是其后要替换的值的占位符。
对每个值,把一个问号插入到语句字符串中(用于 SQLPrepare)。这些值的指导方针是:
注:当参数标志符确实具有正常参数值时,TSD 脚本不能提供象常规参数(用在 SQLInsert、SQLUpdate、SQLSelectInto 和 SQLFetch)中那样强有力的类型转换。
注:这些语句仅应用于 Sybase 和 Microsoft SQLServer。
一些 DBMS 每次连接时仅支持一个活动的 SQL 语句。例如,把 SQLSelectInto 语句放到 SQLFetch 循环主体中去要求两个同时使用 SQLServe 的 SQL 语句。开发工具包用克隆连接的方法来对此做出补偿。克隆或"备用"连接有自己的事务处理空间。
开发工具包的事务处理模型使所有的连接像属于同一事务处理那样工作。尽管这样有些不符常规(不使用两相确认协议),但它实际工作起来很可靠。现有的要求是,推迟暗含的"自动" 承诺,直到由于打开的光标和准备语句在承诺或回退而关闭时,才关闭外部光标。
在开发工具包的早期版本中,所有嵌套的或 SQL 的同时操作都在事务处理的内部完成。在那些版本中,在最外层的光标打开前调用 SQLBeginWork,并且在最外层光标关闭时必须承诺(或回退)。
如果您想让语句在它们自己的事务处理空间里执行,您必须用 SQLCommand 为语句打开附加的主连接。
开发工具包 6.0 不再要求在"手动"承诺模式下为同时执行语句进行事务处理包装。但是,在使用"自动"承诺模式下,如果 DBMS 不允许光标跨越事务处理,事务处理包装还是需要的。
由于 SQL 嵌套语句的复杂性和副作用(例如连接的额外开销、处理来自不同的执行空间中的两个连接的同一个表时的死锁可能性,等等),我们郑重建议尽可能避免使用它。
把 SQL 操作分成两部分,几乎总是可能的:
在避免嵌套语句的许多副作用的同时,这样做还有提高一致性的好处,因为您不必被迫在事务处理内部来执行操作。但是根据应用程序的不同,您可能还是需要对第二部分进行事务处理。
Tivoli 服务台 6.0 开发工具包脚本程序设计指南