现在的位置: 首页 > 数据库 > DB2 > 正文

DB2操作

2016年05月27日 DB2 ⁄ 共 80591字 ⁄ 字号 暂无评论

第一章DB2开发基础

在进行DB2应用开发之前,了解DB2应用程序的结构,掌握相关概念,设置开发环境是很必要的。本章主要介绍这几个方面的内容。

1.1 DB2应用程序开发概述

1.1.1 程序结构

DB2应用程序包括以下几个部分:

1.声明和初始化变量

2.连接到数据库

3.执行一个或者多个事务

4.与数据库断开连接

5.结束程序

一个事务是一组数据库操作,在提交给数据库之前,必须确认完全成功执行。在嵌入式SQL应用程序中,当应用程序成功地连接到一个数据库时,一个事务就自动开始了,结束于执行一条COMMIT语句或者ROLLBACK语句。同时,下一条SQL语句开始一个新的事务。

每一个应用程序的开始必须包括:

l 数据库管理器用来与宿主程序交互的所有变量和数据结构的声明

l 设置SQL通信区(SQLCA),提供错误处理的SQL语句

注意:用Java写的DB2应用程序在SQL语句出错时抛出一个SQLException异常,需要在catch块里处理,而不是使用SQLCA。

每个应用程序的主体包括访问和管理数据的SQL语句。这些语句组成事务,事务必须包括下列语句:

l CONNECT语句,其建立一个与数据库服务器的连接

l 一条或多条:

▲数据操纵语句(例如,SELECT语句)

▲数据定义语句(例如,CREATE语句)

▲数据控制语句(例如,GRANT语句)

l COMMIT或者ROLLBACK语句结束事务

应用程序的结束通常包括释放程序与数据库服务器的连接和释放其他资源的SQL语句。

1.1.2 开发方法选择

可使用几种不同的程序设计接口来存取 DB2 数据库。您可以:

l 将静态和动态 SQL 语句嵌入应用程序。

l 在应用程序中编写“DB2 调用层接口”(DB2 CLI) 的函数调用,以调用动态 SQL 语句。

l 开发调用“Java 数据库链接”应用程序设计接口 (JDBC API) 的 Java 应用程序和小程序。

l 开发符合“数据存取对象 (DAO) ”和“远程数据对象 (RDO) ” 规范的 Microsoft Visual Basic 和 Visual C++ 应用程序,以及使用“对象链接和嵌入数据库 (OLE DB) 桥接”的“ActiveX 数据对象”(ADO) 应用程序。

l 使用 IBM 或第三方工具如 Net.Data、Excel、Perl、“开放式数据库链接”(ODBC) 最终用户工具如 Lotus Approach 及其程序设计语言 LotusScript 来开发应用程序。

l 要执行备份和复原数据库等管理功能,应用程序可以使用 DB2 API。

应用程序存取 DB2 数据库的方式将取决于想要开发的应用程序类型。例如,如果想开发数据输入应用程序,可以选择将静态 SQL 语句嵌入应用程序。如果想开发在万维网 (WWW) 上执行查询的应用程序,可能要选择 Net.Data、Perl 或 Java。

1.2相关概念

1.2.1 嵌入式SQL编程

嵌入式SQL应用程序就是将SQL语句嵌入某个宿主语言中,SQL语句提供数据库接口,宿主语言提供应用程序的其他执行功能。

“结构化查询语言”(SQL) 是一种数据库接口语言,它用来存取并处理 DB2 数据库中的数据。可以将 SQL 语句嵌入应用程序,使应用程序能执行 SQL 支持的任何任务,如检索或存储数据。通过使用 DB2,可以用 C/C++、COBOL、FORTRAN、Java (SQLJ) 以及 REXX 程序设计语言来编写嵌入式 SQL 应用程序。

嵌入了 SQL 语句的应用程序称为主程序。用于创建主程序的程序设计语言称为宿主语言。用这种方式定义程序和语言,是因为它们包含了 SQL 语句。

对于静态 SQL 语句,您在编译前就知道 SQL 语句类型以及表名和列名。唯一未知的是语句正搜索或更新的特定数据值。可以用宿主语言变量表示那些值。在运行应用程序之前,要预编译、编译和捆绑静态 SQL 语句。静态 SQL 最好在变动不大的数据库上运行。否则,这些语句很快会过时。

相反,动态 SQL 语句是应用程序在运行期构建并执行的那些语句。一个提示最终用户输入 SQL 语句的关键部分(如要搜索的表和列的名称)的交互式应用程序是动态 SQL 一个很好的示例。 应用程序在运行时构建 SQL 语句,然后提交这些语句进行处理。

可以编写只有静态 SQL 语句或只有动态 SQL 语句,或者兼有两者的应用程序。

一般来说,静态 SQL 语句最适合用于带有预定义事务的高性能应用程序。预订系统是这种应用程序一个很好的示例。

一般来说,动态 SQL 语句最适合于必须在运行期指定事务的、要快速更改数据库的应用程序。交互式查询界面是这种应用程序一个很好的示例。

将 SQL 语句嵌入应用程序时,必须按以下步骤预编译应用程序并将其与数据库捆绑:

1. 创建源文件,以包含带嵌入式 SQL 语句的程序。

2. 连接数据库,然后预编译每个源文件。

预编译程序将每个源文件中的 SQL 语句转换成对数据库管理程序的 DB2 运行期 API 调用。预编译程序还在数据库中生成一个存取程序包,并可选择生成一个捆绑文件(如果您指定要创建一个的话)。

存取程序包包含由 DB2 优化器为应用程序中的静态 SQL 语句选择的存取方案。这些存取方案包含数据库管理程序执行静态 SQL 语句所需的信息,以便该管理程序可以用优化器确定的最有效的方式来执行这些语句。对于动态 SQL 语句,优化器在您运行应用程序时创建存取方案。

捆绑文件包含创建存取程序包所需要的 SQL 语句和其他数据。可以使用捆绑文件在以后重新捆绑应用程序,而不必首先预编译应用程序。重新捆绑创建针对当前数据库状态的优化存取方案。如果应用程序将存取与预编译时所用数据库不同的数据库,则必须重新捆绑应用程序。如果数据库统计信息自上次捆绑后已经更改,建议您重新捆绑应用程序。

3. 使用主语言编译程序编译修改的源文件(以及其他无 SQL 语句的文件)。

4. 将目标文件与 DB2 和主语言库连接,以生成一个可执行程序。

5. 如果在预编译时未对捆绑文件进行捆绑;或者准备存取不同数据库,则应对捆绑文件进行捆绑以创建存取程序包。

6. 运行该应用程序。此应用程序使用程序包中的存取方案存取数据库。

1.2.2 预编译

创建源文件之后,必须对每一个含有SQL语句的宿主语言文件用PREP命令进行预编译。预编译器将源文件中的SQL语句注释掉,对那些语句生成DB2运行时API调用。

在预编译一个应用之前,必须连接到一个数据库服务器,不论是自动连接还是显性连接。即使你在客户端工作站上预编译应用程序、预编译器在客户端产生的修改后源文件和信息,预编译器也需要使用服务器连接来执行一些确认任务。

预编译器也创建数据库管理器在处理针对某个数据库的SQL语句时需要的信息。这些信息存储在一个程序包或者一个捆绑文件或者两者之中,视预编译器的选项而定。

下面是使用预编译器的一个典型例子。预编译一个名叫filename.sqc的C嵌入式SQL源文件,发出下面的命令创建一个C源文件,默认名字为filename.c,和一个捆绑文件,默认名字为filename.bnd:

DB2 PREP filename.sqc BINDFILE

预编译器最多产生四种类型的输出:

l 修改后的源文件

l 程序包

l 捆绑文件

l 信息文件

1、修改后的源文件

这个文件是预编译器将SQL语句转化为DB2运行时API调用后,原始源文件的新版本。它被赋予了相应宿主语言的扩展名。

2、程序包

如果使用了PACKAGE选项(默认的),或者没有指定任何BINDFILE、SYNTAX、SQLFLAG选项,程序包存储在所连接到的数据库中。程序包仅仅包含执行访问本数据的SQL语句时需要的所有信息。除非你用PACKAGE USING选项指定一个不同的名字,否则预编译器将使用源文件名字的前8个字符作为程序包名。

使用PACKAGE选项时,在预编译处理过程中使用的数据库必须拥有源文件中静态SQL语句参考到的所有数据库对象。例如不能够预编译一条SELECT语句,如果参考的表在数据库中不存在。

3、捆绑文件

如果使用了BINDFILE选项,预编译器将创建一个捆绑文件(扩展名为.bnd),它包含创建程序包的一些数据。这个文件可以在后面用BIND命令将应用捆绑到一个或多个数据库。如果指定了BINDFILE选项,没有指定PACKAGE选项,捆绑被延缓直到执行BIND命令。注意,对于命令行处理器(CLP),PREP默认不指定BINDFILE选项。因此,如果你使用CLP,又想延缓捆绑,那么你必须指定BINDFILE选项。

如果在预编译时请求一个捆绑文件但是没有指定PACKAGE选项,不会在数据库中创建程序包;对象不存在和没有权限的SQLCODE被看作警告而不会被看作错误。这使得你能够预编译程序和创建一个捆绑文件,不需要参考到的对象必须存在,也不需要你拥有执行正被预编译的SQL语句的权限。

4、信息文件(Message File)

如果使用了MESSAGES选项,预编译器将信息重定向到指定的文件中。这些信息包括警告和错误信息,它们描述了在预编译过程中产生的问题。如果源文件没有预编译成功,使用警告和错误信息来断定问题,改正源文件,然后再预编译。如果没有使用MESSAGE选项,预编译信息被写到标准输出上。

1.2.3 程序包

程序包就是存储在相对应数据库中的包含数据库系统在捆绑时对特定SQL语句所产生的访问策略。

所有SQL语句经过编译优化后就产生可以直接对数据库进行访问的访问策略,存储于相应的数据库中。这些访问策略可以在应用程序调用相对应的SQL语句时得到访问。程序包对应于特定的应用程序,但是并不是与应用程序一起存放,而是同相对应的数据库一起存放。

1.2.4 捆绑

捆绑(bind)是创建数据库管理器在应用执行时为了访问数据库而需要的程序包的过程。捆绑可以在预编译时指定PACKAGE选项隐含地完成,或者使用BIND命令依据预编译过程中产生的捆绑文件显性地完成。

下面是使用BIND命令的一个典型例子。将名为filename.bnd的捆绑文件捆绑到数据库,使用下面的命令:

DB2 BIND filename.bnd

每一个独立预编译的源代码模块都需要创建一个程序包。如果一个应用有5个源文件,其中3个需要预编译,那么要创建3个程序包或者3个捆绑文件。默认上,每一个程序包的名字与产生.bnd文件的源文件名字相同,但是只要前8个字符。如果新建的程序包名字与已存在于数据库中的程序包名相同,新的程序包将替换原先存在的程序包。要显性地指定一个不同的程序包名,必须在PREP命令使用PACKAGE USING选项。

1.2.5 工作单元

一个工作单元是一个单一的逻辑事务。它包含一个SQL语句序列,在这个序列中的所有操作,被看作一个整体,要么都成功,要么都失败。DB2支持两种类型的连接(connection)。连接类型决定一个应用程序如何与远程数据库工作,并且决定该应用程序同时能与多少个数据库工作。

(1)连接类型1

应用程序在每个工作单元中只能连接单个数据库,此时,这个工作单元称为远程工作单元(RUOW, Remote Unit of Work)

(2)连接类型2

允许应用程序在每个工作单元中连接多个数据库,此时,这个工作单元称为分布式工作单元(DUOW, Distributed Unit of Work)

我们来看下面的例子:

(1)Remote Unit of Work – Type 1 Connect

 
  clip_image001

在这个例子中,连接类型为1,工作单元为远程工作单元(RUOW),应用程序在连接到数据库DB2_2之前,必须结束当前的工作单元(即事务,通过执行COMMIT语句)。

(2)Distributed Unit of Work – Type 2 Connect

 
  clip_image002

在这个例子中,连接类型为2,工作单元为分布式工作单元(DUOW)。应用程序在连接到数据库DB2_2之前,不需要结束当前的事务。在一个工作单元中,可以有多个数据库连接,但是只有一个处于激活状态,其它都处于睡眠状态。用SET CONNECTION TO db_name语句切换数据库连接。

1.2.6 应用程序、捆绑文件与程序包之间的关系

一个程序包是一个存储在数据库中的对象,它包含在执行某个源文件中特定SQL语句时所需的信息。数据库应用程序对用来创建应用程序的每一个预编译源文件使用一个程序包。每一个程序包是一个独立的实体,同一个或不同应用程序所使用的程序包之间没有任何关系。程序包在对源文件执行附带绑定的预编译时创建,或者通过绑定绑定文件创建。

数据库应用程序使用程序包的原因:提高性能和紧凑性。通过预编译SQL语句,使得SQL语句在创建应用程序时被编译进程序包,而不是在运行时。每一条语句都被分析,高效率的操作数串存储在程序包中。在运行时,预编译器产生的代码调用数据库管理器运行时服务APIs,根据输入输出数据的变量信息,执行程序包。

预编译的优点仅仅对静态SQL语句有效。动态执行的SQL语句不用预编译,但是它们在需要在运行时完成处理的整个步骤。注意:不要认为一条SQL语句的静态版本的执行效率一定比其动态版本的高。在某个方面,静态SQL语句快是因为不需要动态语句的准备开销。在另一方面,同样的语句,动态执行会快些,是因为优化器能利用当前数据库的统计信息,而不是以前的统计信息 。有关静态SQL与动态SQL的比较,参照下表:

表 静态SQL与动态SQL的比较

考虑因素 最好的选择

执行SQL语句的时间:

少于两秒 静态

2 到10 秒 两者均可

多于10秒 动态

数据一致性

统一的数据分布 静态

轻微不统一分布 两者均可

高度不统一分布 动态

范围谓词(<,>,BETWEEN,LIKE)使用

很少使用 静态

偶然使用 两者均可

经常使用 动态

执行的重复性

很多次(10或者更多) 两者均可

几次(少于10次) 两者均可

一次 静态

查询的种类

随机 动态

固定 两者均可

运行时环境(DML/DDL)

事务处理(DML Only) 两者均可

混合(DML和DDL – DDL affects packages) 动态

混合(DML和DDL – DDL does not affect packages) 两者均可

运行runstats的频度

很低 静态

正常 两者均可

频繁 动态

1.2.7 定界符

如果源程序中有SQL语句,源程序不能立即被源语言编译器处理。它首先要经过一个翻译过程,将SQL语句翻译成源语言编译器能够理解的东西。做翻译工作的程序就叫做预编译器。预编译器识别SQL语句的方法是通过定界符将SQL语句标识出来。

定界符的作用是使预编译器能够识别出需被翻译的SQL语句,并且必须标识出每一条嵌入的SQL语句。不同的宿主语言使用不同的定界符,下表列出了四种常用语言的定界符:

语言

定界符

C;C++

EXEC SQL

sql statement ;

COBOL

EXEC SQL

sql statement END-EXEC.

FORTRAN

EXEC SQL

sql statement

JAVA

#sqlj {sql statement} ;

例子:

l SQL语句

UPDATE TEMPL

SET WORKDEPT = ‘C02’

WHERE WORKDEPT = ‘C01’

l 在C程序中的SQL语句

EXEC SQL

UPDATE TEMPL

SET WORKDEPT = ‘C02’

WHERE WORKDEPT = ‘C01’ ;

l 在COBOL程序中的SQL语句

EXEC SQL

UPDATE TEMPL

SET WORKDEPT = ‘C02’

WHERE WORKDEPT = ‘C01’

END-EXEC.

l 在Java程序中的SQL语句

#sqlj {UPDATE TEMPL SET WORKDEPT = ‘C02’ WHERE WORKDEPT = ‘C01’} ;

第二章DB2应用程序设计方法

本章介绍DB2应用程序设计的一般方法,以及如何设置测试环境。

2.1编程方法

2.1.1 访问数据

在关系数据库中,必须使用SQL访问请求的数据,但是可以选择如何将SQL结合到应用程序中去。可以从下表列出的接口和它们支持的语言中选择:

接口

支持的语言

嵌入SQL

C/C++, COBOL, FORTRAN, Java (SQLJ), REXX

DB2 CLI 和ODBC

C/C++, Java (JDBC)

Microsoft Specifications, including ADO, RDO, and OLE DB

Visual Basic, Visual C++

Perl DBI

Perl

Query Products

Lotus Approach, IBM Query Management Facility

一、嵌入SQL

嵌入SQL有其优势,它可以包含静态SQL或者动态SQL,或者两种类型混合使用。如果在开发应用程序时,SQL语句的格式和内容已经确定,应该考虑在程序中采用嵌入的静态SQL。利用静态SQL,执行应用程序的人暂时继承将应用程序捆绑到数据库中的用户的权限,而不需要对此人赋予其它权限(除了应用程序的执行权)。动态SQL的执行需要执行应用程序的人的权限,但也有例外情况,就是在捆绑应用程序的时候使用DYNAMICRULES BIND选项。一般来讲,如果直到执行时才能确定SQL语句,那么应该采用嵌入的动态SQL。这比较安全,而且可以处理更多形式的SQL。

注意:JAVA语言的嵌入SQL(SQLJ)应用程序只能嵌入静态SQL语句。然而,在SQLJ应用程序中,可以通过使用JDBC调用动态SQL语句。

在使用编程语言编译器前,必须对源文件进行预编译,将嵌入的SQL语句转换为宿主语言的数据库服务APIs。在应用程序运行之前,必须将程序中的SQL捆绑到数据库里。

我们在第三章“静态SQL应用编程”中有详细介绍。

二、DB2 CLI和ODBC

DB2调用级接口(DB2 CLI)是IBM公司数据库服务器的DB2系列可调用SQL接口,它是一个关系数据库数据访问的C和C++语言编程接口,它用函数调用的方式,将动态SQL语句作为参数传递给数据库管理器。也就是说,一个可调用的SQL接口就是一个调用动态SQL语句的应用程序编程接口(API)。CLI可以替代嵌入动态SQL,但是与嵌入SQL不同,它不需要预编译或者捆绑。

DB2 CLI是基于微软开放数据库连接(ODBC)规范和X/Open规范开发的。IBM选择这些规范是为了遵循业界标准,使熟悉这些数据库接口的应用程序开发人员能在短期内掌握CLI的开发方法。

JDBC:

DB2在Java语言方面的支持包括JDBC,JDBC是一个与厂商无关的动态SQL接口,利用它使得应用程序可以通过标准Java方法调用实现数据的访问。

JDBC与DB2 CLI一样不需要作预编译或者捆绑,作为一个与厂商无关的标准,JDBC应用程序具有良好的移植性。用JDBC开发的应用程序只采用动态SQL。

三、微软规范

开发符合“数据存取对象 (DAO) ”和“远程数据对象 (RDO) ” 规范的 Microsoft Visual Basic 和 Visual C++ 应用程序,以及使用“对象链接和嵌入数据库 (OLE DB) 桥接”的“ActiveX 数据对象 (ADO) ”应用程序。

四、Perl数据库接口

DB2支持Perl数据库接口(DBI)数据访问规范,使用DBD::DB2驱动程序。DBD::DB2驱动程序支持下列平台:

AIX

Operating Systems

Version 4.1.4 and later

C Compilers

IBM C for AIX Version 3.1 and later

HP-UX

Operating Systems

HP-UX Version 10.10 with Patch Levels: PHCO_6134, PHKL_5837,

PHKL_6133, PHKL_6189, PHKL_6273, PHSS_5956

HP-UX Version 10.20

HP-UX Version 11

C Compilers

HP C/HP-UX Version A.10.32

HP C Compiler Version A.11.00.00 (for HP-UX Version 11)

Linux

Operating Systems

Linux Redhat Version 5.1 with kernel 2.0.35 and glibc version 2.0.7

C Compilers

gcc version 2.7.2.3 or later

Solaris

Operating Systems

Solaris Version 2.5.1

Solaris Version 2.6

C Compilers

SPARCompiler C Version 4.2

Windows NT

Operating Systems

Microsoft Windows NT version 4 or later

C Compilers

Microsoft Visual C++ Version 5.0 or later

从DB2通用数据Perl DBI网页(http://www.software.ibm.com/data/db2/perl/)上可以下载最新的DBD::DB2驱动程序的最新版本以及更多信息。

五、查询工具产品

查询工具产品包括IBM查询管理工具(QMF)和Lotus Notes,它们支持查询开发和报表。

2.1.2 数据值控制

应用程序的部分逻辑是通过控制数据库中允许的值得到实施和保护数据完整的。DB2提供了几个不同的方法。

一、数据类型

数据库将每一个数据元素存储在某个表的列中,并且每一列都用一个数据类型来定义,数据类型增加了此列放置数据的限制。例如,整数类型必须是在某个固定范围内的数字。在SQL语句中使用列也必须符合一定的行为,例如,数据库不能将一个整数与一个字符串比较。DB2有一组内置的数据类型,定义了特性和行为。DB2支持用户定义数据类型,叫做UDT,它们是基于内置的数据类型定义的。

二、唯一性约束

唯一性约束防止在一个表中,在一列或多列上出现重复的值。唯一主关键字也是唯一性约束。例如,在表DEPARTMENT的DEPTNO列上定义一个唯一性约束,防止将相同的部门号分配给两个部门。

如果对所有使用同一个表里的数据的应用程序都要执行一个唯一性规则,应当采用唯一性约束。

三、表检查约束

表检查约束(Table Check Constraint)限制在表中某列出现的值的范围。

四、参考完整性约束

通过定义唯一约束和外部关键字,可以定义表与表之间的关系,从而实施某些 商业规则。唯一关键和外部关键字约束的组合通常称为参考完整性约束。 外部关键字所引用的唯一约束称为父关键字。 外部关键字表示特定的父关键字,或与特定的父关键字相关。 例如,某规则可能规定每个雇员(EMPLOYEE 表)必须属于某现存的部门 (DEPARTMENT 表)。因此,将 EMPLOYEE 表中的“部门号”定义为外部关键字,而将 DEPARTMENT 表中的“部门号”定义为主关键字。下列图表提供参考完整性 约束的直观说明。

图 外部约束和主约束定义关系并保护数据

clip_image004

2.1.3 数据关系控制

应用程序逻辑的另外一个主要任务是管理系统中不同实体之间的关系。例如,如果增加一个新的部门,就要创建一个新的帐号。DB2提供了管理数据库中不同实体之间的管理的两种方法:参考完整性约束和触发器。

一、参考完整性约束

我们在前面已经介绍过。

二、触发器

一个触发器定义一组操作,这组操作通过修改指定基表 中数据的操作来激活。

可使用触发器来执行对输入数据的验证;自动生成新插入行的值; 为了交叉引用而读取其他表;为了审查跟踪而写入其他表; 或通过电子邮件信息支持警报。 使用触发器将导致应用程序开发及商业规则的全面实施更快速并且应用程序 和数据的维护更容易。

DB2 通用数据库支持几种类型的触发器。 可定义触发器在 DELETE、INSERT 或 UPDATE 操作之前或之后激活。 每个触发器包括一组称为触发操作的 SQL 语句, 这组语句可包括一个可选的搜索条件。

可进一步定义后触发器以对每一行都执行触发操作, 或对语句执行一次触发操作,而前触发器总是 对每一行都执行触发操作。

在 INSERT、UPDATE 或 DELETE 语句之前使用触发器,以便在执行触发操作之前 检查某些条件,或在将输入值存储在表中之前更改输入值。 使用后触发器,以便在必要时传播值或执行其他任务,如发送信息等,这些任务可能是触发器操作所要求的。

2.1.4 服务器上的应用逻辑

DB2提供了将应用程序程序的部分在数据库服务器上运行的功能,通常是为了提高性能和支持公共功能。主要方法有:存储过程,UDF,触发器。

2.1.5 构造SQL语句的原型

当设计和编写应用程序时,利用数据库管理器的特性和一些工具来构造SQL语句的原型,提高执行性能。可以按照下面的方法优化SQL语句:

1.在预编译一个完整的程序之前,用命令行处理器(CLP)测试其中的SQL(可能不是全部)。

2.用解释设施估算程序中的DELETE, INSERT, UPDATE, 和 SELECT语句的开销和获取访问策略。根据解释设施的输出,改写SQL语句或者增加数据库对象(如索引)或者调节数据库服务器的参数 。

2.2 设置测试环境

在进行DB2应用开发时,需要建立测试环境。一个测试环境,应该包括:

1. 一个测试数据库. 如果应用程序要更新,插入或者删除来自表和视图的数据,那么使用测试数据检查执行情况;如果仅仅从表和视图中提取数据,可以考虑使用生产数据.

2. 测试的输入数据. 用来测试应用程序的测试数据应该是有效的,能体现所有可能输入情况. 也要用无效的数据去测试,看应用程序能否辨别出.

2.2.1 创建测试数据库

可以使用CLP发出Create Database dbname语句创建测试数据库,也可以使用数据库管理器API编写一个程序来创建测试数据库.

2.2.2 创建测试表

先分析应用程序的数据需求,然后使用CREATE TABLE语句创建测试表.

2.2.3 生成测试数据

使用下面任何一种方法将数据插入表中:

l INSERT...VALUES (an SQL statement) 每次可以插入一行或多行数据

l INSERT...SELECT 从一个已存在的表提取数据 (基于一个SELECT条款),并放入INSERT语句标识的表中.

l 用IMPORT和LOAD工具 从定义的数据源插入大量的数据

l 用RESTORE工具从某个数据库的备份,将数据还原到特定的测试数据库

第三章 静态SQL应用编程

静态SQL语句,是指嵌入在宿主语言中的SQL语句在预编译时完全知道。这是相对于动态SQL而言的,动态SQL语句的语法在运行时才知道。注意:解释语言中不支持静态SQL语句,例如REXX只支持动态SQL语句。

一条SQL语句的结构在预编译时完全清楚,才被认为是静态的。例如,语句中涉及到的表(TABLES)和列的名字,在预编译时,必须完全知道,只能在运行时指定的是语句中引用的宿主变量的值。然而,宿主变量的信息,如数据类型,也必须在预编译时确定。

当静态SQL语句被准备时,SQL语句的可执行形式被创建,存储在数据库中的程序包里。SQL语句的可执行形式可以在预编译时创建或者在捆绑时创建。不论哪种情况,SQL语句的准备过程都发生在运行之前。捆绑应用程序的人需要有一定的权限,数据库管理器中的优化器还会根据数据库的统计信息和配置参数对SQL语句进行优化。对静态SQL语句来说,应用程序运行时,不会被优化。

3.1 静态SQL程序的结构和特点

3.1.1 例程

下面先来看一个静态SQL程序的C语言例子。这个例程演示了静态SQL语句的使用,它将表中LASTNAME列等于‘JOHNSON’的记录的FIRSTNME列的值输出,否则打印错误信息。

/******************************************************************************

**

** Source File Name =
static.sqc
1.4

**

** Licensed Materials - Property of IBM

**

*******************************************************************************/

#include

#include

#include

#include "util.h"

#ifdef DB268K

/* Need to include ASLM for 68K applications */

#include

#endif

EXEC SQL INCLUDE SQLCA; /* :rk.1:erk. */

#define CHECKERR(CE_STR) if (check_error
(CE_STR, &sqlca) != 0) return 1;

int main(int argc, char *argv[]) {

EXEC SQL BEGIN DECLARE SECTION; /* :rk.2:erk. */

char firstname[13];

char userid[9];

char passwd[19];

EXEC SQL END DECLARE SECTION;

#ifdef DB268K

/* Before making any API calls for 68K environment,

need to initial the Library Manager */

InitLibraryManager(0,kCurrentZone,kNormalMemory);

atexit(CleanupLibraryManager);

#endif

printf( "Sample C program: STATIC/n"
)
;

if (argc == 1) {

EXEC SQL CONNECT TO sample;

CHECKERR ("CONNECT TO SAMPLE");

}

else if (argc == 3) {

strcpy (userid, argv[1]);

strcpy (passwd, argv[2]);

EXEC SQL CONNECT TO sample USER :userid USING :passwd; /* :rk.3:erk. */

CHECKERR ("CONNECT TO SAMPLE");

}

else {

printf ("/nUSAGE: static [userid passwd]/n/n");

return 1;

} /* endif */

EXEC SQL SELECT FIRSTNME INTO :firstname /* :rk.4:erk. */

FROM employee

WHERE LASTNAME = 'JOHNSON';

CHECKERR ("SELECT statement"); /* :rk.5:erk. */

printf( "First name = %s/n", firstname
)
;

EXEC SQL CONNECT RESET; /* :rk.6:erk. */

CHECKERR ("CONNECT RESET");

return 0;

}

/* end of program :
static.sqc
*/

这个例程中实现了一个选择至多一行(即单行)的查询,这样的查询可以通过一条SELECT INTO语句来执行。SELECT INTO 语句从数据库中的表选择一行数据,然后将这行数据的值赋予语句中指定的宿主变量(下节将要讨论宿主变量)。例如,下面的语句将姓为‘HAAS’的雇员的工资赋予宿主变量empsal:

SELECT SALARY

INTO :empsal

FROM EMPLOYEE

WHERE LASTNAME='HAAS'

一条SELECT INTO语句必须只能返回一行或者零行。如果结果集有多于一行,就会产生一个错误(SQLCODE –811,SQLSTATE 21000)。如果查询的结果集中有多行,就需要游标(CURSOR)来处理这些行。在节3.2.3中介绍如何使用游标。

静态程序是如何工作的呢?

1.包括结构SQLCA。 INCLUDE SQLCA语句定义和声明了SQLCA结构,SQLCA结构中定义了SQLCODE和SQLSTATE域。数据库管理器在执行完每一条SQL语句或者每一个数据库管理器API调用,都要更新SQLCA结构中的SQLCODE域的诊断信息。

2.声明宿主变量。SQL BEGIN DECLARE SECTION和END DECLARE SECTION 语句界定宿主变量的声明。

有些变量在SQL语句中被引用。宿主变量用来将数据传递给数据库管理或者接收数据库管理器返回的数据。在SQL语句中引用宿主变量时,必须在宿主变量前加前缀冒号(:)。详细信息看下节。

3.连接到数据库。应用程序必须先连接到数据库,才能对数据库进行操作。这个程序连接到SAMPLE数据库,请求以共享方式访问。其他应用程序也可以同时以共享访问方式连接数据库

4.提取数据。SELECT INTO语句基于一个查询提取了一行值。这个例子从EMPLOYEE表中,将LASTNAME列的值为JOHNSON的相应行的FISRTNME列的值提取出来,置于宿主变量 firstname中。

5.处理错误。CHECKERR 宏/函数是一个执行错误检查的外部函数。

3.1.2 创建应用程序

创建应用程序的整个过程如图所示:

clip_image005

3.1.3 静态SQL的特点

静态SQL编程比动态SQL编程简单些. 静态SQL语句嵌入宿主语言源文件中,预编译器将SQL语句转换为宿主语言编译器能够处理的数据库运行时服务API调用。

因为在捆绑应用程序时,做捆绑的人需要有一定的授权,因此最终用户不需要执行程序包里的语句的直接权限。例如,一个应用程序可以只允许某个用户更新一个表的部分数据 ,而不用将更新整个表的权利给予这个用户。这个功能通过限制嵌入的静态SQL语句只能更新表中的某些列或者一定范围内的值,只将程序包的执行权限给予这个用户。

静态SQL语句是持久稳固的,动态SQL语句只是被缓存,直到变为无效、因为空间管理原因被清理或者数据库被关闭。如果需要,当被缓存的语句变为无效时,DB2 SQL编译器隐性地重新编译动态SQL语句。

静态SQL语句的主要优点是静态SQL在数据库关闭后仍然存在,而动态SQL语句在数据库关闭后就被清除了。另外,静态SQL在运行时不需要DB2 SQL编译器来编译,相反,动态SQL语句需要在运行时编译(例如,使用PREPARE语句)。因为DB2缓存动态SQL语句,这些语句也不总是需要DB2编译。但是,每一次运行程序至少需要编译一次。

静态SQL有性能上的优势。对简单、运行时间短的SQL程序,静态SQL语句 比相同目的的动态SQL语句执行得快。因为静态SQL语句准备执行形式的开销在预编译时间,而不是在运行时。

注意:静态SQL语句的性能决定于应用程序最后一次被捆绑时数据库的统计信息。 然而,如果这些统计信息改变了,那么比较起来,等效的动态SQL语句的性能可能好些。在某个使用静态SQL的应用程序捆绑之后,数据库增加了一个索引,如果这个应用程序不重新捆绑,就不能利用这个索引。还有,如果在静态SQL语句中使用宿主变量,优化器也不能使用表的分布信息来优化SQL语句。

3.2 宿主变量和指示符变量的应用

3.2.1 宿主变量的声明

宿主变量(Host variables) 在主应用程序中由嵌入式SQL语句引用的变量。宿主变量是该应用程序中的程序设计变量,并且是在数据库中的表与应用程序工作区之间传送数据的主要机制。我们称之为“宿主变量”,是为了与通常方法声明的源语言变量区分开来,通常方法声明的变量不能被SQL语句引用。宿主变量在宿主语言程序模块中以一种特殊的方式声明:必须在BEGIN DECLARE SECTION和END DECLARE SECTION程序节内定义。

下图显示在不同编程语言中声明宿主变量的例子。

语言

例子源码

C/C++

EXEC SQL BEGIN DECLARE SECTION;

short dept=38, age=26;

double salary;

char CH;

char name1[9], NAME2[9];

/* C comment */

short nul_ind;

EXEC SQL END DECLARE SECTION;

Java

// Note that Java host variable declarations follow

// normal Java variable declaration rules, and have

// no equivalent of a DECLARE SECTION

short dept=38, age=26;

double salary;

char CH;

String name1[9], NAME2[9];

/* Java comment */

short nul_ind;

COBOL

EXEC SQL BEGIN DECLARE SECTION END-EXEC.

01 age PIC S9(4) COMP-5 VALUE 26.

01 DEPT PIC S9(9) COMP-5 VALUE 38.

01 salary PIC S9(6)V9(3) COMP-3.

01 CH PIC X(1).

01 name1 PIC X(8).

01 NAME2 PIC X(8).

* COBOL comment

01 nul-ind PIC S9(4) COMP-5.

EXEC SQL END DECLARE SECTION END-EXEC.

下面是引用宿主变量的例子

语言

例子源码

C/C++

EXEC SQL FETCH C1 INTO :cm;

printf( "Commission = %f/n", cm );

Java

#SQL { FETCH :c1 INTO :cm };

System.out.println("Commission = " + cm);

COBOL

EXEC SQL FETCH C1 INTO :cm END-EXEC

DISPLAY 'Commission = ' cm

在SQL语句中引用宿主变量时,必须加前缀—冒号(:)。冒号的作用是将宿主变量与SQL语法中的元素区分开。如果没有冒号,宿主变量会误解释为SQL语句的一部分。例如:

WORKDEPT = dept

将被解释为WORKDEPT列的值等于dept列的值。在宿主语言语句中,则不需要加前缀,正常引用即可。从下图中可看出如何使用宿主变量:

clip_image007

DB2名字空间(如表名、列名等等)不能用宿主变量指定。例如不能写如下SQL语句:

SELECT :col1 FROM :tabname

但是,这种类型的功能可以通过采用动态SQL实现。

总的来说,宿主变量有以下特点:

l 可选的,在语句运行之前用来赋值

l 宿主语言标号在SQL语句中,前面加冒号

l 宿主变量与列的数据类型必须匹配

l 对于宿主变量有以下要求:

a. 所有被嵌入SQL引用的宿主变量必须在BEGIN和END DECLARE语句界定的代码区里声明;

b. 宿主变量的数据类型必须与列的数据类型匹配,而且尽量避免数据转换和截取;

c. 宿主变量名不能以EXEC、SQL、sql开头;

d. 宿主变量应该被看作是模块程序的全局变量,而不是定义所在函数的局部变量;

e. 在界定区外定义的变量不能与界定区内定义的变量同名;

f. 在一个源文件中,可以有多个界定区;

g. BEGIN DECLARE SECTION语句可以在程序中宿主语言规则允许变量声明的任何位置出现,宿主变量定义区以END DECLARE SECTION语句结束;

h. BEGIN DECLARE SECTION和END DECLARE SECTION语句必须成对出现,并且不能嵌套;

i. 宿主变量声明可以使用SQL INCLUDE语句指定。另外,一个宿主变量声明区不能含有除宿主变量声明以外的语句。

3.2.2 宿主变量的使用

下面我们通过几个例子来说明宿主变量的用法:

1.在INSERT语句中的使用

l SQL语句

INSERT INTO TEMPL (EMPNO, LASTNAME)

VALUES (‘000190’, ‘JONES’)

l 嵌入程序的SQL语句

EXEC SQL INSERT INTO TEMPL (EMPNO, LASTNAME)

VALUES (:empno, :name);

第一条SQL语句可以在CLP中发出,它也可以嵌入程序中,但是它每一次只能插入一行值,如果要插入不同的值就要重新输入,程序也要修改。

第二条SQL语句只能嵌入程序中,每一次执行需要用户通过其它代码指定新值给宿主变量empno和name,宿主变量的作用是将用户指定的值传递给VALUES子句。可以实现输入多行值(循环或多次运行程序)。

2.在SET和WHERE子句中的使用

l SQL语句

UPDATE TEMPL

SET SALARY = SALARY *1.05

WHERE JOBCODE = 54

l 嵌入程序的SQL语句

EXEC SQL

UPDATE TEMPL

SET SALARY = SALARY * :percent

WHERE JOBCODE = :code;

3. 用宿主变量提取值。在程序中执行一个SELECT语句时,必须提供一个存储区域来接收返回的数据,而且对于被选择(selected)的每一列,都要定义一个宿主变量。语法为:SELECT … INTO :hostvaribale …。例子:

EXEC SQL

SELECT LASTNAME, WORKDEPT

INTO :name, :deptno

FROM TEMPL

WHERE EMPNO = :empid;

例子中定义了三个宿主变量,从表TEMPL中选择符合条件—EMPNO=:empid—的两列:LASTNAME和WORKDEPT,结果存放到宿主变量name和deptno中。此形式的用法要保证只能返回单行数据,如果返回多行数据库,则不能使用这种方法。后面会介绍如果使用游标(cursor)处理多行的返回结果集。

从上面的例子可将宿主变量分为两类:

l 输入宿主变量

输入宿主变量规定需要在语句执行期间从应用程序传递给数据库管理器的值。例如,在下面的SQL语句中将使用一个输入宿主变量:

SELECT name FROM candidate

WHERE name = < input host variable >

l 输出宿主变量

输出宿主变量规定需要在语句执行期间从数据库管理器传递给应用程序的值。例如,在下面的SQL语句中将使用一个输出宿主变量:

SELECT INTO < output host variable > FROM candidate

WHERE name = ‘ HUTCHISON ’

3.2.3 指示符变量的声明

在实际中,有些对象的值未知,我们用空值表示。当我们选择数据时,如果是空值,宿主变量的内容将不会被改变,是随机的。DB2数据库管理器提供了一个机制去通知用户返回数据是空值,这个机制就是指示符变量。

指示符(indicator)变量是一种特殊的宿主变量类型,它用来表示列的空值或非空值。当这些宿主变量作为输入进入数据库中时,应当在执行SQL语句之前由应用程序对它们设置值。当这些宿主变量作为数据库的输出使用时,这些指示符由应用程序定义,但由DB2更新和将它们返回。然后,在结果被返回时,应用程序应当检查这些指示符变量的值。

看下面一条SQL语句:

SELECT COLA INTO :a:aind

其中a是宿主变量,aind是指示符变量。如果COLA列的值不为空,DB2将aind的值设置为非负(通常为0);如果COLA列的值为空,DB2将aind的值设置为负数(通常为-1);如果DB2试图提示一个空值的存在,但是程序没有提供指示符,将会产生错误,SQLCODE等于-305。

指示符变量的定义:

指示符变量的定义与宿主变量的定义方法相同,都需要在BEGIN DECLARE SECTION和END DECLARE SECTION之间定义,并且数据类型与SQL数据类型SMALLINT对应,在C语言中为SHORT类型。

例子:

CREATE TABLE TEMPL

( EMPNO CHAR(6) NOT NULL,

LASTNAME VARCHAR(2) NOT NULL,

JOBCODE CHAR(2),

WORKDEPT CHAR(3), NOT NULL,

PHONENO CHAR(10))

EXEC SQL

SELECT JOBCODE, WORKDEPT, PHONENO

INTO :jc:jci, :dpt, :pho:phoi

FROM TEMPL

WHERE EMPNO = :id;

EMPNO(6)

LASTNAME(20)

JOBCODE

0-99

WORKDEPT(3)

PHONENO(10)

000070

000120

000320

JOHNSON

SCOTT

MILLIGAN

54

?

?

C01

C01

C01

5137853210

8592743091

?

3.2.4 指示符变量的使用

指示符逻辑例子1:

EXEC SQL

SELECT PHONENO, SEX

INTO :phoneno:phoneind, :sex

FROM TEMPL

WHERE EMPNO = :eno;

if (phoneind < 0)

null_phone();

else

good_phone();

在这个例子里,DB2维护指示符变量,应用程序在SQL语句执行后,询问指示符变量的值,调用相应的处理函数

指示符逻辑例子2:

if (some condition)

phoneind = -1;

else

phoneind = 0;

EXEC SQL

UPDATE TEMPL

SET NEWPHONE = :newphoneno :phoneind

WHERE EMPNO = :eno;

在这个例子里,应用程序维护指示符变量。应用程序根据条件设置指示符变量phoneind的值。如果DB2发现指示符的值为负数,那么给定行集合中的列被设置为空值,宿主变量的值被忽略;如果指示符的值为正数或者为零,宿主变量中的值被使用。

在嵌入SQL语句中可以使用关键字NULL。下面是不使用指示符变量的一个UPDATE语句例子:

if ( some condition)

EXEC SQL

UPDATE TEMPL

SET PHONENO = NULL

WHERE EMPNO = :eno ;

else

EXEC SQL

UPDATE TEMPL

SEST PHONENO = :newphone

WHERE EMPNO = :eno ;

但是,这种写法有缺点:如果UPDATE语句需要修改,就要修改两处代码。

如何设置指示符变量:

谁维护指示符变量

SQL语句类型

宿主变量

:cd

指示符变量

:cdi

JOBCODE

DB2

SELECT/

FETCH

60

不改变

0

<0

60

NULL

应用程序

UPDATE/

INSERT

50

N/A

0

<0

50

NULL

注解:

DB2在执行SELECT和FETCH语句的过程中设置指示符变量的值,应用程序应该在执行SELECT和FETCH语句后检查它们的值。

应用程序在执行UPDATE和INSERT语句之前设置指示符变量的值来指示DB2是否在数据库中放置一个空值(NULL)。

上表的第一行:在一条SELECT或者FETCH语句中,如果列(JOBCODE)中的值不为空,值被设置到宿主变量(:cd)中,指示符变量(:cdi)的值为零;如果列中的值为空,指示符变量的值将为负数,宿主变量的值不改变。

上表的第一行:在一条UPDATE或者INSERT语句中,如果指示符变量(:cdi)中的值不为负数,宿主变量(:cd)中的值被放到相应的列中;如果指示符变量中的值为负数,宿主变量中的值被忽略,相应的列被设置为空(NULL)。

指示符变量在数值转换方面的应用:

当宿主变量的数据类型与相应列的数据类型不兼容或者不能转换时,DB2也通过指示符变量通知应用程序。

数值转换由数据库管理器处理,能转换时自动完成,对程序透明;

如果列中的值不能存储到宿主变量中时(例如,列的数据类型为DECIMAL(15),值的长度为12个数字,不能存储到INTEGER类型的宿主变量中),指示符变量的值为-2。

指示符变量在截取方面的应用:

在SQL语句执行后,如果指示符变量的值为正数,说明发生了数据截取:

—如果是时间数据类型的秒部分被截取,那么指示符变量中的值为截取的秒数

—对于其他数据类型,指示符变量表示数据库中列的数据原始长度,通常为字节数(数据库尽可能返回更多的数据)。

例子:

定义宿主变量和指示符变量:

EXEC SQL BEGIN DECLARE SECTION;

char too_little[5];

short iv1;

EXEC SQL END DECLARE SECTION;

表BANK_ITEMS:

ITEM#

QTY

DESCRIPTION

101

3000

PASSBOOKS

200

100

CHECKBOOKS

执行的SQL语句:

SELECT DESCRIPTION INTO :too_little:iv

FROM BANK_ITEMS WHERE ITEM# = 200

结果:

:too_little中的值为’CHECK’,:iv1中的值为10

3.3 使用游标处理多行结果集

为了使应用程序能够提取多行结果集,SQL使用了一种机制,叫做游标(cursor)。

为了理解游标的概念,我们假设数据库管理器创建了一个结果表(result table),里面包含有执行一条SELECT语句所提取的所有行。通过标识或指向这个表的“当前行”,游标使得结果表中的行对应用程序可用。当一个是拥游标时,应用程序可以顺序从结果表中提取每一行,直到产生一个数据结束条件,这个条件就是NOT FOUND条件,SQLCA中的SQLCODE为+100(SQLSTATE为02000)。执行SELECT语句的结果集,可能有零行、一行或者更多行,决定于有多少行符合搜索的条件。

处理一个游标涉及到以下几个步骤:

1.使用DECLARE CURSOR语句声明一个游标

2.使用OPEN语句执行查询和创建结果表

3.使用FETCH语句每次提取一行结果

4.使用DELETE或UPDATE语句处理行(如果需要)

5.使用CLOSE语句终止(关闭)游标

一个应用程序中,可以使用多个游标,但是每一个游标要有自己的DECLARE CURSOR,OPEN,CLOSE和FETCH语句集。

3.3.1声明和使用游标

DECLARE CURSOR语句定义和命名游标,确定使用SELECT语句要提取的行结果集。

应用程序给游标分配一个名字。这个名字在随后的OPEN、FETCH和CLOSE语句中都要被参考到。查询可以是任何有效的SELECT语句。

下面例子展示了一条DECLARE语句如何与一条静态SELECT语句关联起来:

语言

源码例程

C/C++

EXEC SQL DECLARE C1 CURSOR FOR

SELECT PNAME, DEPT FROM STAFF

WHERE JOB=:host_var;

Java (SQLJ)

#sql iterator cursor1(host_var data type);

#sql cursor1 = { SELECT PNAME, DEPT FROM STAFF

WHERE JOB=:host_var };

COBOL

EXEC SQL DECLARE C1 CURSOR FOR

SELECT NAME, DEPT FROM STAFF

WHERE JOB=:host-var END-EXEC.

FORTRAN

EXEC SQL DECLARE C1 CURSOR FOR

+ SELECT NAME, DEPT FROM STAFF

+ WHERE JOB=:host_var

注解:DECLARE语句的在程序中位置是任意的,但是它必须在第一次使用游标的位置之前。

3.3.2游标与工作单元的考虑

COMMIT或者ROLLBACK操作的动作随游标的不同而不同,依赖于游标的定义。

1.只读游标(Read Only Cursors)

如果一个游标被确定为只读的,并且使用可重复读隔离级(isolation level),那么系统表仍会收集和维护工作单元需要的可重复读锁。因此,即使只读游标,应用程序周期性地发出COMMIT语句还是很重要的。

2.有WITH HOLD选项

如果应用程序通过发出一条COMMIT语句来完成一个工作单元,除了声明时有WITH HOLD选项的游标,所有游标将自动地被数据库管理器关闭。

用WITH HOLD声明的游标维护它访问的跨多个工作单元的资源。用WITH HOLD声明的游标受到的影响依赖于工作单元如何结束。

如果工作单元使用一条COMMIT语句结束,已打开的定义为WITH HOLD的游标将保持打开状态。游标的位置在结果集的下一个逻辑行之前。另外,参考用WITH HOLD定义的已准备好的语句也会被保留。紧跟COMMIT语句后面, 只有与一个某个特定游标相关联的FETCH和CLOSE请求才有效。UPDATE WHERE CURRENT OF和DELETE WHERE CURRENT OF 语句仅仅对在同一个工作单元中提取的行有效。如果程序包在工作单元期间被重新绑定,那么所有保持的游标都会被关闭。

如果工作单元使用一条ROLLBACK语句结束,所有打开的游标被关闭,所有在工作单元中获得的锁被释放,以及所有依赖于这个工作单元的已准备好的语句被删除。

举个例子,假设TEMPL表中有1000条记录。要更新所有雇员的工资,应该每更新100行就要发出一条COMMIT语句。

A. 使用WITH HOLD选项声明游标:

EXEC SQL DECLARE EMPLUPDT CURSOR WITH HOLD FOR

SELECT EMPNO, LASTNAME, PHONENO, JOBCODE, SALARY

FROM TEMPL FOR UPDATE OF SALARY

B. 打开游标,每一次从结果表中提取一行数据:

EXEC SQL OPEN EMPLUPDT

.

.

.

EXEC SQL FETCH EMPLUPDT

INTO :upd_emp, :upd_lname, :upd_tele, :upd_jobcd, :upd_wage,

C. 当想要更新或者删除一行时,使用带WHERE CURRENT OF选项的UPDATE或者DELETE语句。例如,要更新当前行,程序可以发出下面的语句:

EXEC SQL UPDATE TEMPL SET SALARY = :newsalary

WHERE CURRENT OF EMPLUPDT

在一条COMMIT语句发出之后,在更新其它行之前必须发出FETCH语句。

如果应用程序使用了用WITH HOLD声明的游标或者执行了多个工作单元并且有一个用WITH HOLD声明的游标跨工作单元处于打开状态,那么在程序中应该加入代码检测和处理SQLCODE为-501(SQLSTATE为24501)的错误,这个错误由FETCH或者CLOSE语句返回。

如果应用程序的程序包由于其依赖的表被删除而变得无效,程序包会自动被重新绑定。这种情况下,FETCH或CLOSE语句返回SQLCODE –501(SQLSTATE 24501),因为 数据库管理器关闭游标。在此情形下,处理SQLCODE –501(SQLSTATE 24501)的方法决定于是否要从游标提取行数据。

l 如果要从游标提取行,打开游标,然后运行FETCH语句。注意,OPEN语句使得游标重定位到开始处。原来的位置信息丢失。

l 如果不准备从游标提取行,那么不要对游标发出任何SQL请求。

WITH RELEASE 选项:当应用程序用WITH RELEASE选项关闭一个游标时,DB2试图去释放游标持有的所有读锁(READ locks)。游标只继续持有写锁(WRITE locks)。如果应用程序没有用RELEASE选项关闭游标,那么在工作单元完成时,所有的读锁和写锁都被释放。

3.3.3例程

游标程序

C Example: CURSOR.SQC

#include

#include

#include

#include "util.h"

#ifdef DB268K

/* Need to include ASLM for 68K applications */

#include

#endif

EXEC SQL INCLUDE SQLCA;

#define CHECKERR(CE_STR) if (check_error (CE_STR, &sqlca) != 0) return 1;

int main(int argc, char *argv[]) {

EXEC SQL BEGIN DECLARE SECTION;

char pname[10];

short dept;

char userid[9];

char passwd[19];

EXEC SQL END DECLARE SECTION;

#ifdef DB268K

/* Before making any API calls for 68K environment,

need to initial the Library Manager */

InitLibraryManager(0,kCurrentZone,kNormalMemory);

atexit(CleanupLibraryManager);

#endif

printf( "Sample C program: CURSOR /n" );

if (argc == 1) {

EXEC SQL CONNECT TO sample;

CHECKERR ("CONNECT TO SAMPLE");

}

else if (argc == 3) {

strcpy (userid, argv[1]);

strcpy (passwd, argv[2]);

EXEC SQL CONNECT TO sample USER :userid USING :passwd;

CHECKERR ("CONNECT TO SAMPLE");

}

else {

printf ("/nUSAGE: cursor [userid passwd]/n/n");

return 1;

} /* endif */

EXEC SQL DECLARE c1 CURSOR FOR (1)

SELECT name, dept FROM staff WHERE job='Mgr'

FOR UPDATE OF job;

EXEC SQL OPEN c1; (2)

CHECKERR ("OPEN CURSOR");

do {

EXEC SQL FETCH c1 INTO :pname, :dept; (3)

if (SQLCODE != 0) break;

printf( "%-10.10s in dept. %2d will be demoted to Clerk/n",

pname, dept );

} while ( 1 );

EXEC SQL CLOSE c1; (4)

CHECKERR ("CLOSE CURSOR");

EXEC SQL ROLLBACK;

CHECKERR ("ROLLBACK");

printf( "/nOn second thought -- changes rolled back./n" );

EXEC SQL CONNECT RESET;

CHECKERR ("CONNECT RESET");

return 0;

}

/* end of program : CURSOR.SQC */

Java Example: Cursor.sqlj

import java.sql.*;

import sqlj.runtime.*;

import sqlj.runtime.ref.*;

#sql iterator CursorByName(String name, short dept) ;

#sql iterator CursorByPos(String, short ) ;

class Cursor

{ static

{ try

{ Class.forName ("COM.ibm.db2.jdbc.app.DB2Driver").newInstance ();

}

catch (Exception e)

{ System.out.println ("/n Error loading DB2 Driver.../n");

System.out.println (e);

System.exit(1);

}

}

public static void main(String argv[])

{ try

{ System.out.println (" Java Cursor Sample");

String url = "jdbc:db2:sample";

// URL is jdbc:db2:dbname

Connection con = null;

// Set the connection

if (argv.length == 0)

{ // connect with default id/password

con = DriverManager.getConnection(url);

}

else if (argv.length == 2)

{ String userid = argv[0];

String passwd = argv[1];

// connect with user-provided username and password

con = DriverManager.getConnection(url, userid, passwd);

}

else

{ throw new Exception("Usage: java Cursor [username password]");

}

// Set the default context

DefaultContext ctx = new DefaultContext(con);

DefaultContext.setDefaultContext(ctx);

// Enable transactions

con.setAutoCommit(false);

// Using cursors

try

{ CursorByName cursorByName;

CursorByPos cursorByPos;

String name = null;

short dept=0;

// Using the JDBC ResultSet cursor method

System.out.println("/nUsing the JDBC ResultSet cursor method");

System.out.println(" with a 'bind by name' cursor .../n");

#sql cursorByName = {

SELECT name, dept FROM staff

WHERE job='Mgr' FOR UPDATE OF job }; (1)

while (cursorByName.next()) (2)

{ name = cursorByName.name(); (3)

dept = cursorByName.dept();

System.out.print (" name= " + name);

System.out.print (" dept= " + dept);

System.out.print ("/n");

}

cursorByName.close(); (4)

// Using the SQLJ iterator cursor method

System.out.println("/nUsing the SQLJ iterator cursor method");

System.out.println(" with a 'bind by position' cursor .../n");

#sql cursorByPos = {

SELECT name, dept FROM staff

WHERE job='Mgr' FOR UPDATE OF job }; (1) (2)

while (true)

{ #sql { FETCH :cursorByPos INTO :name, :dept }; (3)

if (cursorByPos.endFetch()) break;

System.out.print (" name= " + name);

System.out.print (" dept= " + dept);

System.out.print ("/n");

}

cursorByPos.close(); (4)

}

catch( Exception e )

{ throw e;

}

finally

{ // Rollback the transaction

System.out.println("/nRollback the transaction...");

#sql { ROLLBACK };

System.out.println("Rollback done.");

}

}

catch( Exception e )

{ System.out.println (e);

}

}

}

游标程序如果工作:

1.声明游标。DECLARE CURSOR语句将游标c1与一个查询关联起来。查询确定应用程序使用FETCH语句提取的行。表staff的job字段被定义为可更新的,即使它在结果表中。

2.打开游标。游标c1被打开,数据库管理器执行查询并创建结果表。游标的指向位于第一行的前面。

3.提取一行。FETCH语句使游标指向下一行,将那行的内容移动到宿主变量中。那行成为当前行。

4. 关闭游标。发出CLOSE语句,释放与游标相关联的资源。游标仍然可以再次打开。

3.3.4更新和删除提取的数据

可以更新和删除一个游标指向的行。要使行能被更新,对应于游标的查询不能是只读的。

更新提取的数据

为了利用游标来更新数据,在UPDATE语句中使用WHERE CURRENT OF子句。使用FOR UPDATE子句告诉系统结果表中的哪些列要更新。也可以在FOR UPDATE中指出不在选择范围之内的列,从另一个角度讲,可以更新不被游标提取的列。如果FOR UPDATE子句中没有指定任何列名,那么在第一个FROM子句中标识的表或者视图的所有列都被看作是可更新的。性能上要求只在FOR UPDATE子句里指定要更新的列,如果指定不需要更新的列,DB2将浪费没有必要的开销去访问数据。

删除提取的数据

为了利用游标来删除数据,在DELETE语句中使用WHERE CURRENT OF条款。通常上,为了删除游标的当前行,定义游标时,不需要FOR UPDATE条款。一个例外情况是,在应用程序中使用动态的SELECT语句或者DELETE语句,预编译时LANGLEVEL选项设为SAA1,捆绑时使用BLOCKING ALL选项。这种情况下,SELECT语句需要FOR UPDATE条款。(详细信息请看3.3节开发动态SQL应用程序)

DELETE语句使得游标指示的那行被删除。游标的位置指向下一行的前面。要进行下一个WHERE CURRENT OF操作之前,必须对游标发出一条FETCH语句。

游标的类型

游标可以分为三类:

1.只读类型

游标指向的行只能够读,不能够更新。应用程序只是读取数据不修改时,使用只读游标。如果游标是基于只读SELECT语句,那么它便是只读的。只读游标在性能方面较好。

2.可更新类型

游标指向的行可以更新。应用程序需要修改提取的数据时,使用可更新游标。指定的查询只能涉及到一个表或视图。查询必须包含FOT UPDATE子句,并写出每一个要更新的列(除非预编译时使用LANGLEVEL MIA选项)。

3.不明确类型

从游标的定义或者上下文来确定游标是可更新的还是只读的。

当预编译或绑定时使用BLOCKING ALL选项,不明确的游标被看作是只读的。否则,被看作是可更新的。

注意:动态处理的游标通常是不明确类型的。

例子 OPENFTCH程序

这个例子使用一个游标从一个表中选择行,打开游标,从表中提取行。对提取的每一行,判断是否要删除或者更新(根据一个简单的条件)。这个例子有以下语言的版本:

C语言:openftch.sqc

Java语言:Openftch.sqlj and OpF_Curs.sqlj

COBOL语言:openftch.sqb

FORTRAN 语言:openftch.sqf

C Example: OPENFTCH.SQC

#include

#include

#include

#include "util.h"

#ifdef DB268K

/* Need to include ASLM for 68K applications */

#include

#endif

EXEC SQL INCLUDE SQLCA;

#define CHECKERR(CE_STR) if (check_error (CE_STR, &sqlca) != 0) return 1;

int main(int argc, char *argv[]) {

EXEC SQL BEGIN DECLARE SECTION;

char pname[10];

short dept;

char userid[9];

char passwd[19];

EXEC SQL END DECLARE SECTION;

#ifdef DB268K

/* Before making any API calls for 68K environment,

need to initial the Library Manager */

InitLibraryManager(0,kCurrentZone,kNormalMemory);

atexit(CleanupLibraryManager);

#endif

printf( "Sample C program: OPENFTCH/n" );

if (argc == 1) {

EXEC SQL CONNECT TO sample;

CHECKERR ("CONNECT TO SAMPLE");

}

else if (argc == 3) {

strcpy (userid, argv[1]);

strcpy (passwd, argv[2]);

EXEC SQL CONNECT TO sample USER :userid USING :passwd;

CHECKERR ("CONNECT TO SAMPLE");

}

else {

printf ("/nUSAGE: openftch [userid passwd]/n/n");

return 1;

} /* endif */

EXEC SQL DECLARE c1 CURSOR FOR (1)

SELECT name, dept FROM staff WHERE job='Mgr'

FOR UPDATE OF job;

EXEC SQL OPEN c1; (2)

CHECKERR ("OPEN CURSOR");

do {

EXEC SQL FETCH c1 INTO :pname, :dept; (3)

if (SQLCODE != 0) break;

if (dept > 40) {

printf( "%-10.10s in dept. %2d will be demoted to Clerk/n",

pname, dept );

EXEC SQL UPDATE staff SET job = 'Clerk' (4)

WHERE CURRENT OF c1;

CHECKERR ("UPDATE STAFF");

} else {

printf ("%-10.10s in dept. %2d will be DELETED!/n",

pname, dept);

EXEC SQL DELETE FROM staff WHERE CURRENT OF c1;

CHECKERR ("DELETE");

} /* endif */

} while ( 1 );

EXEC SQL CLOSE c1; (5)

CHECKERR ("CLOSE CURSOR");

EXEC SQL ROLLBACK;

CHECKERR ("ROLLBACK");

printf( "/nOn second thought -- changes rolled back./n" );

EXEC SQL CONNECT RESET;

CHECKERR ("CONNECT RESET");

return 0;

}

/* end of program : OPENFTCH.SQC */

Java Example: Openftch.sqlj

OpF_Curs.sqlj

// PURPOSE : This file, named OpF_Curs.sqlj, contains the definition

// of the class OpF_Curs used in the sample program Openftch.

import sqlj.runtime.ForUpdate;

#sql public iterator OpF_Curs implements ForUpdate (String, short);

Openftch.sqlj

import java.sql.*;

import sqlj.runtime.*;

import sqlj.runtime.ref.*;

class Openftch

{ static

{ try

{ Class.forName ("COM.ibm.db2.jdbc.app.DB2Driver").newInstance ();

}

catch (Exception e)

{ System.out.println ("/n Error loading DB2 Driver.../n");

System.out.println (e);

System.exit(1);

}

}

public static void main(String argv[])

{ try

{ System.out.println (" Java Openftch Sample");

String url = "jdbc:db2:sample"; // URL is jdbc:db2:dbname

Connection con = null;

// Set the connection

if (argv.length == 0)

{ // connect with default id/password

con = DriverManager.getConnection(url);

}

else if (argv.length == 2)

{ String userid = argv[0];

String passwd = argv[1];

// connect with user-provided username and password

con = DriverManager.getConnection(url, userid, passwd);

}

else

{ throw new Exception(

"/nUsage: java Openftch [username password]/n");

} // if - else if - else

// Set the default context

DefaultContext ctx = new DefaultContext(con);

DefaultContext.setDefaultContext(ctx);

// Enable transactions

con.setAutoCommit(false);

// Executing SQLJ positioned update/delete statements.

try

{ OpF_Curs forUpdateCursor;

String name = null;

short dept=0;

#sql forUpdateCursor =

{ SELECT name, dept

FROM staff

WHERE job='Mgr'

FOR UPDATE OF job

}; // #sql (1)(2)

while (true)

{ #sql

{ FETCH :forUpdateCursor

INTO :name, :dept

}; // #sql (3)

if (forUpdateCursor.endFetch()) break;

if (dept > 40)

{ System.out.println (

name + " in dept. "

+ dept + " will be demoted to Clerk");

#sql

{ UPDATE staff SET job = 'Clerk'

WHERE CURRENT OF :forUpdateCursor

}; // #sql (4)

}

else

{ System.out.println (

name + " in dept. " + dept

+ " will be DELETED!");

#sql

{ DELETE FROM staff

WHERE CURRENT OF :forUpdateCursor

}; // #sql

} // if - else

}

forUpdateCursor.close(); (5)

}

catch( Exception e )

{ throw e; }

finally

{ // Rollback the transaction

System.out.println("/nRollback the transaction...");

#sql { ROLLBACK };

System.out.println("Rollback done.");

} // try - catch - finally

}

catch( Exception e )

{ System.out.println (e); } // try - catch

} // main

} // class Openftch

OPENFTCH程序是如何工作的:

1.声明游标。DECLARE CURSOR语句将游标c1与一个查询关联起来。查询确定应用程序使用FETCH语句提取的行。表staff的job字段被定义为可更新的,即使它不在结果表中。

2.打开游标。游标c1被打开,导致数据库管理器执行查询并创建一个结果表。游标的位置位于第一行的前面。

3.提取一行。FETCH语句将游标指向下一行,并将行的内容移到宿主变量中。那行成为当前行。

4.更新或删除当前行。当前行被更新或者被删除,决定于FETCH语句返回dept的值。

如果一个UPDATE语句被执行,游标的位置保持不变,因为UPDATE语句不改变当前行的位置。

如果一个DELETE语句被执行,游标的位置位于下一行的前面,因为当前行已被删除。在进行另外的WHERE CURRENT OF操作之前,必须执行一条FETCH语句。

5.关闭游标。CLOSE语句被发出,释放与游标关联的资源。游标可以被再次打开。

3.4 诊断信息处理与SQLCA结构

3.4.1 SQLCA结构

SQLCA的全称为SQL通信区(Communication Area),是应用程序用来接收数据库管理返回的SQL执行情况信息的数据结构。

下面是SQLCA的C语言定义:

SQL_STRUCTURE sqlca

{

_SQLOLDCHAR sqlcaid[8]; /* Eyecatcher = 'SQLCA ' */

sqlint32 sqlcabc; /* SQLCA size in bytes = 136 */

sqlint32 sqlcode; /* SQL return code */

short sqlerrml; /* Length for SQLERRMC */

_SQLOLDCHAR sqlerrmc[70]; /* Error message tokens */

_SQLOLDCHAR sqlerrp[8]; /* Diagnostic information */

sqlint32 sqlerrd[6]; /* Diagnostic information */

_SQLOLDCHAR sqlwarn[11]; /* Warning flags */

_SQLOLDCHAR sqlstate[5]; /* State corresponding to SQLCODE */

};

下表是SQLCA数据结构的详细描述:

元素名

   

sqlcaid

sqlcabc

sqlcode

sqlerrml

sqlerrmc

sqlerrp

sqlerrd

sqlwarn

sqlstate

CHAR(8)

INTEGER

INTEGER

SMAIIINT

VARCHAR(70)

CHAR(8)

INTEGER ARRAY

CHARACTER ARRAY

CHAR(5)

有助于从外观上识别该数据的视觉—捕捉器。它应当包含字符串‘SQLCA’

包含SQLCA的长度。它应当总是包含值136

这可能是SQLCA结构的最重要元素。如果SQL被成功地处理,这个值包含错误代码或者是零值。如果该值是正数,那么返回警告信息并且处理SQL语句。如果该值是负数,那么有错误信息发生并且不处理SQL语句。如果该值是零(0),那么没有出现错误和警告信息并且处理了SQL语句

包含在元素sqlerrmc中字符串的长度

包含用X’FF’间隔的一个到多个信息标志。这些信息标志提供了所发生错误/警告的更多情况。间隔符用来传递多个参数/标志

通常,这个元素包含产品的签名。这个产品签名是一个代表目前正在被使用的DB2数据库服务器的类型。例如SQL02010说明当前的服务器是DB2通用服务器2.1.0版本。其它的DB2数据库服务器是:

DSN —DB2 for MVS

SQS —DB2 for OS/400

ARI — DB2 for VM/VSE(SQL/DS)

格式是pppvvrrm,其中:

ppp — product(产品)

vv — version (版本号)

rr — release (颁布号)

m — modification level (修改级别)

当发现错误条件时,这6个整数值的数组能够包含附加的诊断信息

SQLERRD(1) — 内部返回码

SQLERRD(2) —

内部返回码

复合的SQL —失败号

SQLERRD(3)

PREPARE — 返回的行数(估计)

UPDATE/DELETE/INSERT — 受影响的行数

复合的SQL — 所有行的总和

CONNECT — 1,如果数据库可更新;2,如果数据库不可更新

SQLERRD(4)

PREPARE — 开销评估

复合的SQL — 成功的子语句数

CONNECT

0 — 由底层客户机的一阶段委托确认

0 — 一阶段委托确认

0 — 一阶段只读委托确认

0 — 二阶段委托确认

SQLERRD(5)

DELETE/INSERT/UPDATE—行数(根据限制条件)

复合的SQL—行数(根据限制条件)

SQLERRD(6)—保留(未使用)

对应于各种警告条件的指示符的集合

SQLWARN0 — 全局指示符,’’,若无警告;’W’,否则

SQLWARN1 — ‘W’,字符串被截断地复制到宿主变量

SQLWARN2 — ‘W’, null value(s) eliminated from evaluation of a column function

SQLWARN3 — ‘W’, 列的个数大于宿主变量的个数

SQLWARN4 — ‘W’, UPDATE/DELETE语句不包含’WHERE’子句

SQLWARN5 — 保留

SQLWARN6 — date or timestamp value adjusted to correct an invalid data resulting from arithmetic operation

SQLWARN7 — 保留

SQLWARN8 — 字符结果不能被转换,而被子串替换

SQLWARN9 — 在列函数的处理过程中,忽略了有错误的算术表达式

SQLWARNA —在转换SQLCA中的一个字符数据值时发生转换错误

交叉平台错误代码。sqlstate的值对应于sqlcode。某些sqlcodes没有对应的sqlstate,因为对于指定的错误条件没有等价的sqlstate

对于大多数应用程序开发环境,为了创建和维护一个质量合格的软件,适当的错误处理是极其重要的。从应用程序中发出的每条SQL语句可能产生一个成功条件或一个错误条件。应用程序开发人员判定SQL语句结果的主要手段是检查SQL通信区(SQLCA)的内容。SQLCA是由DB2定义的宿主语言的数据结构。它包含着在处理SQL语句期间由DB2装填的许多数据元素。

[注解]:在DB2 REXX环境中自动地提供SQLCA

开发者的应用程序必须在发出任何SQL语句之前声明SQLCA(SQL 通信区)。有以下两种在程序中定义SQLCA的方法:

l 使用EXEC SQL INCLUDE SQLCA。

l 在DB2的头文件中声明一个叫做sqlca的结构。

在表中给出的SQLCA数据结构可以作为在应用程序与DB2之间处理错误的主要手段。关键的是开发者的应用程序要在执行每条SQL语句之后核查SQLCA的内容。不能核查SQLCA的内容可能会引起无法预料的错误发生。对于可能发生的许多错误,通常系统会向用户建议相应的措施(它们可以被编码到用户的应用程序中)。例如,假设开发者试图与数据库服务器相连接并且收到一个错误信息,说尚未启动数据库管理器。应用程序可以发出DB2START命令去启动数据库管理器,然后再次试图与数据库服务器相连接。

3.4.2 WHENEVER语句

由于错误或警告的发生是不可预料的,所以我们无法象普通编程那样用条件或者循环等控制结构来实现错误处理,于是DB2提供了WHENEVER语句.

WHENEVER这一控制结构使预编译器产生的执行代码,使应用程序在遇到错误、警告或"没有行发现"时转到指定的代码段,继续执行相应的处理程序. WHENEVER对同一源程序中所有另一同类WHENEVER前的SQL执行语句均有效.

WHENEVER有三种基本形式:

1. EXEC SQL WHENEVER SQLERROR action

2. EXEC SQL WHENEVER SQLWARNING action

3. EXEC SQL WHENEVER NOT FOUND action

在这些形式中,SQLERROR指的是SQLCODE<0的情况;SQLWARNING指的是SQLWARN0 = ‘w’或者SQLCODE>0;NOT FOUND指的则是SQLCODE=+100的情况.

action可以是以下两种情况之一:

CONTINUE: 表示忽略这个异常,继续应用程序的下一步执行;

Go To label: 表示发生异常时,转到label所标识的语句开始.

例子:

SQL statement

EXEC SQL

WHENEVER SQLERROR GO TO :A;

SQL statement

IF SQLCODE <0 ―>A

SQL statement

IF SQLCODE <0 ―>A

EXEC SQL

WHENEVER SQLWARNING GO TO :W;

EXEC SQL

WHENEVER SQLERROR GO TO :B;

SQL statement

IF SQLCODE < 0 ―>B

IF warning ―>w

EXEC SQL

WHENEVER SQLERROR CONTINUE;

SQL statement

IF warning ―>W

3.4.3 查询错误

无论何时在任何示例中输入出错时,或者如果执行 SQL 语句期间出错,则数据库 管理程序返回错误信息。错误信息由信息标识符、简要说明以及 SQLSTATE 组 成。

SQLSTATE 是 DB2 系列产品的公共错误码。 SQLSTATE 符合 ISO/ANSI SQL92 标准。

例如,如果 CONNECT 语句中用户名或口令不正确,则数据库管理程序将返回 信息标识符 SQL1403N 和 SQLSTATE 为 08004。该信息如下:

SQL1403N 提供的用户名和/或口令不正确。 SQLSTATE=08004

可以通过输入一个问号(?),然后输入信息标识符或 SQLSTATE 来 获取关于错误信息的更多信息:

? SQL1403N

? SQL1403

? 08004

注意:错误 SQL1403N 的说明中倒数第二行表明 SQLCODE为-1403。 SQLCODE 为产品特定错误码。 以 N(通知)或 C(严重)结尾的信息标识符表示一个错误,并且具有负 SQLCODE。 以 W(警告)结尾的信息标识符表示一个警告,并且具有正 SQLCODE。

3.5 复合SQL语句

3.5.1 复合SQL的定义

构建在客户/服务器体系结构上的数据库应用程序,每执行一条SQL语句就要与数据库管理器交互一次。为了减少客户与服务器之间的交互信息及次数、提高整体系统的性能,DB2在程序开发方法上提供了一种机制―复合SQL(Compound SQL)语句。

复合 SQL 允许您将几条 SQL 语句分组为单个的可执行块。包含于块中的 SQL 语句(子语句)可以单独执行;但是,通过创建和执行一个语句块,您可以减少数据库管理程序的额外开销。对于远程客户机,复合 SQL 也减少必须通过网络传送的请求的数目。

有两种类型的复合 SQL:

l 基本 (ATOMIC)

当所有子语句都成功完成时,或当一个子语句因出错而结束时,应用程序将从数据库管理程序收到一个响应。如果一个子语句因出错而结束,则将整个块视为因出错而结束,并且将回滚在此块中对数据库所做的任何更改。

l 非基本(NOT ATOMIC)

当所有子语句都完成时,应用程序将从数据库管理程序接收到一个响应。不管其前面的子语句是否成功执行,一个块中的所有子语句都要执行。仅当回滚包含非基本复合 SQL 的工作单元时,才可以回滚此语句组。

3.5.2 复合SQL的例子

我们通过一个例子来说明如何编写复合SQL语句:

EXEC SQL BEGIN COMPOUND ATOMIC STATIC

UPDATE SAVINGS SET

BALANCE = BALANCE - :transfer

WHERE ATMCARD = :atmcard;

UPDATE CHECKING SET

BALANCE = BALANCE + :transfer

WHERE ATMCARD = :atmcard;

INSERT INTO ATMTRANS (TTSTMP, CODE, AMOUNT)

VALUES (CURRENT TIMESTAMP, :code, :transfer);

COMMIT;

END COMPOUND;

1. 这个例子演示了复合SQL语句的一个简单使用:将钱从银行存款帐号转到支票帐号,并将转帐记录在审计表中;

2. 关键字ATOMIC 指明:在其中的所有SQL语句都成功执行,复合SQL语句才算成功,也就是可以提交(COMMIT);如果其中有一条SQL语句失败,那么复合SQL语句中所有SQL语句对数据库所作的改变都被回滚(Roll Back);

3. 可以用关键字NOT ATOMIC替换ATOMIC。这样会忽略任何子语句的失败,数据库管理器不会回滚复合SQL语句中其他语句所作的改变。SQLCA中的信息指出有多少条语句处理成功和检测到的错误信息;

4. 关键字STATIC表示在复合SQL语句中引用的宿主变量在复合SQL语句被调用时获得的值,不会被复合SQL语句中的任何SQL语句改变。例如:

EXEC SQL BEGIN COMPOUND ATOMIC STATIC

UPDATE SAVINGS SET

BALANCE = BALANCE - :transfer

WHERE ATMCARD = :atmcard;

SELECT ACCOUNT INTO :atmcard

WHERE CARDHOLDER = ‘Cartwright’;

UPDATE CHECKING SET BALANCE = BALANCE + :transfer

WHERE ATMCARD = :atmcard;

INSERT INTO ATMTRANS (TTSTMP, CODE, AMOUNT)

VALUES (CURRENT TIMESTAMP, :code, :transfer);

COMMIT;

END COMPOUND;

在每一条UPDATE和INSERT语句中使用:atmcard在复合SQL运行之前获得的值,而不是对应于‘Cartwright‘的值。

5. 语句:STOP AFTER FIRST host_var STATEMENTS可以加在BEGIN COMPOUND语句中,允许程序指定复合SQL语句执行时,只执行其中的多少条。例如:

EXEC SQL BEGIN COMPOUND NOT ATOMIC STATIC STOP AFTER FIRST :nbr STATEMENTS

INSERT INTO TAB1 VALUES(:col1val1, :col2val1);

INSERT INTO TAB1 VALUES(:col1val2, :col2val2);

INSERT INTO TAB1 VALUES(:col1val3, :col2val3);

INSERT INTO TAB1 VALUES(:col1val4, :col2val4);

INSERT INTO TAB1 VALUES(:col1val5, :col2val5);

INSERT INTO TAB1 VALUES(:col1val6, :col2val6);

INSERT INTO TAB1 VALUES(:col1val7, :col2val7);

INSERT INTO TAB1 VALUES(:col1val8, :col2val8);

END COMPOUND;

将宿主变量:nbr的值设置为希望一次插入的行数。

6. 如果复合SQL语句中包含COMMIT语句,必须放在最后一条语句的位置,即在END COMPOUND之前。即使使用STOP AFTER FIRST host_var STATEMENTS选项,并且host_var的值小于复合SQL语句中的SQL语句条数,COMMIT语句也会被执行。注意:当连接类型(CONNECT TYPE)为2时,不允许有COMMIT语句。

7. SQLSTATE和SQLCODE的值是复合SQL语句中的最后执行的语句的信息。

3.5.3复合SQL的限制

使用复合SQL语句的限制:

1. DB2 Connect只支持NOT ATOMIC类型的复合SQL语句

2. 复合SQL语句只能包含静态SQL语句

3. 复合SQL语句只能在嵌入SQL语句程序中使用

4. 下列语句不能包含在复合SQL语句中:

CALL

CLOSE

CONNECT

Compound SQL

DESCRIBE

DISCONNECT

EXECUTE IMMEDIATE

FETCH

OPEN

PREPARE

RELEASE

ROLLBACK

SET CONNECTION

第四章 动态嵌入式SQL应用编程

4.1 动态SQL概述

4.1.1动态嵌入式SQL与静态嵌入式SQL的比较

*嵌入在数据库应用程序中的SQL语句,如果在预编译时其内容已确定则称其为静态嵌入式SQL语句。由于静态嵌入式SQL语句的内容在预编译时是已知的,所以BIND过程可以在程序实际运行之前确定其执行方案并以程序包(Package)的形式存储在数据库中。

*为了增加应用程序的灵活性,允许应用程序在运行过程中动态地产生并执行SQL语句,并称其为动态嵌入式SQL语句。由于动态嵌入式SQL语句的内容(或其中一部分内容)是在程序运行中产生的,在预编译时还无法确定,所以BIND过程无法象对待静态嵌入式SQL语句那样,在程序实际运行之前确定其执行方案并创建相应的程序包。因此,动态嵌入式SQL语句的BIND过程只能是动态的,即在程序实际运行过程中完成的。

4.1.2 动态嵌入式SQL语句的类型

按照动态嵌入式SQL语句是否为查询语句以及是否包含参数标志,可以将其分为以下几种类型:

1 除SELECT之外的动态SQL语句

即除了SELECT之外的其它所有可执行的SQL语句。由于这类语句没有返回结果集,所以其处理也相对最为简单。

2 输出结果列固定的动态SELECT语句

又可进一步分为输出结果列完全固定的SELECT语句和所有部分都不固定的SELECT语句两类。与处理静态嵌入式SELECT语句的返回结果集一样,在应用程序中也需要使用游标来处理动态嵌入式SELECT语句的返回结果集。而对于输出结果列完全固定的SELECT语句,由于在编程时已事先知道其输出结果列的数据类型,所有可以在程序中预先定义相应的宿主语言变量来接收查询结果集中的数据值。而对于所有内容都不固定的SELECT语句,由于在编程时无法事先知道其会产生哪些输出结果列,所有只能在程序中使用SQLDA(又称SQL描述符区)这种动态数据结构及动态分配的宿主语言变量来接收查询结果集中的数据值。

3 全部内容完全动态的SQL语句

4参数化的动态语句

即当中含有参数标记(?)的动态SQL语句。由于参数化的语句在执行之前必须对其中的参数标记进行赋值,所以,相对非参数化的语句,其处理要更加复杂一些。

4.3 支持动态嵌入式SQL的语句

在SQL中,嵌入式动态语句是通过下列4种语句来处理的:PREPARE,DESCRIBE,EXECUTE及EXECUTE IMMEDATE。此外,为了得到动态SELECT语句返回的查询结果集,还引入了动态游标说明语句,并对OPEN语句和FETCH语句增加了一些选项。

4.3.1 PREPARE语句

PREPARE语句的语法格式为:

PREPARE 语句名 [INTO 描述符] FROM :宿主变量

其中,语句名可为任何标识符,宿主变量是一个字符串型的变量并且必须含有一条有效的SQL语句,描述符是一个类型为SQLDA的宿主语言变量。该语句的功能是对一条SQL语句(包含在FROM之后的宿主变量中)进行预处理,并用语句名作为处理后的语句的标识(以便在后面的DESCRIBE,EXECUTE或OPEN语句中引用)。PREPARE语句要求被处理SQL语句中不能出现任何宿主变量,但可以包含一个或多个参数标记(用问号“?”表示),表示语句执行时需要从外部得到的值。如果PREPARE语句出现“INTO 描述符”选项,则将被处理语句的结果集中各个列的描述信息填入所给的描述符中。

PREPARE语句的例子:

EXEC SQL PREPARE S1 FROM :stmt;

EXEC SQL PREPARE Q1 INTO :sqlda FROM :query1

4.3.2 DESCRIBE语句

DESCRIBE语句的语法格式为:

DESCRIBE 语句名 INTO 描述符

其中,语句名为一个标识符并且必须标识一条前面已预处理过的语句,描述符是一个类型为SQLDA的宿主语言变量。该语句的作用类似于PREPARE语句中的“INTO描述符”子句,其功能是得到语句名所标识的那条语句(已预处理过的)的结果集中各个列的描述信息,并将其填入所给的描述符中。

例如:

EXEC SQL DESCRIBE Q1 INTO :sqlda;

4.3.3 EXECUTE语句

EXECUTE语句的语法格式为:

EXECUTE 语句名 [USING 宿主变量1,…,宿主变量n |

USING DESCRIPTOR描述符]

其中,语句名为一个标识符并且必须标识一条前面已预处理过的语句,描述符是一个类型为SQLDA的宿主语言变量。该语句的功能是执行一条前面已预处理过的语句(由语句名所标识)。

例如:

EXEC SQL EXECUTE S1;

EXEC SQL EXECUTE S2 USING :x,:y,:z;

EXEC SQL EXECUTE S3 USING DESCRIPTOR :sqlda;

4.3.4 EXECUTE IMMEDATE语句

EXECUTE IMMEDATE语句的语法格式为:

EXECUTE IMMEDATE 宿主变量

其中,宿主变量是一个字符串型的变量并且必须含有一条有效的SQL语句(但不能是SELECT或VALUES语句)。EXECUTE IMMEDATE语句的功能是对包含在宿主变量中的语句进行预处理,并立即执行它。该语句的作用相当于将PREPARE和EXECUTE两条语句的功能组合在一起。

例如:

EXECUTE IMMEDATE :stmt;

4.4 动态游标的处理

4.4.1 动态游标说明语句

动态游标说明语句的语法格式为:

DECLARE 游标名 CURSOR [WITH HOLD] FOR 语句名

其中,游标名可为任何标识符,语句名为一个标识符并且必须标识一条前面已预处理过的查询语句。动态游标说明语句的作用是将一个游标与一条已预处理过的动态查询语句相关连,但并不实际执行这一动态查询语句。动态游标被说明后即可通过OPEN和FETCH语句执行这一动态查询并获得结果集。如果游标说明语句中包含WITH HOLD选项,则游标可以在事务提交后仍保持为打开状态。

动态游标说明的例子:

EXEC SQL PREPARE Q1 FROM :query;

EXEC SQL DECLARE C1 CURSOR FOR Q1;

4.4.2 动态游标OPEN语句

动态游标OPEN语句的语法格式为:

OPEN 游标名 [USING 宿主变量1,…,宿主变量n |

USING DESCRIPTOR 描述符名]

动态OPEN语句中出现的游标名必须在前面的动态游标说明语句中被说明。该语句的功能是执行与游标名相关连的动态查询语句并产生一个查询结果集,该语句执行成功后,如果结果集不为空,则游标将指向结果集的第1行数据。动态游标被打开后即可使用动态FETCH语句从其结果集中读取出数据行。此外,如果被执行的动态查询语句中含有参数标记,则可通过USING子句中的宿主变量列表(或描述符)为其提供替换值。

动态OPEN语句的例子:

EXEC SQL PREPARE Q1 FROM :query;

EXEC SQL DECLARE C1 CURSOR FOR Q1;

EXEC SQL OPEN C1 USING :x,:y;

4.4.3 动态游标FETCH语句

动态FETCH语句的语法格式为:

FETCH 游标名 {INTO 宿主变量1,...,宿主变量n |

USING DESCRIPTOR 描述符名}

其中,游标名必须是一个已经打开的游标。动态FETCH语句的功能是从结果集中取出一行数据并将游标当前位置向下移动一行,所取出数据将被放入INTO子句所指定的宿主变量(或描述符)之中。INTO子句中所指定的宿主变量的数目和数据类型(或描述符中的项数及数据类型)必须与结果集的列数及数据类型相匹配。如果结果集为空集或游标的当前位置已指向结果集中最后一行之后,则动态FETCH语句将返回SQLCODE+100(SQLSTATE为02000)。

动态FETCH语句的例子:

EXEC SQL FETCH C1 INTO :x,:y,:z;

EXEC SQL FETCH C1 USING DESCRIPTOR :sqlda;

以下是一个动态SQL语句的较完整的例子:

char qstr[]=”SELECT EMPNO,SALARY FROM EMP WHERE NAME=?” ;

EXEC SQL PREPARE Q1 FROM :qstr;

EXEC SQL DECLARE C1 CURSOR FOR Q1;

EXEC SQL OPEN C1 USING :name;

EXEC SQL FETCH C1 INTO :empno,:salary;

EXEC SQL CLOSE C1;

4.5 SQLDA的组成和存储格式

SQLDA又称SQL描述符区(Descriptor Area),可用来在应用程序和数据库系统之间传递有关列(或宿主变量)的数目及数据类型与/或值等方面的信息。SQLDA是一种功能非常强大且使用非常灵活的动态数据结构。SQLDA的存储空间可在程序运行期间根据需要动态进行分配,并可针对任何动态SQL语句,得到有关列的数目、数据类型、长度、精度等方面的描述信息,或针对动态分配的宿主变量设置变量的数目、数据类型、位置及变量值。因此,SQLDA比程序中的静态宿主变量列表更加灵活,也更加复杂。

clip_image008

HEADER

sqldaid CHAR(8)

sqldabc INTEGER

sqln SMALLINT

sqld SMALLINT

 

SQLVARs

(1 PER

FIELD )

sqltype SMALLINT

sqllen SMALLINT

clip_image009sqldata
POINTER

sqlind POINTER

sqlname VARCHAR(30)

图4-1 SQLDA的存储格式

如图所示,SQLDA是由1个头(header)及若干个(可以为0个或多个)需要描述的列或变量(又称为条目(entry))的描述信息组成。其中SQLDA头(header)信息的大小为16字节(Byte);每一个条目信息占44字节(Byte)。因此,一个SQLDA的总的大小是由其中所包含的条目数(sqlvars)决定的,即按字节计算,一个SQLDA的大小应为16+条目数*44 Byte。

SQLDA的头信息包括下列内容:

- sqldaid: 存储块标志,其中固定含有字符串“SQLDA”;

- sqldabc:SQLDA存储块的总长(以字节计);

注:通常,sqldabc的值是由DESCRIBE语句设置的。但如果程序员在使用SQLDA时未调用DESCRIBE语句,则应有程序员自己负责为该域设置适当的值。

- sqln:SQLDA中可以使用的描述区块(44Byte)的数目;(SQLVARs)

- sqld:SQLDA中实际描述的条目数;

注:sqld的值可以程序执行不同的语句而改变,但必须总是小于或等于sqln。

SQLDA中每一个条目的信息包括以下内容:

- sqltype:被描述的列的数据类型(如character,decimal,date等)及该列是否允许空值(NULL);

- sqllen: 被描述的列的外部长度(以字节计)。对于decimal(十进制)类型的列,其第1个字节说明列的精度,第2个字节说明总的位数;

- sqldata:为一个无符号的字符指针,其中包含的是指向相应宿主变量的地址;

- sqlind:为一个指向short(短整型)型的指针,其中包含的是指向相应指示符变量(indicator variable)的地址(如果有的话);

- sqlname:包含了被描述的列的名称。如果被描述的列在原SELECT 列表中对应的是一个表达式,则sqlname中包含是该列在原SELECT列表中的相对位置编号。在C语言中,sqlname为一个命名的结构(struct),其中包含一个名为length的整型域表示该列名字的长度以及名为name的字符数组用于存放该列的名字。

SQLDA中条目的内容是由SQLDA的使用方式决定的。

1)如果在PREPARE或DESCRIBE语句中使用SQLDA,则得到的条

目是对查询结果集各数据列的描述,包括列的数据类型、长度、精度和名称。

2)如果是在OPEN,FETCH,EXECUTE或CALL语句中使用SQLDA,

则其中的条目将被用于描述相应语句中所需宿主变量(其中OPEN和EXECUTE语句中为输入主变量,FETCH语句中为输出主变量,CALL语句中为双向主变量)。描述的内容包括变量的数据类型、由宿主程序分配给该变量的内存地址和长度,此外还可以包含一个指示符变量的地址,用于表示空值。

3)SQLDA中每一个条目的数据类型由该条目中的sqltype域表示。

sqltype存放的是相应数据类型的编码,表X 列出了sqltype中可能出现部分编码及其对应的数据类型。

Sqltype中的编码值

表示的数据类型

长 度

448

VARCHR,NOT NULL

MAX

452

CHAR,NOT NULL

长度

480

FLOAT,NOT NULL

8

484

DECIMAL,NOT NULL

精度

位数

496

INTERGER,NOT NULL

4

500

SMALLINT,NOT NULL

2

注1:编码值为偶数时表示相应的列不能为空值,为奇数时则表示相应的列允许为空值;

注2:该表只列出了部分数据类型的编码,完整的编码可查阅《SQL参考手册》;

表X 部分数据类型的编码

4.6 SQLDA的使用

SQLDA是一种动态数据结构,其作用是在应用程序和数据库系统之间传递有关列(或宿主变量)的数目及数据类型与/或值等方面的信息。SQLDA的使用涉及应用程序和数据库系统两个方面。以下是使用SQLDA的基本流程:

1.SQLDA的定义及存储空间的分配和释放都是由应用程序负责的。所以,应用程序首先应保证为SQLDA分配足够大小的存储空间,以便能够容纳需要描述的条目信息,并负责将SQLDA可容纳的最大条目数填入sqln域中。

2.为了得到一条动态生成的SQL语句的描述信息,应用程序可通过调用含有SQLDA选项的PREPARE语句或DESCRIBE语句将相关的语句及SQLDA交给数据库系统,数据库系统负责对提交的语句进行预处理,将描述该语句实际需要的条目数填入sqld中。并且如果SQLDA的存储空间足够大(即sqln>=sqld),则数据库系统还会将该语句结果集中各个列的描述信息填入SQLDA中;否则(即sqln

3.应用程序在调用了含有SQLDA选项的PREPARE语句之后,应对sqld的值进行检查,如果sqld=0,则表明要执行的是一条非SELECT语句,这时可直接调用EXECUTE IMMEDIATE执行该语句。如果sqld>0,则表明要执行的是一条SELECT语句,这时,如果SQLDA中已有足够的条目(即sqld <= sqlm)则应按照每一条目的SQLTYPE分配相应的主变量和指示符变量,并将分配的主变量和指示符变量的地址填入SQLDATA和SQLIND中;如果SQLDA中没有足够的条目(即sqld >sqlm),则重新为SQLDA分配足够的条目,并用DESCRIBE语句获得SQL语句的描述信息,然后按照每一条目的SQLTYPE分配相应的主变量和指示符变量,并将分配的主变量和指示符变量的地址填入SQLDATA和SQLIND中。

以下是采用SQLDA处理任意动态SQL语句的程序代码片段:

……

EXEC SQL INCLUDE SQLDA; /* or #include */

……

struct sqlda *sqldaptr;

char stmt[250];

……

sqldaptr = (struct sqlda * malloc(SQDLDASIZE(n));

…….

sqldaptr->sqln = 0;

{ 在stmt中读入或生成需要执行的动态语句 }

EXEC SQL PREPARE AOK INTO :*sqldaptr FROM :stmt;

/* 对stmt中的语句进行预处理, */

/* 并将得到的结果集描述信息填入sqldaptr */

{ 检查sqlca并进行相应的错误处理 }

if (sqldaptr->sqld == 0){ /* 如果是 non-select 语句*/

EXEC SQL EXECUTE AOK; /* 则直接执行 */

return;

}

/* 如果是select 语句 */

if(sqldaptr->sqln < sqldaptr->sqld){ /* 如果实际分配的条目数少于需要描

述的条目数*/

sqldaptr = (struct sqlda * remalloc(SQLDASIZE(sqldaptr->sqld));

/*则重新为SQLDA分配足够大小的空间*/

sqldaptr->sqln = sqldaptr->sqld; /* 并重置sqln的值 */

}

EXEC SQL DESCRIBE AOK INTO :sqldaptr; /*获得结果集描述信息*/

{根据获得的结果集描述信息分配输出主变量和指示符变量,并将其地

址填入sqldaptr 中 }

EXEC SQL DECLARE C1 CURSOR FOR AOK;

EXEC SQL OPEN C1;

……

EXEC SQL FETCH C1 USING DESCRIPTOR :*sqldaptr;

…….

EXEC SQL CLOSE C1;

4.7 动态嵌入式SQL语句的限制

动态嵌入式SQL语句受到以下限制:

1.不能引用宿主程序中的变量,即不能出现宿主变量;

2.不能是操作语句的语句,即不能是EXECUTE IMMEDIATE,PREPARE,EXECUTE,DESCRIBE语句;

3.不能对游标进行操作,即不能是OPEN,FETCH,CLOSE语句;

4.不能是辅助性SQL语句,如DECLARE,INCLUDE,WHENEVER语句等;

4.8动态绑定与静态绑定的比较

1.嵌入在应用程序中的动态SQL语句的绑定(BIND),是在程序运行期间通过调用EXECUTE IMMEDIATE或PREPARE动态完成的。因此应用程序的相应时间应包括绑定动态语句所耗费的时间。而静态绑定是在程序运行之前进行的,并且其结果(即语句的执行策略)已以程序包的形式存储在数据库中。因此,应用程序运行期间只需调用相应的程序包,而不再需要为绑定花费时间。

2.与静态绑定一次将对程序中的全部静态嵌入式语句进行分析和处理不同,一次动态绑定只对单个一条语句进行分析和处理。

3.与静态绑定所产生的结果(即程序包)将被长久保存在数据库中不同,动态绑定所产生的结果(即语句的执行策略),只被临时缓存在内存中,而不再长久保存。

第五章 DB2调用层接口(CLI)

5.1 调用层接口(CLI)的特点

与使用嵌入式SQL接口的应用程序需要通过预编译程序(precompiler)将嵌入的SQL语句转换为宿主语言的语句,然后再被编译并绑定到数据库上相反,使用DB2 CLI接口的应用程序不再需要进行预编译和绑定,而是在运行时刻使用一组标准的函数来执行SQL语句和相关的服务。

上述差别的重要性在于,传统的预编译程序都是针对特定的数据库系统提供的,因此也将应用程序与特定的数据库系统紧密地结合在一起。而使用DB2 CLI将使我们可以编写出不依赖任何特定数据库的可移植的应用程序。这种可移植性意味着,用DB2 CLI编写的应用程序只需要在运行时刻连接到适当的数据库上,即可对不同的数据库(包括DRDA数据库)进行访问,而不再需要做任何重新编译或重新绑定。

5.1.1 嵌入式SQL与CLI的比较

CLI应用程序与嵌入式SQL应用程序相比较具有以下差别:

l CLI应用程序不需要显式地说明游标(cursor)。在CLI接口中,游标是根据需要由系统内部自动生成的,应用程序可以按照通常的游标读取模式使用系统提供的游标。

l CLI应用程序中不再使用OPEN语句。作为替代,SELECT语句的执行将会自动打开一个游标。

l 在CLI应用程序中,事务的提交(commit)和回滚(rollback)是通过调用SQLTransact()函数完成的,而不再是通过传递SQL语句完成的。

l CLI应用程序可以在运行当中管理与语句相关的信息,并提供了语句句柄(statement handle)将语句作为一个抽象对象进行引用。这种句柄减轻了应用程序处理特定数据结构的负担。

l 与语句句柄相似,CLI还提供了环境句柄(environment handle)和连接句柄(connection handle),从而使应用程序可以对所有全局变量和连接相关信息进行引用。

l CLI使用的状态返回码(SQLSTATE values)是由X/OPEN SQL CAE 规范定义的。尽管其格式及大部分状态码与IBM关系数据库产品所使用状态码是一致的,但两者之间仍然存在一些差别。

5.2 DB2 CLI 句柄和程序结构

5.2.1 CLI句柄

为了阅读或编写CLI应用程序,就必须首先理解CLI句柄(handle)这一概念。简单地讲,CLI句柄是一个简化的long型C变量,表示由CLI实现所管理的各种对象(环境、连接和语句)的系统内部信息,。CLI支持3种类型的句柄:

1. 环境句柄(environment handle)

表示应用程序的整体环境,如各种属性、应用程序的连接等。每一个CLI应用程序在启动时都要用SQLAllocEvn()函数分配一个环境句柄,它将在以后分配连接句柄时用到,并在程序结束时用SQLFreeEvn()函数释放所有的环境句柄,而在程序运行中对环境句柄的访问则较少。

2. 连接句柄(connection handle)

连接句柄记录了有关数据库的连接的各种信息,如连接选项、状态、事务状态和诊断信息等。DB2 CLI允许一个CLI应用程序连接多个数据库,或者与同一个数据库连接多次,每次连接都使用不同的连接句柄。利用连接句柄可提交(commit)或回滚(rollbak)相应数据库中的事务。

3. 语句句柄( statement handle)

表示SQL语句的执行状态,一个CLI应用程序可同时分配多个语句句柄,也可通过重复使用一个语句句柄来处理多条SQL语句。语句句柄是一种功能很强的对象,其作用是将嵌入式SQL程序中的SQLCA(系统返回码和状态信息)、SQLDA(数据类型及相关的主变量)等数据结构中的信息与游标(结果集中的当前位置)组合在一起。由于这些信息已由语句句柄加以表示,所以CLI应用程序不再需要去直接管理这些数据结构。应用程序通过调用CLI函数来执行SQL语句,并使用其句柄来获得该语句执行的有关信息。语句句柄可在下列方式中使用:

(1) 检索与该语句执行有关的出错代码和信息;

(2) 检查所执行的SQL语句是否为查询语句,如果是查询语句,则可检索其结果集中各个列的数据类型。

(3) 告诉系统在哪里交付查询结果,每次取出结果集中的一行。

(4) 像游标(cursor)一样,维护结果集中当前行的位置,并可为其它SQL语句修改或删除当前行提供一个名字(称为游标名)。

(5) 一个CLI应用程序可分配一个环境句柄、一个或多个连接句柄及

一个或多个语句句柄,并且当不再需要时应及时释放这些句柄。

5.2.2 CLI程序的基本结构

如图所示,一个CLI程序主要由:初始化、事务处理和结束等三个阶段组成。

初 始 化

其中:

*初始化阶段:对各项资源进行初始化,为事务处理阶段做准备;

clip_image010clip_image011

事务处理

*事务处理阶段:这是程序的主要部分,它通过

clip_image012clip_image013
SQL语句对数据库进行查询、修改等操作;

*结束阶段:释放各项资源;

clip_image014

结 束

5.3 CLI应用的配置

配置文件db2cli.ini可用于指定各种选项来控制CLI的行为。每一个配置参数都有一个系统范围的缺省值,如果在db2cli.ini中没有包含该配置参数,就使用其系统缺省值。此外,db2cli.ini中的许多配置参数也可被单个CLI命令所覆盖。例如,事务隔离级()的系统缺省值为“游标稳定的”(CS)。对于一台特定的计算机,可以用一个db2cli.ini文件为该计算机上运行的全部CLI程序建立一个缺省的事务隔离级。一个CLI应用程序可通过SQLSetConnectOption()函数得到或设置指定连接数据库上的事务隔离级。

Db2cli.ini文件在Windows系统中,将放在sqllib/win目录下;在Unix系统中,将放在sqllib/cfg目录下。

以下是db2cli.ini文件中常用的一些CLI配置参数(完整的CLI配置参数列表可参见DB2 UDB Call Level Interface Guid and Reference):

AUTOCOMMIT:控制在执行完每条SQL语句后是否自动执行一次提交。

系统缺省值为ON;

CONNECTTYPE:控制由CLI应用程序所取得的数据库连接的类型。系统

缺省值为1,它表示应用程序只能同时连接到一个数据

库上;

CURSORHOLD:控制在执行了COMMIT语句之后,打开的游标是否仍保

持打开。系统缺省值为保持游标仍打开。不需要该特性的

应用程序可这一缺省设置,以提高性能;

DB2OPTIMIZATION:控制对CLI应用程序所发出的SQL语句进行处理时

所采用的优化级别。系统缺省值为5;

TXNISOLATION:控制CLI应用程序的事务隔离级。系统缺省值为游标稳

定的(CS)。

5.4 常用CLI函数总结

下面按相关功能类别列出DB2 UDB 所支持的全部CLI函数。有关这些函数的调用参数和说明可参见《DB2 Call Level Interface Guide and Reference》。

1.分配和释放句柄的函数

SQLAllocEnv():分配一个环境句柄。

SQLAllocConnect():分配一个连接句柄。

SQLAllocStmt():分配一个语句句柄。

SQLFreeEnv():释放一个环境句柄。

SQLFreeConnect():释放一个连接句柄。

SQLFreeStmt():释放一个语句句柄。

2.控制数据库连接的函数

SQLConnect():使应用程序连接到一个特定数据库。

SQLDriverConnect():将应用程序连接到一个特定数据库后,给用户某些提

示信息,如用户标识和口令。

SQLDisconnect():关闭一个数据库连接。

SQLSetEnvAttr():设置控制选项,例如设置一个环境句柄范围内全部数据

库的连接类型。

SQLGetEnvAttr():检索由SQLSetEnvAttr()所设置的选项的当前值。

SQLSetConnectOption():设置控制选项,例如设置一个数据库连接的隔离

级或是否自动提交。

SQLGetConnectOption():检索一个数据库连接选项的当前值。

SQLSetConnection():用于指定一个连接数据库,该数据库为嵌入在CLI

应用程序中的静态SQL语句所使用。

3. 为执行并得到结果描述而对一条SQL语句进行预处理的函数

SQLPrepare():为随后执行而预处理一条语句。

SQLNumResultCols():如果被处理的语句为一查询语句,则返回其结果集

所含的列数。

SQLDescribeCol():描述结果集中一个特定的列。

SQLColAttributes():检索结果集中某一列的指定属性,如列的名称、数据

类型或长度。

SQLSetColAttributes():向系统通知某一列的已知属性,该属性不再需要从

数据库服务器上得到。

SQLNativeSql():接收一条SQL语句并返回它,同时将其转换成所要送达

的数据库服务器的本机形式。该转换处理用于消除SQL

语句对本地系统的依赖性。该函数只返回所转换的语句但

不执行它。

4. 处理参数标记的函数

SQLNumParams():返回所处理的SQL语句中的参数标记的数目。

SQLBindParameter():将一个参数标记与一个宿主程序变量相关联。该函

数的旧版本名为SQLSetParam()。

SQLParamData(),SQLPutData()和SQLCancel()可一起使用从一个CLI程序

发送大量参数值到数据库系统。

5. 执行一条SQL语句并测试其结果的函数

SQLExecute():执行一条以前已预处理过的语句。

SQLExecDirect():在一步当中预处理并执行一语句。

SQLRowCount():返回由一语句所插入、删除或修改的行数。

SQLError():返回在处理一语句时所遇到的错误的信息。

SQLGetSQLCA():返回由于处理一语句而产生的SQLCA结构。SQLCA

中的许多信息也可由SQLError()或SQLRowCount()获

得。

SQLSetStmtOption():控制附属于一语句执行的某些选项,如限制返回结果

集的行数。

SQLGetStmtOption():检索某一语句选项的当前值。

6. 处理查询结果的函数

SQLBindCol():告诉CLI将结果集中某一列的值送到哪儿,以及在列值上

执行什么样的类型转换。如果列的数据类型是Blob,Clob

或Dbclob,则SQLBindCol()可指定系统按定位符

(Locator)的形式交付列值。

SQLFetch():将结果集中的一行数据读入宿主程序中由SQLBindCol()所指

定的地方。

SQLExtendedFetch():将结果集中的多行数据读入宿主程序中一个或多个

数组当中。

SQLGetData():从结果集的当前行中读取单个列值,并可按片段读取大数

据(LOB)的值。

SQLSetCursorName():将一个游标名与一个语句句柄相关联,用于指定删

除或修改的位置。如果没有为语句句柄指定游标

名,则系统将自动生成一个。

SQLGetCursorName():返回与某语句句柄相关联的游标名。

7. 处理大对象(LOB)的函数

SQLGetLength():返回字符串值的长度。该函数可用于测量由定位符表示

的大对象数据的长度。

SQLGetSubString():返回一LOB型字符串值的一个片段,该字符串值由大

对象定位符表示。该函数的返回结果可以是一具体字

符串或另一定位符。该函数可用于推迟大对象的具体

化。

SQLGetPosition():返回一字符串在另一字符串中出现的位置。被查找的字

符串必须由定位符表示,要查找的字符串可以由定位符

或直接量表示。该函数对于在大对象中按位置或按字符

搜索匹配很有用。

SQLBindFileToParam():用于将SQL语句中的参数标记与包含大对象的文

件相关联。当SQL语句被执行时,将用相应文件

的内容替换参数标记。

SQLBindFileToCol():用于取回查询结果。该函数可引导系统将一个大对

象列的值送到一个文件中而不是送到一个程序变量

中。

8. 管理事务的函数

SQLTransact():用于提交或回滚一事务。对于CLI应用程序而言,一个事

务可以涉及多个数据库连接,也可以为每一数据库连接保持

一个独立的事务。程序可独立地提交或回滚这些事务。

注:在CLI中,事务的提交或回滚是由函数调用完成的,而不是由SQL

语句完成的。此外还应注意自动提交选项的作用,该选项由db2cli.ini

文件中的相应配置参数或SQLSetConnectOption()控制,一旦被打

开,则系统将自动提交每一SQL语句。自动提交选项的缺省值为打

开,如果应用要自己控制事务的提交或回滚,则需将其关闭。

9. 查询系统编目表的函数

注:下列这些函数都将执行一个查询,其结果可以用SQLFetch()函数或者

其它可以对结果集进行操作的函数得到

SQLTables():列出存储在指定数据库中的所有表名(包括视图名和别名)。

SQLColumns():列出指定表的所有列名及其数据类型。

SQLForeignKeys():列出某个表的外码包含的所有列名。

SQLPrimaryKeys():列出某个表的主码包含的所有列名。

SQLSpecialColumns():列出被包含在一个主码或一个唯一索引中的列名。

SQLStatistics():返回一个需要维护的表上有关行数及索引的信息。

SQLTablePrivileges():列出用户对数据库中各个表所拥有的权限。

SQLColumnPrivileges():列出用户对数据库中各个列所拥有的权限。

SQLProcedures():根据系统编目表DB2CLI.PROCEDURES中的信息,列出指定数据库中的存储过程。

SQLProcedureColumns():列出与一存储过程相关连的输入输出参数。

10. 获得可用数据库及服务器信息的函数

SQLDataSources():列出一应用程序可使用的数据库。

SQLGetInfo():返回有关CLI应用程序所连接的数据库系统的功能性一般信息。

SQLGetFunctions():返回一指定数据库服务器所支持的特定CLI函数的信息,这些信息可使应用程序具有更好的可移植性。

SQLGetTypeInfo():返回指定数据库服务器所支持的数据类型的信息。

11. 用于处理输入输出值集合的函数

SQLParamOptions():将一个参数标记与宿主语言中的一个数组相关连,使给定的SQL语句可利用数组中的参数值执行多次。

SQLMoreResults():在使用参数数组的SQL查询被执行多次的情况下,用于从一个结果集前进到下一个结果集。在每一结果集中可用SQLFetch()取出单个行。SQLMoreResults()还可用于由一个存储过程返回多个结果集时,从从一个结果集前进到下一个结果集。

第六章 存储过程(Stored Procedure)

6.1 存储过程的概念

通常情况下,在客户-服务器体系结构中,运行在客户端的应用程序通过SQL对服务器端的数据库进行访问时,每一条SQL语句是分别地、一句一句地从客户端向服务器发出请求,然后数据库服务器再将结果一个一个地返回给应用程序。但是,有一些应用程序,涉及的服务器端数据库处理活动很多,而与用户的交互活动较少,在这种情况下,将有关数据库的处理活动以储存过程的形式放在数据库服务器上完成,则可以大大减轻网上传输流量,提高应用程序性能。储存过程的概念如下图所示。

clip_image015

图6-1 普通程序的执行过程

clip_image016

图6-2 存储过程应用的体系结构

使用储存过程的应用程序具有以下优点:

l 减少网上传输流量;

l 改善服务器处理密集型应用的性能;

l 可以访问数据库服务器特有的功能特性;

l 易于维护;

6.2 存储过程的程序结构

一个完整的使用储存过程的应用程序由两部分组成:一是储存过程本身,它被存放在数据库服务器端并数据库服务器上运行;二是对存储过程进行调用的客户端应用程序,它在客户端上运行。客户端应用程序与服务器端储存过程分别运行在两个不同的进程空间中,并且有不同的功能划分。

客户端应用程序的主要功能是:

l 为有关数据结构和主变量定义、分配并初始化存储空间;

l 与数据库进行连接;

l 通过SQL CALL语句调用存储过程

l 完成事务的提交(COMMIT)和回滚(ROLLBACK)(注:除非采用分布式工作单元,服务器端的存储过程也可以完成事务的提交和回滚);

l 执行CONNECT RESET语句;

服务器端储存过程的主要功能是:

l 接收客户端应用程序传送的SQLCA和SQLDA等数据库结构;

l 作为与客户端应用程序相同的事务在数据库服务器上运行;

l 向客户端应用程序返回SQLCA和其它输出数据;

6.3 客户端应用程序

客户端应用程序在使用CALL语句调用存储过程之前必须先执行几个步骤。首先,必须连接数据库,声明、分配并初始化SQLDA结构或宿主变量。

提示:不要在数据库服务器上分配这些变量结构。数据库管理系统将根据客户端应用程序中的结构在服务器端自动分配所需的存储。

客户端应用程序必须保证在调用存储过程之前已建立了数据库连接,否则,将会产生错误。

6.3.1 CALL 语句

客户端应用程序可通过CALL语句向服务器发出存储过程调用请求。CALL的语法格式如下所示:

CALL {过程名|主变量名}{(主变量1,…,主变量N)|USING DESCRIPTION 描述符名}

CALL语句的作用是将调用参数通过一个SQLDA数据结构从客户端传送给服务器端的存储过程,存储过程执行后再将输出结果通过同一个SQLDA返回给客户端应用程序。

1. 有关被调用存储过程的名称的规定

1) 在客户端应用程序中,被调用的存储过程的名称既可以直接给出,也可以通过一个主变量给出。

2) 如果被调用的存储过程的名称是直接给出的,则它必须是一个通常的标识符(identifier)并且长度不能超过254个字节。由于通常的标识符中不能含有空格或其它特殊字符,所以,如果被调用的存储过程的名称中必须包含有空格或其它特殊字符时,则只能使用主变量来给出其名称。

3) 如果是通过主变量给出被调用的存储过程的名称,则使用的主变量必须是一个具有长度属性的字符串型的变量,并且其长度不能超过254个字节。

4) 被调用的存储过程的名称可以采用下列几种格式:

l proname:表示要装入名为proname 的存储过程函数库并执行其中名为proname的函数;

l proname!funcname:表示要装入名为proname 的存储过程函数库并执行其中名为funcname的函数;

注:按照缺省方式,在基于UNIX的平台上,数据库管理系统将在INSTHOME/sqllib/function目录中查找被隔离的(fenced)存储过程函数库。而在Intel平台上,存储过程函数库的搜索位置则由操作系统环境变量LIBPATH说明。对于不加隔离的(unfenced)存储过程函数库,其搜索位置分别是INSTHOME/sqllib/ unfenced(基于UNIX的系统)或sqllib/dll/unfenced(基于Intel的系统)。

l 绝对路径:如/home/user1/procname!funcname即表示要装入/home/user1目录下的名为proname 的存储过程函数库并执行其中名为funcname的函数;

2. 通过宿主变量传递过程调用参数

以下是一段通过宿主变量传递过程调用参数的程序例子:

EXEC SQL BEGIN DECLARE SECTION;

char host_var1[15];

float host_var2;

short ind_var2;

long host_var3;

short ind_var3;

char procname[254] = “myproc”;

EXEC SQL END DECALRE SECTION;

strcopy(host_var1,”new data”);

host_var2 = 17.6;

ind_var2 = 0;

ind_var3 = -1;

EXEC SQL CALL :procname

(:host_var1,:host_var2 :ind_var2,:host_var3 :ind_var3);

如上面的程序段所示,在调用存储过程时,可以通过宿主变量向存储过程传递调用参数。存储过程的调用参数可分为只输入的(input only)、只输出的(output only)和既输入又输出的(both input and output)。如果调用参数没有明确被说明为只输入的(input only)或只输出的(output only),则CALL语句将其缺省处理为既输入又输出的(both input and output)的调用参数。调用参数的输入输出类型由相应的指示符变量确定。对于只输入的(input only)的调用参数其相应指示符变量的值应为0(例如,上面程序段中的host_var2);对于只输出的(output
only)的调用参数其相应指示符变量的值应为-1( 例如,上面程序段中的host_var3);

3. 通过SQLDA传递过程调用参数

以下是一段通过SQLDA传递过程调用参数的程序例子:

struct sqlda *inout_sqlda = (struct sqlda *)malloc(SQLDASIZE(3));

long host_var3;

short ind_var3 = -1;

short ind_var2 = 0;

inout_sqlda->sqln = 3;

inout_sqlda->sqld = 3;

inout_sqlda->sqlvar[0].sqltype = SQL_TYPE_CSTR;

inout_sqlda->sqlvar[0].sqllen = 16;

inout_sqlda->sqlvar[0].sqldata = (char *)malloc(inout_sqlda->sqlvar[0].sqllen);

strcpy(inout_sqlda->sqlvar[0].sqldata,”new data”);

inout_sqlda->sqlvar[1].sqltype = SQL_TYPE_NFLOAT;

inout_sqlda->sqlvar[1].sqllen = sizeof(float);

inout_sqlda->sqlvar[1].sqldata = (char *)malloc(inout_sqlda->sqlvar[1].sqllen);

*(float *) inout_sqlda->sqlvar[1].sqldata = 17.6;

inout_sqlda->sqlvar[1].sqlind = &ind_var2;

inout_sqlda->sqlvar[2].sqltype = SQL_TYPE_NINTEGER;

inout_sqlda->sqlvar[2].sqllen = sizeof(long);

inout_sqlda->sqlvar[2].sqldata = (char *)&host_var3;

inout_sqlda->sqlvar[1].sqlind = &ind_var3;

EXEC SQL CALL myproc USING DESCRIPTOR :*inout_sqlda;

如上面的程序段所示,在调用存储过程时,也可以通过SQLDA向存储过程传递调用参数。

6.3.2 创建存储过程

以下是通过CREATE PROCEDURE语句创建存储过程的例子:

CREATE PROCEDURE MYPROC(INOUT HOST1 CHAR(15),

IN HOST2 DOUBLE, OUT HOST3 INTEGER)

EXTERNAL NAME ‘/home/user1/myfn!fn1’

LANGUAGE C

PARAMETER STYLE DB2DARI

1)CREATE PROCEDURE语句的作用是向数据库服务器注册一个新的存储过程;

2)MYPROC为存储过程的指定名。客户端应用程序可以在CALL语句使用这一名字调用相应的存储过程;

3)INOUT HOST1 CHAR(15)表示存储过程中所需的一个调用参数。其中,参数输入输出类型INOUT表明该参数既可向存储过程提供输入信息,也可接收从存储过程返回的信息。参数输入输出类型IN表示相应的参数只用于输入(input only);参数输入输出类型OUT表示相应的参数只用于输出(output only);

4)在同一个模式下,不允许定义名称、参数个数和数据类型都完全相同的存储过程;

5)EXTERNAL NAME(外部名)表示用户为实现所定义的存储过程而编写的程序代码段的名称;

6)LANGUAGE C 在CREATE PROCEDURE语句中是必不可少的,其作用是指明存储过程体的语言接口约定。该子句的另一个选项是LANGUAGE JAVA;

7)PARAMETER STYLE 的作用是说明向存储过程传递参数以及从存储过程接收返回结果的有关约定。DB2DARI表明存储过程使用的参数传递约定将与C语言的函数调用和连接约定相兼容;PARAMETER STYLE DB2DARI必须与LANGUAGE C一起使用。DB2GENERAL表明存储过程使用的参数传递约定将与JAVA语言的函数调用和连接约定相兼容;PARAMETER STYLE DB2GENERAL必须与LANGUAGE JAVA一起使用;

6.4 服务器端存储过程的实现
6.4.1实现存储过程的函数定义

在UDB中,存储过程的实现代码一般可应用某种编程语言(如C、C++或JAVA等)来编写。下面以C语言为例,说明在编写存储过程的实现代码时应注意的事项。

SQL_API_RC SQL_API_FN

myproc(void *reserved1,

void *reserved2,

struct sqlda * inout_sqlda,

struct sqlca *ca)

{

/* no connecttion related statements */

/* runs in background */

/* no command to terminate current process *./

/* (no exit, _exit, or at exit) */

/* if DUOW no COMMIT or ROLLBACK */

return(ret_value);

}

1)上面程序段开头的SQL_API_RC和SQL_API_FN为2个预定义的宏,目的是程序的可移植性;

2)在实现存储过程的函数体中不能出现与数据库连接相关的语句,即不能出现CONNECT,CONNECT RESET,CREATE DATABASE,DROP DATABASE,ALTER DATABASE,BACKUP,RESTORE,ROLLFORWORD等语句;

3)由于存储过程只在后台运行,所以不允许有写屏幕(如,printf)的动作,但允许写文件(fprintf);

4)存储过程的函数体只是一个被数据库管理系统调用的程序例程,所以当执行结束时总是应当将控制交还该调用它的函数,而不应终止当前进程,即在其函数体中不应出现exit(),_exit()这样的函数调用;

5)如果调用存储过程的客户端应用程序的数据库连接类型(CONNECT TYPE)为DUOW,则存储过程中不能发出终止事务的语句,即无论动态或静态的COMMIT、ROLLBACK都不允许出现。

6.4.2 存储过程的参数传递

SQL_API_RC SQL_API_FN

myproc(void *reserved1,void *reserved2,

struct sqlda *inout_sqlda, struct sqlca *ca)

{

struct sqlca sqlca; /*for local use */

/* use input data in SQLDA */

/* do not change SQLD,SQLTYPE, or SQLLEN */

/* do not change pointer for SQLDATA or SQLIND */

/* return data in SQLDATA(and SQLIND) */

memcpy(ca,&sqlca,sizeof(struct sqlca));

1)存储过程将通过SQLDA中的输入变量得到客户端应用程序传来的输入参数,然后再通过SQLDA中的输出变量将输出结果返回给客户端应用程序。由于SQLDA各个域的原始值都是由客户端应用程序在过程调用前设置的,因此,存储过程的函数体不应改变SQLDA中的SQLD,SQLTYPE,SQLLEN等域的值。此外,虽然存储过程的函数体可以改变SQLDATA及SQLIND中所含指针指向的变量的值,但却不应改变SQLDATA及SQLIND中所含的指针。

注:SQLDA中的变量可以同时既是输入变量又是输出变量。

2)在存储过程的函数体返回之前,应当显式地将本地SQLCA中的信息拷贝到存储过程的SQLCA参数之中。

6.4.3存储过程的返回值

SQL_API_RC SQL_API_FN

myproc(void *reserved1,void *reserved2,

struct sqlda *inout_sqlda, struct sqlca *ca)

{

/* processing */

/* in this application,the second SQLVAR field

is used to determine if the client intends to

call the server procedure again, A value of 0

means no further calls. */

if((*float *)inout_sqlda->sqlvar[1].sqlda != 0)

return(SQLZ_HOLD_PROC)

else

return(SQL_DISCONNECT_PROC);

}

1) 应当特别注意的是,存储过程的返回值根本不会返回给客户端应用程序。这里所说的返回值的作用是使数据库管理系统能够确定当存储过程执行终止时是否将存储过程从内存中释放掉。

2) 存储过程可以向数据库管理系统返回下列2个值:

—SQL_DISCONNECT_PROC:其含义是告诉数据库管理系统当所有信息都传递给客户端后,即可释放(或卸载)存储过程及其数据存储;

—SQL_HOLD_PROC:其含义是告诉数据库管理系统将存储过程库函数仍然保持在内存当中,这样将可以保证当客户端下一次发出对该存储过程的DARI调用时被调用的库函数已经在内存当中了,从而提高系统性能。

3) 如果客户端只对存储过程调用一次,则应返回SQL_DISCONNECT_PROC;

4) 如果客户端需要对存储过程调用多次,则应返回SQL_HOLD_PROC,从而保证存储过程不会被卸载;

5) 如果存储过程以SQL_HOLD_PROC作为返回值,则当其被最后一次调用时则应以SQL_DISCONNECT_PROC作为返回值,从而保证最后一次调用后将存储过程从内存中释放掉。否则该存储过程将被一直保存在主存中,直到数据库管理系统停止运行;

客户端应用程序在对存储过程进行最后一次调用时,应将这一信息通知被调用的存储过程。

第七章 大对象(LOB)的处理

7.1 大对象数据的类型和特征

LONG VARCHAR和LONG GRAPHIC型的数据有一个存储限制,即最大不能超过32K,这只适合于存放一般中等规模的文本数据。而随着多媒体类型应用的大量出现,许多应用不仅需要存储更大容量的数据,而且需要存储多种特殊类型的数据,如声音、影象、视频、图象、混合文本等。这些数据不仅结构复杂,而且容量变化范围也极大。为此,DB2 UDB提供了三种大对象(简称LOB)数据类型,以满足上述要求。

由于,每一个数据库表中都可以包含大量的大对象数据,其中,单一大对象数据最大可以达到2GB,单个一行中最大可以包含24GB的大对象数据,而一个表中最大可以包含4TB的大对象数据。因此,大对象数据的存储、使用、维护和操作都需要加以特殊考虑。

与其他数据类型相似,我们也可以使用宿主变量来存放并操作大对象数据。然而,使用宿主变量维护大对象数据往往需要占用大量的系统内存,而很多情况下,可用的内存容量根本就无法满足大对象数据的存储要求。因此,必须采用适当的方法解决这些问题。下面介绍的大对象数据定位器(LOBs Locator)将能够有效地操作和管理大对象数据,而文件引用变量(File Reference Variable)则能有效地实现大对象数据在数据库服务器和客户机之间的双向传输。

大对象数据包括以下三种类型:

1. BLOB(二进制大对象)

BLOB可用于存储诸如影象(image)或声音(voice)这样大容量的二进制数据。该数据类型可包含多至2G字节的二进制数据,BLOB不能与其它类型的值进行赋值或比较。

2. CLOB(字符型大对象)

CLOB可用于存储大量的单字节字符(或称正文)数据。该数据类型可包含多至2G字节的单字节字符数据,每个CLOB都有一个代码页(codepage)与其相关连。CLOB可与其它字符串类型(char,varchr,long varchar)的值进行赋值或比较。

3. BDCLOB(双字节字符型大对象)

DBCLOB可用于存储大量的双字节字符数据。该数据类型可包含多至1个G的双字节字符数据,仅当数据库建立时被配置为双字节数据才可使用DBCLOB,每个CLOB都有一个双字节代码页(codepage)与其相关连。CLOB可与其它双字符串类型(graphic,vargraphic,long vargraphic)的值进行赋值或比较。

与普通数据类型相比,LOB除了需要占用大量存储空间,还有以下特征:

(1) DB2允许在一行数据中出现多个LOB列。事实上一单个数据行中最多可以包含24GB的LOB数据。

(2) 应用程序可通过定位符(locator)对LOB数据进行操作。定位符可用来表示LOB的值,但并不真正包含LOB的数据。通过操作定位符,应用程序可以推迟或避免将LOB数据调入内存。

(3) 通过使用文件引用变量(file reference),应用程序可直接从文件中将LOB数据输入到数据库中,或直接将从数据库中获取的LOB数据输出到文件中,而不需要通过应用程序的内存缓冲区传送数据。

(4) 在建表时,可对表中的每一个LOB类型的列独立指定对于该列 数据的改变是否要记录在系统日志中。

7.2 大对象数据的操作方式

在应用程序中访问号 和处理大对象数据有以下三种方式:

1.将整个大对象数据送入宿主变量中进行处理,即将 个大对象从服务器调入客户端内存中。

2.使用大对象定位器,即数据仍然存放在服务器端,而定位器移到客户端;

3.使用文件引用变量,即将整个大对象传输到客户端的文件中,而不是内存中。

在程序中选择合适的处理方式可以大大提高大对象的处理效率。

7.3 LOB定位符的使用

大对象定位器的作用就在于一个简短的变量表示一个大对象,通常只需要用4个字节就可以表示任何大对象。由于大对象定位器表示的 是一个大对象,而不是一个数据行,因此,当对定位器所指向的大对象所在数据行进行操作时,定位器本身并不会受到影响。定位器的值一旦被声明,将一直保持有效,直到本工作单元结束或被显式释放。

使用大对象定位器时,大对象数据在被真正操作前并不移入内存,即使要移入内存也可以只移入相关的那一部分数据,这意味着大对象的 I/O将得到优化。

由此可以看出大对象定位器适用于以下几种情况:

1、程序中只用到大对象数据中很少一部分内容;

2、大对象的 大小已超过可用的内存空间;

3、程序只需要得到大对象的暂时 描述,而不需要对其进行任何操作;

4、最关注的是程序的运行效率;

在程序使用大对象数据一般需要以下步骤:

1、声明宿主变量和定位器,例如:

EXEC SQL BEGIN DECLARE SECTION;

long startper;

long startaca;

SQL TYPE IS CLOB_LOCATOR loc1;

SQL TYPE IS CLOB_LOCATOR loc2;

EXEC SQL END DECLARE SECTION;

2、将大对象取至定位器,例如:

EXEC SQL SELECT BIO INTO :loc1 FROM TAB WHERE …

3、通过 定位器操作大对象,例如:

EXEC SQL VALUES(POSSTR(:loc1,’Personal Info’) INTO :startper;

EXEC SQL VALUES(POSSTR(:loc1,’Academic Info’) INTO :startaca;

EXEC SQL VALUES(SUBSTR(:loc1,1,:startper-1)||

SUBSTR(:loc1,1,:startaca)) INTO :loc2;

EXEC SQL INSERT INTO NEWTAB(Info) VALUES(:loc2);

4、根据需要将大对象数据移入内存;

5、释放大对象定位器,,例如:

EXEC SQL FREE LOCATOR :loc1,:loc2;

7.4 文件引用变量的使用

文件引用变量与宿主变量十分相似,其不同之处在于文件引用变量的 用途是将数据放在文件中,而不是内存中。即文件引用变量表示的是一个外存文件,数据的查询、更新、插入都在文件中进行。

对于对对象数据而言,文件是一个较为适合的存储容器;而文件引用变量的优点在于不必提供一般意义上的文件读、写操作,而只需定义文件的相关属性并通过SQL语句进行操作。在应用程序中使用文件引用变量处理大对象数据一般需要以下几个步骤:

1、声明宿主变量和文件引用变量,例如:

EXEC SQL BEGIN DECLARE SECTION;

short indvar;

SQL TYPE IS CLOB_FILE biograph;

EXEC SQL END DECLARE SECTION;

其中,变量声明 SQL TYPE IS CLOB_FILE biograph;将自动被DB2预编译器替换为:

struct(

unsigned long name_length;

unsigned long data_length;

unsigned long file_option;

char name[255];

} biograph;

2、设置文件引用变量的属性,包括文件名,文件名的长度及文件选项,例如:

……

strcpy(biograph.name,”PATTI.TXT”);

biograph.name_length = strlen(biograph.name);

biograph.file_option = SQL_FILE_OVERWRITE;

……

其中,文件选项可以取以下值:

SQL_READ_FILE(输入):文件可以打开、读并关闭;

SQL_FILE_CREATE(输出):创建新文件。如果文件已存在,则出错;

SQL_FILE_OVERWRITE(输出):如果指定的文件已存在,则将其中的数据覆盖,否则,创建新文件;

SQL_FILE_APPEND(输出):如果指定的文件已存在,则将数据附加在原有数据之后,否则,创建新文件;

第八章 用户定义类型(UDT)和用户定义函数(UDF)

8.1 UDT的概念

虽然DB2中已定义了各种基本的数据类型,但大量的应用往往需要更多特殊的数据类型。因此,DB2提供了用户自定义数据类型(User Defined Type,UDT)来满足上述要求。UDT使得用户可以不受系统提供的数据类型的限制,而不断增加定义自己需要的各种数据类型。

使用UDF可以为应用开发带来以下几点好处:

1、扩展性

使用自定义的数据类型 ,用户可以无限制地增加新的数据类型以满足应用开发的各种需求;

2、灵活性

通过使用用户定义大函数(UDF)可以为 自定义的数据类型定义特殊的语法和行为;

3、一致性

对于某个特定的自定义数据类型,DB2可以确保只有其实例才能使用相关的自定义函数;

4、封装性

自定义的数据类型只能由其函数和操作进行处理,从而确保了应用程序的封装性;

5、易管理

自定义数据类型对数据库系统而言十分容易管理,由于UDT是一种完备的数据类型,具有和其他数据类型相似的内部表示,因此,同样可以建立索引并进行相互比较;

6、面向对象的基础

自定义数据类型体现了面向对象技术的主要特征,因此是实现面向对象数据库系统的关键。

8.2 创建UDT

语句CREATE DISTINCT TYPE 可以创建新的自定义数据类型。使用CREATE DISTINCT TYPE语句时必须注意:

1、自定义数据类型的源类型必须是DB2的内嵌数据类型,而不能是以前已定义过的其他自定义数据类型;

2、WITH COMPARISONS子句将使的自定义数据类型的值之间可以进行比较运算,但其源类型必须是可相互比较的;例如:

CREATE DISTINCT TYPE US_DOLLAR AS DECIMAL(9,2)

WITH COMPARISONS;

CREATE DISTINCT TYPE GERMAN_MARK AS DECIMAL(9,2)

WITH COMPARISONS;

以上两个语句定义了 US_DOLLAR(美元)和 GERMAN_MARK(德国马克)两种自定义的数据类型,虽然它们都源于DECIMAL(9,2),但DB2不允许这两种类型之间进行比较运算。要在这两种类型之间进行比较,必须首先将它们转换为同一种类型,例如根据汇率将德国马克转换为美元。

8.3 数据类型之间的比较运算

比较运算是SQL语言中最常见的运算之一,在进行逻辑判断、算术比较或进行MIN、MAX、DISTINCT、GROUP BY、ORDER BY等操作时都需要使用比较运算。

两个值之间进行比较运算的基本原则是它们所属的数据类型之间具有可比性。

8.4 类型转换(CAST)

通常情况下,应用程序会要求将一种数据类型的值转换为另一种数据类型的值,或转换为同一类型,但数据长度、精度、小数点位数不同的值。DB2提供了数据类型转换功能。能够被转换为另一种类型的数据类型又称为可转换的类型。

不同类型(包括用户自定义的类型)之间的转换可以显式地用CAST函数进行,也可以隐式地进行。此外,当创建用户定义函数时,源函数的参数类型必须是可转换的。

需要注意的是用户自定义数据类型的转换。DB2支持以下形式的UDT类型转换:

1、从自定义类型DT转换到源类型S;

2、从DT的源类型S转换到DT;

3、如果自定义类型A可转换为DT的源类型S,则A可转换为DT;

4、从整型转换到源类型为短整型的自定义类型;

5、从变长字符串型转换到源类型为字符串的自定义类型;

6、从变长图形字符串型转换到源类型为图形字符串的自定义类型;

8.5 用户定义函数的概念

8.5.1 SQL函数的分类

在DB2系统中,SQL语句中可以直接调用的函数以下几类:

1. 内部函数(built_in function)

是指建立在UDB系统代码中的那些函数,包括:

(1)算术运算符和字符串运算符:如+、-、*、/、=、||等;

(2)纯量函数:如substr,concat,length,days,sign等;

(3)列函数:如avg,count.min,max,stdev,sum,variance等;

内部函数全部定义在SYSIBM模式下。此外,UDB支持的其它许多函数被定义在SYSFUN模式下,虽然这些函数与系统一起装载,但它们并不是由系统程序直接实现的,而是作为一种预定义的外部函数实现的,即这些函数采用了与用户自定义的函数(UDF)相同的定义机制。

2. 系统生成的函数(system generated function)

是指当一个用户定义的类型(UDT)被创建时,由系统自动生成的那些函数。这些函数将可在该用户定义的类型(UDT)所属的模式中找到。系统生成的函数包括该用户定义类型(UDT)的转换函数和比较运算符。

3. 用户定义的函数(UDF)

是指由用户通过CREATE FUNCTION语句显式创建并命名的函数,其语义也由用户自己确定。用户定义的函数可进一步分以下为2类:

(1)有源函数(sourced function)

是指利用一个已有的函数(称为源函数(source function))而创建的函数。一个有源函数可以是一个运算符、纯量函数或列函数。有源函数可以使一个用户定义的类型(UDT)有选择地继承其基类型的语义。

(2)外部函数(external function)

是指其函数体是由用户采用某种宿主程序设计语言编写的函数。在UDB中,外部函数可采用C,C++或JAVA语言实现。一个外部函数必须使用CREATE FUNCTION语句创建,以便使系统知道如何找到该函数的实现代码(程序)。一个外部函数可以是一个运算符、一个纯量函数或一个表函数,但不能是一个列函数。此外,在UDB中,外部函数不能包含任何SQL语句。换句话说,一个外部函数只能在其输入参数上进行某些计算或通过参数与外界传递某些数据,但决不能存取或修改数据库。

系统生成的函数和用户定义的函数总是建立在一个指定的数据库上且只能在该数据库中使用。在其数据库内部,使用系统生成的函数和用户定义的函数的方法与使用内部函数的方法完全一样,可为该数据库的所有用户使用,而不需要指定任何权限。

任何用户都可以其自己的USERID为模式名建立FENCED函数。但只有DBADM或SYSADM权限的用户才可以在其它模式名下建立用户定义函数。而要建立NOT FENCED函数则必须在数据库上拥有NOT FENCED权限。

CREATE FUNCTION语句可以被嵌入在应用程序中或通过交互式命令接口(如CLP)执行。但如果CREATE FUNCTION语句被嵌入在应用程序中,则该程序中的静态嵌入式SQL语句不能引用这些新创建的函数,因为当程序被绑定时这些函数还并不存在。

同许多现代程序设计语言一样,SQL支持函数重载(function overloading)的概念,即几个不同的函数具有相同的名称,但只要这些函数被定义在不同的模式下或具有不完全相同的参数个数或类型即可。为此可将那些具有相同名称的函数的集合称为一个函数族(function family),并称其中的每一个具体函数为一个函数实例(function instance)。

在创建新的函数时,可一次创建一个新的函数族,也可将一个新的函数实例加入到已有的函数族中。但无论那种情况下,都必须保证新创建的函数具有唯一的标记(signature)。一个函数的标记由其完全限定名和其全部参数组合而成。例如,设函数square(Double)已经存在,则在同一个模式下就不允许再创建另一个square(Double)函数。

用户定义函数族中的每一个实例处理与同一函数族中的其它函数共享同一函数名外,还有一个特殊名(specific name)。每一函数的特殊名在其模式内是唯一的,例如,可以建立一个特殊名为square1的函数square(Integer),还可以建立一个特殊名为square2的函数square(Double)。特殊名只用于标识一个函数实例,例如,当要删除一个函数,注释一个函数或引用一个函数为另一个函数的源函数时,只需要特殊名即可。当要调用一个函数时,则必须使用其族名,而不能使用特殊名。

每一个用户定义的函数和系统生成的函数都在系统编目表FUNCTIONS中有相应的描述。FUNCTIONS表包含以下列:

l FUNCSCHEMA,FUNCNAME:为函数的完全限定名;

l SPECIFICNAME:为函数的特殊名;

l ORIGIN:为一个字母代码,标识函数的来源,其中所用的符号如下:

‘B’:内部函数

‘S’:系统生成函数

‘U’:用户定义的有源函数

‘E’:用户定义的外部函数

l TYPE:标识函数的类型,分别为纯量函数(scaler function)、列函数(column functiom)或表函数(table function);

l 反映函数其它特性的列:

此外,而按其计算形式还可将SQL语句中的函数可分为以下几类:

1.纯量函数(scaler function)

又称标量函数,指一次作用在一个或一行值上并返回一个值的函数,例如,常见的算术函数、字符串函数及时间日期函数等。

2.列函数(column functiom)

又称聚集函数(aggragation function), 指一次作用在一组值上并返回一个汇总性结果值的函数。例如,MIN()、MAX()、AVG()、COUNT()等。

3.表函数(table function)

8.5.2 UDF的优点

1.UDF提供了一种用户可以按照自己的需求扩展SQL的途径。虽然SQL内嵌函数有着强大的功能,但仍然不能满足广泛、复杂、多样的用户需求,而UDF则能很好地解决这一矛盾。这些需求包括:

· 定制(Customization):应用程序希望完成的某项功能在DB2中没有相应的函数。然而,无论该功能是一个简单的事务,还是一次复杂的分析计算,都可以用UDF实现。

· 灵活性(Flexibility):内嵌函数往往不能适应应用程序的变化,而UDF则能很好地满足这一点。

· 标准化(Standardization):许多应用程序都需要实现一些函数,而这些函数完成的是同样的功能。然而,不同的应用程序实现这些相同的功能时却往往存在这样或那样的差别,从而导致结果不一致。但如果用UDF正确地实现了有关功能,则所有应用都可以使用,因而保证了结果的一致。

· 对象-关系支持(object-relational support):前面章节中介绍的用户定义类型(UDT),对于扩充数据库系统的功能,加强数据库安全性都是非常有力的。UDF通过提供行为封装,而可以成为UDT中的方法。

2.在编写DB2应用程序的过程中,当要实现一项功能或操作时,通常可以采用两种途径。一是将其作为UDF;二是将其作为应用程序的子程序或函数。尽管后者似乎较为容易,但我们还是提倡使用UDF。因为两者比较,使用UDF具有以下优点:

· 可重用(reuse):如果多个用户或应用需要完成的操作是相似的,即可以考虑使用UDF。用户或应用只需要通过SQL语句调用相应的UDF。

· 高性能(high-performance):从数据库引擎直接调用UDF将比从应用程序中调用子程序或函数更加有效。使用UDF可避免数据库与应用程序之间的大量数据传输,并使计算效率得到进一步提高。

· 面向对象(object-orientation):可以用UDF来实现UDT的一些行为。UDT在创建时就已经具备了一些转换函数来实现自定义类型与源类型之间的转换。根据源类型的不同,某些UDT还具备比较运算符(如=,>,<等)。而对UDT其他功能的扩展也将依赖于UDF。UDT和UDF正是DB2中面向对象特征的重要体现。

8.5.3 有关UDF的几个基本概念

1、函数全名(full name of function)

函数全名是形如.的字符串。例如:

SLICKO.BOAT_COMPARE SMITH.FOO

SYSIBM.SUBSTR SYSFUN.FLOOR

在调用函数时既可以使用带有模式名的 函数数全名,也可以使用省略模式名的函数名。

2、函数路径(function path)

当调用函数时省略了模式名将产生函数引用的不确定性。为此,需要通过函数路径解决这一问题。函数路径是一个有序的模式名的列表。当遇到无模式名的函数调用时,系统将按照函数路径指定的顺序在模式中查找满足要求的函数实例。

函数路径可由预编译和绑定命令中的FUNCPATH参数确定,也可由SET CURRENT FUNCTION PATH语句动态确定,函数路径的缺省值为:

“SYSIBM”,“SYSFUN”,“”

其中,表示当前用户的标识。

3、函数名重载

DB2允许函数名重载,即多个可以有相同的函数名,只要它们处于不同的模式下,或具有不同数目或类型的参数。

在DB2中许多内嵌函数都已被重载。有时称具有相同名字的函数集合为一个函数族,并用函数实例表示函数族中的一个具体函数。

4、函数原型(signature)

函数原型就是函数的全名与全部参数说明的组合。当创建一个新的函数,无论是在某个函数族中增加一个函数实例,还是创建一个新的函数族,都必须保证新创建的函数具有唯一的原型。

5、特殊名(specific name)

每一个用户定义的函数实例都可以有一个特殊名。在同一个模式下,每一个函数实例的特殊名都必须是唯一的。

6、函数解析

函数解析是指当有多个函数原型满足一个给定的函数调用时,按照函数路径从中选择一个最适合的函数实例。无论是用限定名还是用非限定名进行函数调用,甚至对内嵌函数及SYSFUN模式下的函数进行调用,都需要经过函数解析过程。

8.5.4 UDF的实现步骤

实现役个UDF需要经过以下几个步骤:

1、编写UDF的实现代码

2、编译 UDF的实现代码

3、链接UDF UDF的实现代码

4、注册UDF

在完成上述步骤后,即可在SQL语句中调用新建立的UDF。

8.6 注册有源函数

有源函数是指利用已有的函数(称为源函数(source function))而创建的函数。当新函数被调用时,其变元将被转换为相应源函数的参数类型,然后调用源函数。最后,源函数的结果再被转换为新函数的结果类型。

注册有源函数的CREATE FUNCTION语句的语法如下:

CREATE FUNCTION 函数名([数据类型,…])

RETURNS 数据类型

[SPECIFIC 特殊名]

SOURCE 函数名 | SPECIFIC 特殊名 | 函数名([数据类型,…])

其中每一部分分别说明如下:

1. 函数名

是调用函数时所用的名字,其中可包含模式名,但模式名不能以SYS开头。如果没有给出模式名,则其缺省模式名为当前用户的USERID。只有具有SYSADM或DBADM权限的用户,才能为其新创建的函数指定不同于其USERID的其它模式名。

函数名可以是算术运算符,如“+”或“*”等,但定义这样的函数时必须用双引号将函数名括起来。如果一个函数名是一个中缀运算符(如“+”),则也用中缀表示法调用该函数,例如weight+weight。

比较运算符(如”=”,”>”)和可在SQL谓词中出现的SQL保留字(如AND,OR,NOT,EXISTS,BETWEEN)都不允许作为函数名使用。

2.([数据类型,…])

语句这一部分列出所定义函数的输入参数的数目及数据类型。一个函数可以有0个至90个输入参数。即使一个函数没有输入参数,也必须在其名字面出现一对括号。输入参数的数据类型可以是内部数据类型,也可以是用户定义的数据类型(UDT)。

3.RETURNS子句

该子句说明所定义函数的返回结果的数据类型。

4.SPECIFIC子句

该子句的作用是为新定义的函数指定一个特殊名。当多个函数共用同一个函数名时,特殊名将可以唯一标识出其中的每一个函数实例。当需要删除一个函数实例、对一个函数加注释或将一个函数作为源函数引用时将可以使用其特殊名。应当注意的是,当调用函数时,总是使用其函数名,而不使用其特殊名。

在一个模式内,每一个函数实例的特殊名都必须是唯一的。如果创建函数时没有为其指定特殊名,则系统将会自动为其生成一个特殊名,所生成的特殊名的格式为SQLyymmddhhmmsshhn。

5.SOURCE子句

该子句的作用是标识新定义的函数为一个有源函数,并指定已有的函数为其源函数。被指定的源函数可以为内部函数或用户定义的函数,也可以是运算符(如“+“)、纯量函数(如substr)或列函数(如AVG)。指定的源函数可以采用下列3种方法进行引用:

(1)由源函数的函数名指定,不带参数。这种方式只适用于源函数为用户定义的函数,或其名字在模式中是唯一的函数。在所提供的函数名之前可以用模式名对其进行限定。也可以不用模式名对其进行限定,这时系统将按照函数路径指定的模式顺序进行查找,并将找到的第一个具有指定函数名的函数作为源函数。此外,如果被引用的源函数的名字为一个运算符(如“+”)或SQL关键字,则必须用双引号将其括起来。

(2)由源函数的特殊名指定。由于内部函数没有特殊名,所以这种方式只适用于用户定义的函数。函数的特殊名可以通过查找系统编目表FUNCTIONS的SPECIFICNAME列得到。在所提供的特殊名之前可以用模式名对其进行限定。也可以不用模式名对其进行限定,这时系统将按照函数路径指定的模式顺序进行查找,并将找到的第一个具有指定特殊名的函数作为源函数。

(3)由源函数的标记指定,即由其函数名及其参数的数据类型指定。这也是唯一标识内部函数为源函数的有效方式。在所提供的函数标记之前可以用模式名对其进行限定。也可以不用模式名对其进行限定,这时系统将按照函数路径指定的模式顺序进行查找,并将找到的第一个符合指定函数标记的函数作为源函数。

8.7 注册外部函数

注册有源外部函数的CREATE FUNCTION语句的语法如下:

CREATE FUNCTION 函数名([数据类型,…])

RETURNS 数据类型1 [CAST FROM 数据类型2]

[SPECIFIC 特殊名]

EXTERNAL NAME ‘字符串’

[NOT ]VARIANT

[NO] EXERNAL ACTION

[ [NOT] FENCED ]

[[NOT] NULL CALL]

LANGUAGE {C | JAVA | OLE}

PARAMETER STYLE {DB2SQL | DB2GENERAL}

NO SQL

[ [NO] SCRATCCHPAD]

[[NO] FINAL CALL]

其中每一部分分别说明如下:

1. 函数名

是调用函数时所用的名字。函数名必须是一个SQL标识符,并且最长为18个字符。函数名可以前缀一个模式名,但前缀的模式名不能以SYS开头。如果没有前缀模式名,则其缺省模式名为当前用户的USERID。只有具有SYSADM或DBADM权限的用户,才能为其新创建的函数指定不同于其USERID的其它模式名作为前缀。

函数名还可以是算术运算符,如“+”或“*”等,但定义这样的函数时必须用双引号将函数名括起来。如果一个函数名是一个中缀运算符(如“+”),则也用中缀表示法调用该函数,例如weight+weight。

比较运算符(如”=”,”>”)和可在SQL谓词中出现的SQL保留字(如AND,OR,NOT,EXISTS,BETWEEN)都不允许作为函数名使用。

2.([数据类型,…])

语句这一部分列出所定义函数的输入参数的数目及数据类型。一个函数可以有0个至90个输入参数。即使一个函数没有输入参数,也必须在其名字面出现一对括号。输入参数的数据类型可以是内部数据类型,也可以是用户定义的数据类型(UDT)。

3.RETURNS子句

该子句说明所定义函数的返回结果的数据类型。结果类型可以是内部数据类型或用户定义的数据类型。如果是字符串类型,则必须显式指定其最大长度。

假定外部函数是由C语言实现的,由C程序返回的值为一个C的数据类型,应将其转换为相应的SQL数据类型(如表X所示)。

表X 传递给C外部函数的参数数据类型

SQL数据类型

符号类型名

对应的C数据类型

Smallint

SQLUDF_SMALLINT

Short

Integer

SQLUDF_INTEGER

Long

Decimal(p,s)

在C中无等价的类型,不能将Decimal类型的数据传递给外部函数,可用char(Decimal)函数将参数转换为Char类型;或利用double(Decimal)函数将其转换为double数据类型。

Double

SQLUDF_duoble

Double

Char(n)

SQLUDF_CHAR

char(n+1)(null终止符)

Varchar(n)

(不适用于位数据)

SQLUDF_VARCHAR

char(n+1)(null终止符)

Varchar(n)

(适用于位数据)

SQLUDF_VARCHAR_FBD

struct{

unsigned short length;

char data[n];

}

Long varchar

SQLUDF_LONG

struct{

unsigned short length;

char data[n];

}

Graphic(n)和

Vargraphic(n)

SQLUDF_GRAPH,

SQLUDF_VARGRAPH

wchar_t[n+1]

(null 终止符)

Long vargaphic

SQLUDF_LONGVAR

struct{

unsigned short length;

wchar_t data[n];

}

(注:length以双字节单位表示)

如果外部函数的返回类型为一个用户定义的数据类型(UDT),则首先将C程序返回的值从C的类型转换为相应的SQL类型,作为一个输入主变量,然后再用系统提供的转换函数将SQL类型转换为所需要的用户定义的类型。

如果外部函数的结果类型与实现该函数的C程序返回的数据类型不一至,则需要用CAST FROM子句指定一个附加的转换。

4.SPECIFIC子句

该子句的作用是为新定义的函数指定一个特殊名。与有源函数一样,当多个外部函数共用同一个函数名时,特殊名将可以唯一标识出其中的每一个函数实例。当需要删除一个函数实例、对一个函数加注释或将一个函数作为源函数引用时将可以使用其特殊名。应当注意的是,当调用函数时,总是使用其函数名,而不使用其特殊名。

在一个模式内,每一个函数实例的特殊名都必须是唯一的。如果创建函数时没有为其指定特殊名,则系统将会自动为其生成一个特殊名,所生成的特殊名的格式为SQLyymmddhhmmsshhn。

5.EXTERNAL NAME子句

该子句标识新定义的函数为一个外部函数,所包含的带引号的字符串告诉系统如何查找实现该函数的代码函数库。所给定的函数库必须是经过编译、连接后存放在服务器的目录中,当需要时,数据库管理系统可以动态地将其装入内存。

6.VARIANT或NOT VARIANT

函数必须被说明为 VARIANT或NOT VARIANT。

VARIANT表示用相同的输入参数值对函数进行两次调用可能返回不同的结果,例如随机数函数就是一 VARIANT函数。

7.EXTERNAL ACTION或NO EXTERNAL ACTION

该子句也是必须的,它表明函数体中是否执行某些外部动作。外部动作是指会对数据库系统以外的事物发生影响的行为。如发送EMAIL,写文件,发出声音报警等。

8.FENCED 或 NOT FENCED

FENCED表示函数将在与数据库系统分离的地址空间中运行。虽然FENCED函数会影响数据库系统的运行效率,但确可以保证数据库系统不会被函数运行中的错误所破坏。NOT FENCED函数将与数据库管理系统在同一个地址空间中运行。要创建NOT FENCED函数必须具有SYSADM,DBADM或数据库上的NOT_FENCED权限。

该子句是可选的,其确省值为FENCED。

9.NULL CALL或NOT NULL CALL

许多函数当输入参数为NULL时,也返回NULL。如果将函数说明为NOT NULL CALL,则系统会自动检测输入参数,若检测到输入参数为NULL值,则系统直接返回NULL,从而使UDF函数体不再需要检测输入参数是否为空值。

常见问题
以下是一些常见问题的解答,如果您有什么其他问题,请发信到db2_faq@vip.sina.com

问:我正在 NT 上运行 DB2 V7 的黄金代码。我以为联合 DB 支持就意味着我可以连接不同 DB2 数据库中的两个表。 我尝试执行该操作,但没有成功。当我试图创建服务器时,我遇到了各种各样的语法错误。有人这样连接成功了吗

答:是的,这是可以支持的。如果您没有成功,我建议您查看 Admin Guide 中略述的有关联合数据库支持的步骤。然而,我通过使用控制中心来定义必要的别名等。您可能已经这样做了,而此时特定的 SQL 可能让人头疼。这就需要 FDB 开发者进行分析。对于 V7 中的 FDB,SQL 限于只读操作。

问: 顾客在 LDAP 服务器上定义了大量用户。DB2 可以用 LDAP 服务器来认证这些用户吗

答: LDAP 用于 DB2 中只是为了共享编目目录(即数据库和节点)。对于认证支持,我们只关注 Kerberos。

问: DB2 版本 7.1 中“技术预览”部分的介绍中曾经提到过增量备份可是在发行的G.A. 代码或命令参考手册中都没有此特性。是不是该特性推迟发行了?什么时候才可能用上?(下一发行版?补丁程序包?九月份?)

答: 在 DB2 V 7.1 未来的补丁程序包中将可用增量备份。

问:当我试图将我的 DB2 v5 数据库恢复至具有 DB2 v7.1 的新服务器时,得到以下消息。

(4)RalIds1E C://db2 restore db cqmsloc1 from m:/db20616

在备份映像中指示的 SQL2548N 数据库代码页“850”同当前磁盘上所指示数据库代码页“437”有所不同。恢复操作失败。

我试图切换 config.sys 为 850,437,但是却无济于事。

劳驾哪位帮忙解决这个问题。

答:我有两种克服该困难的办法,1. 在新机器上安装旧版本,先恢复,然后更新至新版本。 2. 创建一个同名数据库,设置欲设的代码页,然后进行恢复。

问: 客户要从 Oracle 移植到 DB2 UDB,而待移植项目之一就是序列 ID。但是,他们却发现 DB2 UDBV7 中的序列 ID 是在行插入时内部生成的,于是想要知道如何获得所插入行的 ID,以在其应用程序中显示。如果他们需要基于另一个唯一标识符来查询序列 ID 域,这看起来似乎就违背了序列号的用途,并且也增大了从 Oracle 移植的难度。对于插入到 V7 中的一行来说,检索其序列 ID 的方法是什么呢?谢谢。

答:在 V7.1 中提供了一个新函数 IDENTITY_VAL_LOCAL(),它允许应用程序检索插入的 ID 值。这在发行说明中有文档记录。

问: 我已经在 Win98 中安装了 DB2 PE/RC。Oracle NET8 和 SQL*PLUS 也已经安装。我可以针对 Oracle 使用别名。请问是否支持这种配置?

答: 不应该这样。声明信中说得清清楚楚:

“下列产品需要一套经许可的 DB2 EE 或 EEE 的副本”

我们不允许在 Win98 上安装该组件。

问:为了在安装 OS/390 DB2 V5 时管理对象,我尝试在 UDB 中通过一个 NT 对话框使用“控制中心”。我连接成功并且能够看到对象,但是当我试图使用诸如 runstats 或 reorg 等实用程序时,程序线程在试图从 dsnutils 中运行函数时超时。这样做可能吗?如果可能的话,又应该怎么做呢?

答:要想这样做的话,需要将 OS/390 DB2 的实用程序在大型机上作为存储过程来运行。为了运行这些实用程序,您必须对一组实用程序文件执行远程绑定。

问:将 ASP 连接到 DB2 UDB 时 ODBC 还能用吗?

答:可以用,只要您在 Web 服务器上安装了 DB2 客户机,DB2 ODBC 驱动程序就会是可用的。 将您的 DB 定义为 ODBC(例如 MYUDB)。然后就可以像使用其他 ODBC 定义的 DB 一样从 ASP 中使用 ADO-ODBC 调用。

问:我们正在用 ColdFusion 和 MS Access 创建一个内联网应用程序,ColdFusion 和 MS Access 将连接我们的 DB2 公司数据库,并从中提取特定的只读信息。我需要什么样的 DB2 客户机工具和/或 ODBC 驱动程序呢?应用程序将驻留在 NT 服务器上,而且任何驱动程序都需要是 ColdFusion 服务器可访问的,可以有人指点如何开始吗?

答:CAE(v6.0 称其为运行时客户机)包含 DB2 的 ODBC 驱动程序。一旦安装,请运行客户机配置辅助程序。确认通知该程序要将此数据库作为一个 ODBC 数据源。一旦完成该步,您就可以在访问时导入外部数据源,它将出现在显示出的某个选项里。

问:我试图对由某个用户输入的信息和 db2 数据库中的日期字段进行日期比较。
我不能确定如何从语法上在此日期和彼日期间比较 table.column 名称。还有,是否要符合特定格式才能进行此类检查,这样做是否在某种程度上是可配置的?

答:我目前所用版本的 SQL Reference 一书的第 3 章前面有一部分题为“赋值与比较”,但是您所用的书可能在细节和产品方面有一些差别。本部分包括一小段对日期时间作比较的介绍。

您需要经常使用函数来将您的数据转换成可以正确进行比较的格式。第 4 章包含了大量函数,包括 DATE、
DAY、DAYS、TIME、TIMESTAMP 及许多其他函数,尤其是在 DB2 for OS/390 中包含 V6 的更新及样例 UDF 时函数更多。

问:有人知道是否可以给不同数据库中不同的表以相同名称吗?
换句话说,我们具有一个需要向不同用户分发的应用程序,它运行在相同 DB2 系统中的同种机器上,但是每个用户都有其自己的数据库:表名一定不能相同吗?
我们怎样才能用同一条 SQL 语句访问数据库 D1 和数据库 D2 中的表 A(而不必使用动态 SQL)呢?这样做是否有一个 BIND 选项?

答: 您可以使用 TABLE MIRRORING。如果表的结构相同,那么就可以利用BIND PACKAGE 的参数 VERSION。对于这些数据库,要使用两个不同的版本名称和限定符。因此在运行应用程序时,使用 VER1 就可能会用 DB1,使用 VER2 就可能会用 DB2。

这就是使用程序包的优点之一。

问:如何在 Solaris 上自动安装 UDB,使其成为一个“免维护”过程?

答:UNIX 上的 UDB 自动安装过程位于本 CD 的 /db2/install/samples 目录下。此过程包括一个用于安装脚本的“回应文件”,可以回答安装时提出的问题。该方法以 html 格式收录在随 UDB 发布的“安装与配置附录”手册中。

问:IBM 支持 DB2 UDB 使用 Java 的决心有多大?

答:“IBM 支持 DB2 UDB 使用 Java 的决心有多大?”在向现有的 Oracle、Sybase 或 SQL Server 顾客环境进行销售经常遇到这种问题。现有的 Oracle 顾客经常声称 IBM 销售具有 Java 支持的 UDB 时总是晚了一步。下表是从 Gene Kligerman 有关 v6 的介绍中获得的,它说明的事实恰恰相反。眼见为实:

clip_image017

问:DB2 V7 中的 Relational Connect 就是我们从 DataJoiner 中集成的功能吗?

答:是的 — 它是 Datajoiner 功能的部分集成。

问:顾客在 AIX 上测试 DB2 UDB V7 时向从另一台 AIX 机器上的 Oracle 中选取数据。这对单独一个 DB2 UDB EE V7 可能吗?如果不能的话,还需要什么其他产品?

答: DataJoiner 和 DB2 Relational Connect 两者都不用在 V7 上安装。

问:可以通过命令行在 BLOB 类型的列中插入 gif/jpeg 图像吗?
如果不能,我需要编写一个程序来运行插入语句吗?
请就此指教,深表感谢。

答:请参考一本旧的红皮书 (1996!),它会对您有所帮助:World Wide WebAccess to DB2, SG 24-4716-00。网址为
http://www.redbook.ibm.com
作者在示例中说明了在 db2表中如何实现 jpg/gif 图像。

问: DB2 V7 的 stored procedure builder 需要一个针对 SQL 存储过程的编译器。我已经安装了 Visualage C++,但是在创建过程的“nmake”这一步骤中发生错误。VAC++ 产品可以用于 SPB 吗?

答:您用的是哪一种 C 编译器?只有特定发行级别的特定编译器才受支持。如果为 Microsoft 的产品,就仅仅为 V5 或 V6。我认为您的 VA C++ 发行版不是 3.6就是 4.0。此外,还必须设置环境。... 请参阅 DB2 UDB ReleaseNotes 中有关 SQL 存储过程的部分。必须设置两个环境变量。

问:我有一个 VB3.0(16 位)程序,通过 ODBC API 调用来访问 DB2 6.1 for Windows NT。当我试图在同一应用程序中打开两个连接时,ODBC 驱动程序似乎只对最后一个打开的连接有效。例如:我打开第一个连接之后用不同的 ALIAS 打开第二个连接,然后在第一个连接中试图执行 SELECT 语句。这时 ODBC 给我一条错误消息,说没有找到我在 SELECT 语句中引用的表。

有人知道这是怎么回事吗?

答:我不知道 16 位和 32 位的 CAE 版本有什么区别。不管怎么说,解决该问题的方法应为:
1. 连接类型必须为 2
SET CLIENT CONNECT 2
2. 发出第一个连接
CONNECT TO PRODUCTION USER XXX USING XXX
3. 发出第二个连接
CONNECT TO TEST USER XXX USING XXX
4. 用下列语句在连接间切换
SET CONNECTION PRODUCTION
SET CONNECTION TEST
5. 用所需连接发出 SQL 命令
有关详细信息,请查看 SQL Reference Book 中的 Connect (Type 2) 语句。

问:用 ADSM 进行联机备份时,我试图用 ADSM userexit (db2uexit.cad) 来存储归档日志文件。根据 DB2UEXIT.CAD 的说明,我需要将 db2uexit.cad 复制为 db2uexit.c 再进行编译。命令为:“icc -ddsm32bit db2uexit.c adsmv3.lib db2uext3.def”。

有人有过 ADSM userexit 的使用经验吗

答:以下是 db2uexit.c 文件中更改的变量,请加以注意
#define BUFFER_SIZE 4096 /* 发送或接收日志 */
/* 4k 大小的文件 */
#define AUDIT_ACTIVE 1 /* 启用审计踪迹日志记录 */
#define ERROR_ACTIVE 1 /* 启用错误踪迹日志记录 */
#define AUDIT_ERROR_PATH "/u/" /* 路径必须用斜线结束 */
填#define AUDIT_ERROR_ATTR "a" /*添加到文本文件
大多数变量可以采用缺省值.肯定需要更改的变量是 AUDIT_ERROR_PATH。这是 DB2 向 userexit.err 文件写入的路径,如果在向 adsm 归档日志时发生错误,请切记用斜线结束路径。例如,c:/logs/userexit/”。

问:能否在 DB2 (AS/400) 中使用 SQL 执行不区分大小写的 ORDER BY?
对于 DB2 SQL,我找不到这样做的方法。

答:您可以通过选择 UCASE(COL1) 或 LCASE(COL1),执行不区分大小写的 ORDER BY。但是即便是这种 ORDER BY 对于一个字符来说仍然是区分大小写的,也就是说,如果有“aaaa”和“AAAA”,则执行 ORDER BY 时“aaaa”将排在前面。但是这种排序在任两个不同字符间是很管用的。也就是说,如果有“bbbb”和“AAAA”,则在执行 ORDER BY 后“AAAA”将排在前面。

这些函数(UCASE 和 LCASE)可在 UDB 而不是 DB2 中找到,至少不能在 DB2 V5 中找到。

问:我在 Sun Solaris 5.6 中安装了 DB2。我甚至还创建了数据库。我想在这个新数据库中创建一些用户,并给予他们特定的许可。为搜集信息我查遍了所有资料,可是却找不到怎么做。如果有人肯告诉我怎么做,我将感激不尽。

答:
在 DB2 数据库中不能创建用户,只能通过操作系统的工具/命令(例如在 AIX 上用 mkuser)在 Solaris 对话框中创建用户。一旦在 UNIX 对话框中创建了用户,就可以将 DB2 安全性挂接到 userid 上,通过 DB2 的安全机制控制对 DB2 对象的访问。

问:DB2 可以通过某种方法将活动日志卸载到基于 GDG 的归档日志数据集吗?
答:
不可以,归档日志是以封装方式定义并处理的。它不允许定义 GDG 基准。您只能指定限定符。该限定符根据您在 ZPARMS 中的选择添加到唯一名称中。

问:我想从 ODBC 管理员以系统 DSN 身份连接到我的一个外部 ODBC 数据库,提取表信息并将其存储到DB2数据库的一个 DB2表中。

这样做可以吗?又该怎么做呢?
答:
通过 ODBC/OleDB,可以用任何脚本语言程序,如 VBScript、JScript 或 C 等来实现,所用程序取决于平台。

问:我们正在存储过程日益增多的 DB2/OS390 环境中建立数据共享。在数据共享环境中,存储过程在何处运行?
是在开始调用存储过程的 DB2 中吗?
还是在定义存储过程的 DB2 中呢?
还是在包含装载模块的装载库的存储过程地址空间中呢?
或是在其他地方 而需要我留意吗?
如果存储过程访问非共享 DASD 上的 VSAM,那又怎么办呢?

答:存储过程在存储过程地址空间或由 WLM 在启动该过程的 DB2 中建立的地址空间中运行。

问:我们当前正在 9906(一个非数据共享环境)中运行 DB2 OS/390 V5,并且打算升级到版本 6。
我原先计划订购并且使用 0002。但得知要对 March'00 进行更新的计划后,就决定等一段时间。现在更新被推迟到五月份,我还等吗?如果不等,有谁能推荐一个 较好的安装服务级别呢?

而且,能直接从 9906 上的 V5 向版本 6 升级吗?您的意见呢?
答:
要查明有关信息,最好阅读 Ver 6.0 的封面信函以及 IBMLINK 上的先决条件。Hiper 9904 应当是正常的。我不清楚 hiper 9904 的衰退问题,因为早期代码是兼容的。通常最好的办法是升级到当前发布的级别。我建议至少升级到 9909。

问:我正在跨现有的表添加一个新的列(os/390 中的 DB2)。现在我要在该列中装入一个值。到目前为止,所有行都包含相同的值(比如‘abc’)。

我如何才能装入该列...
a) 而不必编写程序
b) 不必通过缺省值‘abc’定义该列
因为有一种可能性,它可能还包含其他值,但到目前为止只得到‘abc’。
能通过装入卡来解决该问题吗?
作为最后一种手段,我正在考虑编写 REXX 实用程序来更改卸装的文件。您对此有何建议?

答:如果您有 bmc,您可以在卸装时硬编码这些值
这里是一个控制卡的实例,其中将 31 作为列的新值:
//sysin dd*
UNLOAD
CNTLCARDS DB2 'REPLACE UPDATEDB2STATS YES LOG NO COPY NO'
UNLOADDN Data
SORTDEVT SYSDA SORTNUM 12 MAXSORTS 12
SELECT
31
,BANK_NBR
,ASOC_GRP_IND
,ASOC_GRP_NBR
,etc
然后您可以按正确的顺序只在字段中装入最近定义的数据。
其他选项(也通过 BMC)可将它编码到您的装入中,在这种情况下 prt_num 的值是硬编码的:
LOAD DATA INDDN LOADIN RESUME YES
LOG NO SORTDEVT SYSDA SORTNUM 12 DELETEFILES YES
ENFORCE NO DISCARDS 0 UPDATEDB2STATS YES COPY NO
INTO TABLE CHS0412.table1 PART 1 REPLACE
(PRT_NUM VALUE ('01')
,BK_NUM POSITION(2:3) SMALLINT

问:db2move 只是一条命令还是一个独立的工具,我如何使用它?
答:
db2move 是一个工具,可使用它方便地在位于工作站中的 DB2 数据库之间移动大量的表。该工具在系统编目表中查询特定的数据库、编译一个所有用户表的列表,然后将这些表以 PC/IXF 格式导出。然后可以将 PC/IXF 文件导入或装入到 相同系统中的另一个本地 DB2 数据库,也可以传送到另一个工作站平台并且导入或装入到该平台中的一个 DB2 数据库。

从命令提示中键入 db2move 并且按 enter 键可查看该工具提供的不同选项。您还可以查阅命令参考指南以获取有关 db2move 的详细资料。

问:在 DB2“控制中心”中,我通过用鼠标右击一个数据库并且选择“Remove”,而不是“Drop”意外地删除了它。现在我要通过相同的名称和/或别名创建一个新的数据库,DB2 指示:

在本地数据库目录或系统数据库目录中已存在数据库别名“SALES_DB”。
我如何消除剩余的旧“SALES_DB” 数据库?

答:您只需右击数据库的图标。选择 add。 然后选择“refresh”按钮。选择您已经删除的数据库。 一旦添加它,就可以删除。

问:我得到了分配给 DB2 的 4GB 内存,但怀疑这是否需要缓冲池中的扩充存储器才能访问更多的该内存,即内存地址在 NT 中是限制为 2GB 还是 4GB。

答:NT 通常只允许非系统进程(包括 DB2)访问 2GB 的内存,而不管帧内是否有更多内存可用。其他 2GB 被保留用于系统进程。因为 NT 的 32 位结构,4GB 是您可以寻址的最高内存位置,这使得使用附加内存成为难题。

您可以通过使用 BOOT.INI 开关为非系统进程获取 3GB。 将 /3GB
添加到 BOOT.INI 中启动您的分区的行的结尾处。然后您可以使用附加的内存。
DB2 V7 有一些新代码,允许 DB2 寻址 4GB 限制以上的内存。

问:我们希望获得当前日期之后三个月的日期。我们能在 DB2 中做到吗?在 Sybase 或 SQL 服务器中,有一个称为 DateAdd 的功效强大的函数。

答:DB2 中有一个功能更强大的函数,允许您将任何内容添加到其他的内容,只要它们的类型相同。这称为‘+’!
您需要运行查询的 SQL 是:
SELECT CURRENT DATE + 3 MONTHS
FROM creator.table;

问:在 DB2 V6.1 中,表名和列名的最大长度是 30,而关系名和索引名的最大长度是 18。这些最大值在 DB2 5.2 这样的旧版本中是相同的吗?

答:
我认为这是非 OS/390 平台的 V6.1 中的新增内容。在先前的版本(仍在 390 中)中,表/视图和列的名称不能超过 18 个字符。在 V6.1 中,表和视图名称的最大长度扩展到了 128 个字符,而列名称的最大长度扩展到了 30 个字符。

问:我可以从嵌入式 SQL 应用程序中成功运行存储过程,但不能从 ODBC/CLI 应用程序中运行它。在这种情况下,我遇到错误“dll could not be loaded”。我使用 DB2/NT 6.1.0。

答:我使用 Java 编写了一个外部存储过程,该过程采用 1 个输入参数(整数类型)并且不返回任何值。我从我的 Java 客户机程序中调用它(它工作时没有出现任何问题),但我试图从命令中心中执行它时遇到下列信息:

---------- Command Entered ------
call IMERCTESTPROC911 (3) ;
---------------------------------
SQL1109N The specified DLL "IMERCTESTPROC911" could not be loaded.
我使用具有修正软件包 3 个人版本 NT 的 DB2 V6.1。
这是我的 Java 程序执行的原样代码。
CallableStatement stmt;
String sql = "Call " + + "(?) ";
stmt = con.prepareCall (sql);
stmt.setInt(1, );
stmt.execute ();
我已将存储过程类文件放在 C:/SQLLIB/FUNCTION 中

问:这里是我正在工作的 SQL 语句。我希望按列 w12.exent_id 排序该视图。感谢您的帮助。
create view Events_Table_Owner (Name, Type, Location, Event, Owner, Event_Number)

as select wl2.event_name, wl2.event_type, wl2.location, wl1.first_name, wl1.last_name,wl2.event_id

from wltable2 wl2, wltable1 wl1
where wl2.event_owner=wl1.contact_id
答:
不能排序视图。您必须在从视图中选择时包含 ORDER BY 子句。

问:我们在两个环境,NT 和 OS/390 中都装有 DB2 V6.1。我们尝试创建类似于SQL 参考书中的实例的触发器,但遇到语法错误。在另一方面,我们成功创建了一些其他的简单触发器,但它们没有工作。

得到语法错误的触发器:
Table GEN.WOF has 2 columns , DAY and WEEKOFYEAR
CREATE TRIGGER TG1 ON GEN.WOF AFTER UPDATE of DAY on GEN.WOF
FOR EACH ROW MODE DB2SQL
BEGIN ATOMIC
UPDATE GEN.WOF SET WEEKOFYEAR=WEEK(DAY);
END
成功创建了触发器,但没有工作:
CREATE TRIGGER TG2 ON GEN.WOF AFTER UPDATE of DAY on GEN.WOF
FOR EACH ROW MODE DB2SQL
BEGIN ATOMIC
UPDATE GEN.WOF SET DAY='01.05.2000';
END
有人遇到这种问题吗?有没有解决办法?
答:
我在 UDB V5.2 中遇到过这种问题。我也遇到了这些语法错误。 因为我只在 BEGIN ATOMIC 和 END 子句中使用了一条语句,所以我将其删掉了。进行该修改后,触发器就正常工作了。而且在您的触发器定义中没有 REFERENCE 子句和 WHEN 子句,为此,您可能要更新整个表。

问:我能在 DB2 OS/390 中建立一个存储 Unicode 数据的表吗?如何创建?支持它的最低 DB2 版本是多少?
答:
DB2/390 直到 V7 才会支持 Unicode(V7 现在仍未进行 GA)。 我认为将要及早发布的功能不会是 PTF,但对其列表进行检查可能是值得的。

问:我想询问关于大型机 DB2 的问题,它通常是一个 peoplesoft / DB2 环境。
我怀疑有一些线程通过 AIX 中的 DB2 连接网关从 PC 客户机远程连接到数据库,如果一个批处理作业(比如一个图像复制)在大型机上进行并且以 util 模式启动数据库,则这些与数据库连接的线程将要发生什么情况?它们将隐藏在不一致状态中?在数据库中有活动时,以 util 模式启动数据库会成功吗?来自客户机的线程在被取消之前将保持对于它们正在使用的资源的锁定吗?


答:
当发出一条启动命令时,它通常立即生效。因此当请求后续要求时,应用程序会收到一个 unavlbl 资源。
star 根据您选择的约束模式耗尽所有要求。只对于 util,它才立即耗尽所有要求。它不关心线程来自哪里。所有线程的行为都相同。

问:我需要从多个数据库访问数据。其中一个数据库具有 2000 万个客户机记录。我需要访问一些其他数据库,它们具有匹配的客户机标识和一些关于这些客户机的其他匹配信息。我的问题是 :

我如何使用单个 SQL 语句访问这些数据库?
我需要创建 synomyms/alias 以完成该任务吗?有其他办法可以使用利用 SPUFI 或 QMF 的一条 SQL 语句来访问多个数据库吗?我不倾向于使用程序来完成该操作。

我如何绑定从多个数据库中访问数据包?我是说,什么是这类包的 qualifier/owner?
答:
如果在大型机上,则您只能进行简单的连接。请参阅 DB2 SQL 参考书以获取 Jion 的语法和规则。您最多只能访问 15 个表/视图,而不管它们是属于一个数据库还是多个数据库。

如果您在客户机服务器环境中的 DB2 UDB 中,则没有任何捷途径。 请尝试使用别名。即使这样,连接还可能不工作。我未试过最新的 DB2 UDB LUNO 版本,因此不能提供明确的答案。

问:我希望在 DB2 中的 SQL Server 中出现 IDENTITY 这样的内容,例如,在 SQL Server 中,我创建一个如下的表:

create table test (tid int identity(1,1), tname char(10))
然后我使用“tid” 标识一行。
在 DB2 中有类似的内容吗?我只需要某个标识来标识表中的一行。我知道我可以通过编程来创建它,但 DB2 可以为我做此事吗?

答:是的,DB2 V7.1可以支持它。

问:在 NT 的 UDB V6.1 中 - 遇到错误,指示“SYSTEM” 没有执行命令的权限。该权限 ID 未被使用,但不知何故“控制中心”却指示它正被使用。有人知道这是怎么回事吗?

答:没关系 — “控制中心”的 JDBC 服务需要由具有本地 NT 管理员权限的本地 ID 来启动。

问:我们在 10 个不同的 Windows NT 中大约安装了 10 个 websphere。所有这些 websohere 都指向一个 DB2/UDB,它安装并运行在一个单独的 Windows NT 服务器上。该 DB2 服务器用于这些 websphere 的配置、会话跟踪等。我希望知道是否有人知道有一些工具、硬件、软件可以监视 DB2 服务器并且可以在主要 DB2 服务器关闭时自动切换到后备 DB2 服务器。

答:您可以使用 NT 群集解决方案。两个带有本地磁盘和一个 ServeRaid 这样的共享磁盘的 NT 机器。该 Raid 系统可以由 Win NT 的群集解决方案共享。您在两个机器的本地磁盘上安装 DB2。在共享磁盘中创建一个 DB2 实例并将它添加到 NT 群集中。然后在磁盘的该实例下创建您的 DB。DB 运行在这些机器的其中一台上。如果一台机器失败,它可以在另一台机器上重新启动。

问:我在 NT 服务器中安装了一个 DB2,并且想要在 NT 服务器中以同样方法另外配置 30 个 DB2。有允许我执行此操作,而不必单独配置每个机器的工具或选项吗?

答:响应文件发生器实用程序从一个现有的、已配置的 DB2 UDB 产品中创建响应文件。该响应文件可以用于在其他机器上创建正确的安装。

请使用命令 db2rspgn -d x:/path 来创建响应文件,文件名是固定的。
要在安装期间使用响应文件,请使用
对于 Windows
setup /U x:/path/response_file /L y:/path/logfile
/U -> 指定响应文件。
/L -> 记录文件
对于 UNIX
db2setup -r /path/response_file
该安装的记录被写入到 /tmp/db2setup.log 中

XML Extender Development 技巧
适用的版本: 版本 7
适用的平台: Windows NT

故障现象

当用户试图调用 UDF 时,接收到下列 DB2 错误消息。

SQL... 用户定义函数 ... 不能在 "/db2xmlfn"'中访问..

解决方法

· 更改系统环境变量 PATH,以便 XML Extender bin 目录 (例如 c:/dxx/bin) 在路径的开头。

· 再引导机器。

此问题可能在路径太长的时候发生。

DB2 v7.1 现在具有仓库功能,很大比重上基于Visual Warehouse 产品。如果你在一台已经装有Visual Warehouse 的机器上安装 DB2 v7.1, 那么 DB2 v7.1 将会卸载 Visual Warehouse 并将 Visual Warehouse 的控制数据库转换成 DB2 7.1的格式。

这个过程是不可以逆转的,所以在安装前,请先确定您和客户的计划。