当使用来自于不同供应商的不同数据库系统时用户和数据库管理员将不可避免地碰到在这些产品中各不相同的特性和功能通常可在以下方面发现这些差异
◆受支持的 SQL 方言中的不同语法
◆数据库管理器应用程序界面
◆不同的管理工具及其用法
为了使得将数据库和应用程序从 OracleSybase 或 Microsoft SQL Server 等数据库产品迁移到 IBM DB Universal Database(UDB)更容易本文将展示一些可行的 DB UDB 功能实现而且这些功能在其他数据库系统中也可获得这些实现将涉及创建存储过程和用户定义函数(UDF)以实现那些常常被请求的功能
在下载小节中您将找到这些过程和函数的源代码以及包含了 CREATE PROCEDURE 和 CREATE FUNCTION 语句的 SQL 脚本如果您对确切的实现细节很感兴趣就请查阅这些代码一旦编译并链接了源代码(或安装了预编译的库)以及在数据库中注册了这些过程和函数之后您就可以按本文实例所演示的那样来使用它们了另外值得注意的是这些过程和函数可用于 DB UDB 版本 和版本
清除表
当从 Oracle 迁移到 DB 时所碰到的一个普遍问题就是 TRUNCATE 命令在 Oracle 中执行时该命令不用借助一个或多个 DELETE 操作就可快速地清除表中所有内容 DELETE 操作需要进行大量的日志记录
DB 的 IMPORT 功能提供了完成相同功能的方法只要使用 REPLACE INTO 子句以及将一个空文件指定为数据源在该情况下表中所有的行都将被快速清除并且只使用一条日志记录接着就从给定的文件中导入新的数据而对于一个空文件就不会导入任何内容从而在该操作结束时清除了该表
要实现该功能我们可以利用 DB 定义的叫做 sqluimpr() 的 C API 函数来以程序的方式将数据导入数据库的表中我们将这个 API 包装到存储过程中以便可通过 SQL 接口用于所有的应用程序而无需考虑编程的语言清单 中展示了存储过程 TRUNCATE_TABLE 的签名
清单 过程 TRUNCATE_TABLE的签名 >>TRUNCATE_TABLE(schema_nametable_name)><
VARCHAR() 类型的参数 schema_name 指定模式用以在其中找到表如果模式名外加了双引号就将其看成定界名称(混合大小写的和特殊的字符)如果模式名为 空 即未指定模式那么则要查阅 CURRENT SCHEMA 专用寄存器来确定所要使用的模式 VARCHAR() 类型的参数 table_name指定将被清除的表的未限定名称加上显式或隐式定义的模式名就可惟一地识别出表如果表名外加了双引号就将其看成定界名称(混合大小写的和特殊的字符)
如果输入参数 schema_name 为 空 则由该过程的逻辑来确定默认模式否则就删除现有模式名上的双引号或者将未加引号的模式名转换为大写体对于表名同样如此比如最后表名上的双引号会被删除或者未加引号的表名会被转换为大写体接着我们通过查询 DB 目录视图 SYSCATTABLES 来证实该表是否存在现在就可以启动导入了先准备好必要的参数其中使用的文件是 /dev/null(Windows 上的 NUL 文件)因为它总是存在并且不包含任何内容也就是可用作数据源的空文件同样/dev/null(Windows 上的 NUL 文件)将用于进行导入所需的消息文件如果成功地启动了导入该过程就会成功返回如果碰到错误则与消息文本一起返回 SQLSTATE 以指示错误清单 演示了过程 TRUNCATE_TABLE 的执行可以在下载小节中找到该脚本( truncate_exampledb)的源代码
清单 测试过程 TRUNCATE_TABLE /* create and insert some values into the table tab */
CREATE TABLE tab (col INTEGER NOT NULL PRIMARY KEY col VARCHAR() )
DBI The SQL command completed successfully
INSERT INTO tab VALUES ( some data ) ( NULL )
DBI The SQL command completed successfully
/* verify the current contents of table tab */
SELECT * FROM tab
COL COL
some data
record(s) selected
/* Call the truncate stored procedure for the DBINST schema and the table tab */
CALL truncate(DBINST tab)
Return Status =
/* Verify that the table contents have been truncated */
SELECT * FROM tab
COL COL
record(s) selected
/* Insert some new values into the tab table */
INSERT INTO tab VALUES ( some new data ) ( NULL )
DBI The SQL command completed successfully
SELECT * FROM tab
COL COL
some new data
record(s) selected
/* Call the truncate procedure with a NULL schema */
CALL truncate(NULL tab)
Return Status =
/* Verify that the table contents have been truncated */
SELECT * FROM tab
COL COL
record(s) selected
Sybase 的 host_name 函数
Sybase 数据库中的 host_name( ) 函数返回的是 客户机进程(非 Adaptive Server 进程)的当前主机名也就是运行该应用程序的计算机的主机名而非数据库服务器的主机名
清单中展示了用户定义函数 HOST_NAME 的签名 >>HOST_NAME( )><
该函数访问存储在 DBINFO 结构中的应用程序 ID 并解码客户机的 IP 地址(它是应用程序 ID 的一部分)然后便使用 C 库函数gethostbyaddr来解析该 IP 地址的名称该函数在必要时将访问名称服务器或其他源(比如 /etc/hosts)
IP 地址是应用程序 ID 中前面 字节的编码或者使用 *LOCAL 来代表本地连接对于本地连接解析的是 IP 地址为 的主机名
注意
由于 DRDA 的需求如果非本地 IP 地址的第一个字符初始为到就将之映射到字母G到P而在该名称查找之前要将该映射反过来进行
清单 演示了 HOST_NAME 函数的执行可以在下载小节中找到该脚本 host_name_exampledb 的源代码
清单 测试函数 HOST_NAME( )
下面这个例子测试演示了用以获取本地连接主机名的函数的执行
在该场景中DB 数据库驻留在一个本地 AIX 机器上
地址 在 /etc/hosts 文件中被映射到计算机名 demoaix /* connect to the local database */
connect to sample
Database Connection Information
Database server = DB/
SQL authorization ID = DBINST
Local database alias = SAMPLE
/* execute the host_name function */
values host_name()
demoaix
record(s) selected
下一个例子测试演示了远程连接上的函数的执行
在该场景中DB 数据库与上面一样驻留在同一 AIX 机器上
到 AIX 上数据库的连接是由一个 Windows 客户机建立的;
该客户机的名字为 mycomputer /* The database samplaix is an alias for the SAMPLE database on AIX */
connect to samplaix
Database Connection Information
Database server = DB/
SQL authorization ID = DBINST
Local database alias = SAMPLAIX
/* execute the host_name UDF against the remote database 鈥?
it returns the name of the computer of the client connection */
values host_name()
mycomputer
通过触发器或用户定义函数调用存储过程的 UDF
当迁移到 DB 时碰到的另一个普遍问题就是其他 RDBMS 可以通过触发器或函数调用存储过程虽然 DB 已经承诺在未来版本中包含该功能但是我们将展示如何使用 DB 的当前版本来实现该功能即通过创建一个将对存储过程发出调用的 UDF 来实现
清单中展示了用于该目的的用户定义函数 CALL_PROCEDURE 的签名
清单 用户定义函数 CALL_PROCEDURE 的签名 >>CALL_PROCEDURE(procedure_nameparameter_list>
>database_nameuser_namepassword)><
VARCHAR() 类型的参数 procedure_name 指定要被调用的存储过程的全限定名 —— 在传递多个参数时要用逗号进行分隔该字符串将被粘贴到用于调用过程的 CALL 语句中因此其语法需要符合 SQL CALL 语句的要求 VARCHAR() 类型的参数 database_name 指定要执行该存储过程的数据库的别名存储过程不一定要驻留在同一数据库中 VARCHAR() 类型的参数 user_name 和 VARCHAR() 类型的参数 password 用于确定连接数据库以及执行该过程时所使用的注册信息
该函数调用当前数据库中的存储过程它建立新的连接之后就通过过程名和作为输入参数而提供的参数来执行 CALL 语句该 UDF 返回 (零)表明 CALL 语句(以及相应的 CONNECT 和 CONNECT RESET 语句)执行成功否则将返回 DB 命令行处理器(Command Line ProcessorCLP)的返回码和一条提供了更多信息的出错消息清单 演示了函数 CREATE_PROCEDURE 的执行可以在下载小节中找到该脚本( trig_calls_procdb)的源代码
清单 测试函数 CREATE_PROCEDURE( )
下面这个例子测试演示了从触发器调用包含一个参数的存储过程
在该示例中我们创建 t 和 t 这两个表带有一个输入参数(p)
的过程(abc)以及一个触发器(ins)在执行触发器时它将调用该过程
然后过程将会将 num 列的新值(ll)插入到表 t 中
这可以通过以下操作来测试在表 t 上执行插入后对 t 发出 select
来检验该表内容 —— 进而检验该过程是否成功执行 create table t ( col int)
DBI The SQL command completed successfully
create table t ( col int )
DBI The SQL command completed successfully
create procedure abc(in p int) begin insert into t values(p); end
DBI The SQL command completed successfully
create trigger ins after insert on t referencing NEW as new for EACH ROW MODE
DBSQL BEGIN ATOMIC values ( call_procedure(DBINSTABC char(l * )
SAMPLE DBINST dbinst) ); END
DBI The SQL command completed successfully
insert into t values
DBI The SQL command completed successfully
/* validate that the trigger has fired it should update t */
select * from t
COL
record(s) selected
下一个例子演示了在 UDF 中调用包含了两个参数的存储过程在该例中我们创建表( c )带有两个输入参数的存储过程( abc )以及带有两个参数(parmparm)的 UDF( udf_withcall )当执行该 UDF 时它将调用存储过程然后该存储过程会将由 UDF 传递给它的值插入表 c对表 c 进行 select 将验证表 c 的内容以及存储过程是否执行成功可以在下载小节中找到该脚本( udf_calls_procdb)的源代码 create table c ( a int check (a <> ) a int )
DBI The SQL command completed successfully
create procedure abc(in p int in p int) begin insert into c values(pp); end
DBI The SQL command completed successfully
create function udf_withcall (parm int parm int)
returns int
Language SQL
not deterministic
external action
return call_procedure(DBINSTABC char(parm) |||| char(parm) SAMPLE DBINST dbinst))
DBI The SQL command completed successfully
select udf_withcall() from sysibmsysdummy
record(s) selected
/* verify that the UDF has called the procedure and updated the table */
select * from c
A A
record(s) selected
构建例程
为了构建 C 例程(UDF 或 存储过程)必须首先对其进行预编译编译以及链接该过程可通过批文件 bldrtn (UNIX/LINUX 上)或 bldrtnbat (Windows 上)自动完成该文件包含在随 DB 一同安装的样本中可以在 UNIX/LINUX 上的 /sqllib/samples/c 目录或 Windows 上的 bldrtn [dbname userid password]
如果未提供 dbname那么批文件会将之默认为 SAMPLE 而 userid 和 password 则被默认为当前会话的用户 ID 和口令