IBM Support

DB2 for i 7.1:并非看起来那么简单

Technical Blog Post


Abstract

DB2 for i 7.1:并非看起来那么简单

Body

今天的这篇博客,我们将给您详细的描述关于DB2 for IBM i 最新版本的一些激动人心的功能!这个专题将分为两个部分,本篇文章是上半部分的内容。
 
 
你确信你已经了解 IBM i 最新版本都有哪些令人激动的功能了吗?可别太肯定哦!
 
在 IBM i 7.1 的声明中,着重介绍了 XML 的支持以及列级别加密 (column-level encryption) 的实现。 不可否认,上面两个功能是非常非常重要的。但是除此以外,DB2 for i 7.1 版本还包含了众多你可能还未曾听说的功能。对于开发者以及系统管理员而言,这些功能也同样的具有很强的吸引力。
这些功能包括:客户端内嵌 SQL 对于存储过程返回结果集的支持,AQP (Adaptive Query Processing),新语法功能 MERGE 的支持,以及 IBM i Navigator 状态监视器对于长时间运行的数据库操作的监控支持。本文将介绍如何使你的应用程序和系统从 DB2 for i 7.1 版本的大小新功能中获益。
 
 
XML 的集成
XML 在 DB2 for i 7.1 版本中的集成是在新版本发布之时被广为宣传的。该集成主要由三大核心技术实现:新的数据类型 – XML,annotated decomposition of XML documents, 以及一些SQL XML publishing functions。如果您想了解更多关于 XML 的内容,请阅读Jon Triebenbach 的文章,他会告诉你更多关于 XML 的细节。
 
 
列级别加密的实现
另外一个激动人心的新功能是对于列级别加密的支持。现如今,已经有太多关于 IT 数据是否安全的报道与讨论,所以你一定不会对于这个新功能感到意外。DB2 for i 7.1 版本基于 field-level exit routine ( FieldProc) 而实现了列级别的加密。FieldProc 功能允许开发人员注册自己的 Field level 的 ILE 程序(如下例所示), 这样 DB2 就可以在记录被更新或者读取的时候自动调用用户的程序。
 ALTER TABLE orders ALTER COLUMN cardnum
                      SET FIELDPROC mylib/ccpgm
在用户自己的应用程序中,可以编写数据加密的相关逻辑,并在读写操作中被自动调用。令人激动的是,对于众多 IBM i 的老客户来讲,现有的 RPG 和 COBOL 的加密应用只需稍做调整就能使用。你只需要把 FieldProc 的程序对象 (本例中的mylib/ccpgm)注册到含有敏感信息(需要加密的信息)的数据列上就可以了。使用 ALTER TABLE 语句可以注册 FieldProc 到 DDS 创建的物理文件上,但更为安全的是先将物理文件转换成 SQL 数据表定义。这是因为, 如果将在 FieldProc 注册以后再去用 CHGPF 去应用新的 DDS, CHGPF 命令将没有任何提示的从物理文件上去除已经注册过的
FieldProc,原因是加密相关的列属性只能使用 SQL 语句进行定义。
FieldProc 可以处理任何形式的加密,但是最常用的加密算法是我们更为推荐的,比如 AES 加密算法。第三方软件厂商 Linoma Software 和 Patrick Townsend Security Solutions 已经将他们的加密软件升级,以支持 DB2 的 FieldProc 功能。这样用户就有了另外一种选择 – 使用已有的加密软件而不用为了使用 FieldProc 而花精力去写自己的加密程序。FieldProc 的实现需求在 SQL Programmer’s Guide 中有详细的介绍。
 
 
存储过程的结果集整合(Stored Procedure Result Set Integration)
对于众多 RPG 和 COBOL 的开发人员而言,DB2 for i 7.1 版本有一个会令他们十分开心的功能:在嵌入式 SQL 中获得存储过程返回的结果集。DB2 for i SQL 语言现在支持两种新的语句:ASSOCIATE LOCATOR 和 ALLOCATE CURSOR。他们使得 RPG 和 COBOL 能够非常方便的获得结果集。下面的例子展示了如何在 SQL 存储过程中使用这些新语句,事实上,这些新语句可以在任何 SQL Routine 中使用(包括存储过程,函数以及触发器)。
 DECLARE sprs1 RESULT_SET_LOCATOR VARYING;
 CALL GetProjects(projdept);
 
 ASSOCIATE LOCATOR (sprs1) WITH PROCEDURE GetProjects;
 ALLOCATE mycur CURSOR FOR RESULT SET sprs1;
 SET totstaff=0;
 myloop: LOOP
    FETCH mycur INTO prname, prstaff;
    IF row_not_found=1 THEN
      LEAVE fetch_loop;
    END IF;
    SET totstaff= totstaff + prstaff;
    IF prstaff > moststaff THEN
      SET bigproj = prname;
      SET moststaff= prstaff;
    END IF;
 END LOOP;
 CLOSE mycur;
第一步是声明一个结果集的 Locator 变量,这个变量将在ASSOCIATE LOCATOR 语句中被使用。ASSOCIATE LOCATOR 语句在存储过程(GetProjects)被调用以后执行,执行完毕后,Locator 变量(sprs1)就与存储过程 GetProjects 返回的结果集相关联了。接下来要做的是,将得到的结果集和游标(mycur)用ALLOCATE CURSOR语句相关联起来。简单来讲,你可以认为在ALLOCATE CURSOR 语句当中,游标被同时定义并且打开。在这个过程中,我们并不需要任何的 SELECT 语句,因为返回结果集的存储过程已经扮演了这个角色。
一旦游标分配完毕,我们的程序就可以利用 FETCH 语句读取所有的存储过程返回的结果集,这个跟我们以前对 SQL CURSOR 的使用没有区别。DESCRIBE CURSOR 语句在这里也同样可以使用,它能够动态的决定返回的结果集中有多少列以及他们的属性。在上面的例子里,列的数量和属性已经在程序之前确定好了。
 
 
RPG 对长列名的支持
在过去,RPG 的开发人员只能在 RPG 代码中使用 10 字节长的列名。现在,他们可以使用更有意义的长列名了。这个新功能是通过 ALIAS 关键字来实现的,如下图:
 
 图像
图1: 新的 ALIAS 关键字可以使用户在 RPG 中使用长列名
 
外部定义(external declaration)语句引用了一个用 SQL 创建的 customer 表。在该表中,数据列的长度都超过了 10 个字节。ALIAS 关键字的使用,使得长 SQL 列名得以使用。编译器会负责将长列名赋给数据结构,这样 RPG 的编程人员可以直接使用这些长名。
 
 
存储过程功能的增强
在 DB2 for i 7.1 版本中,开发人员不仅可以更容易的获取存储过程的结果集,同时他们在调用存储过程时也可以更加的灵活。现在,存储过程可以在被调用时使用表达式。下面的例子表明了如何在调用存储过程时将两个表达式作为参数输入。在以前的版本中,开发人员需要先做很多步骤把表达式的返回值输入到变量中,然后再使用变量作为调用存储过程时的参数。现在,一切都变得简单了 – 使用表达式直接作为存储过程的参数输入。
CALL myprocedure('ABC', UPPER(company_name), company_discountrate*100 )
 
 
SQL 数组
利用新的数组功能,我们可以把一组数据作为参数传递给 SQL routines。 数组功能的支持能够在 SQL 语句执行阶段存储某些数值的集合。在 SQL 数组被声明之前,我们必须用 CREATE TYPE 语句先创建一个基于数组的用户定义类型,如下例:
CREATE TYPE part_ids AS CHAR(3) ARRAY[10];
CREATE TYPE intarray AS INTEGER ARRAY[5];
第一个数组存储不超过10个的数据,每个数据不能超过3个字节。第二个数组用来存储整数类型的数据。SQL 数组现在只支持一维数组,同时,数组类型不能作为 SQL 数组中的元素存在,这些都是 DB2 for i 7.1 版本的限制。
下面一个 SQL 存储过程展示了一个使用数组类型的示例。在该示例中,传入参数被定义为名为 part_ids 的数组类型。存储过程的程序中,会将查询语句返回的结果集同传入的数组中的元素进行比较。现在,只有 Java 通过 JDBC 或者另外一个 SQL routine 可以向存储过程传递数组类型。
CREATE OR REPLACE PROCEDURE List_Parts
     (IN inparts part_ids)
DYNAMIC RESULT SETS 1
LANGUAGE SQL
BEGIN
DECLARE cur1 CURSOR WITH RETURN
  FOR SELECT t1.id, part_qty, part_type
    FROM parts, UNNEST(inparts) AS t1(id) WHERE t1.id = part_num
    IF CARDINALITY( inparts )>5 THEN
      SIGNAL SQLSTATE '38003'
        SET MESSAGE_TEXT='Part limit exceeded';
    END IF;
    OPEN cur1;
END;
DECLARE CURSOR 语句包含对于输入数组的引用,也就是 inparts。UNNEST 操作符将 inparts 数组中的数据打散成行,这样数组中的数据就变成有多条记录的虚拟的数据表,游标定义可以将其跟 part 表关联起来并返回数据。
在游标被打开并返回结果之前,上面的例子利用 CARDINALITY 函数来判断输入的数组是否含有比逻辑需要更多的元素(比如是否大于5个元素)。下面一张图显示了 List_Parts 所输出的结果集,这里假设 in_parts 含有W12,S55以及M22三个值。新的数组支持还提供了 ARRAY_AGG 函数,该函数使得 SELECT 语句所返回的结果集可以被赋于一个数组变量。

图像
图2. List_Parts 所返回的结果集
 
 
SQL Replace 的支持
有经验的 SQL 存储过程开发人员一定注意到了,在上面定义 List_Parts 的例子中同时引入了一个新的语法 – OR REPLACE 语句。该语句提供了跟 使用 IBM i CL命令创建程序中使用的 REPLACE(*YES) 参数的作用是一样的。
OR REPLACE 功能在创建 SQL 对象之前先判断是否该对象已经存在,如果存在,会先删除原有的对象。这样使得开发人员不用再在每次创建对象之前先使用 Drop 功能删除对象。更加方便的是,OR REPLACE 语句在替换对象时,仍然保留之前所有对于该对象的权限。OR REPLACE 语句可以被应用于下面的场景:
CREATE FUNCTION
CREATE TRIGGER
CREATE ALIAS
CREATE SEQUENCE
CREATE VIEW
CREATE VARIABLE
 
 
SQL 全局变量的支持
CREATE VARIABLE 语句在 7.1 新版本中也应博得眼球。该功能实现了对全局变量定义的支持。在这个功能中,所谓“全局”并不是说变量可以在不同作业以及连接之间被共享。“全局”的含义是变量可以存在于整个作业或者连接的生命周期。即使不能在多个作业中共享,全局变量仍然带来了诸多便利,比如可以在不同的 SQL 语句之间,或者不同的触发器以及视图之间共享变量。很多开发人员经常会问,能否实现向触发器传递参数。下面这个例子就告诉你如何实现这一需求:
CREATE VARIABLE batch_run CHAR(1) DEFAULT 'N';
    CREATE TRIGGER upper_case
         BEFORE INSERT ON orders
         REFERENCING NEW AS n FOR EACH ROW
    WHEN (batch_run='N')
      BEGIN
           SET n.shipcity = UPPER(n.shipcity);
           SET n.shipregion = UPPER(n.shipregion);
           SET n.shipcountry = UPPER(n.shipcountry);
      END;
       …
     /* Any batch job that would cause the trigger to be invoked
         would need to change the value of the variable to 'Y'   */        
      VALUES 'Y' INTO batch_run ;
上述触发器在 WHEN 语句中引用了全局变量。每次当触发器被触发时,会判断当前作业中的全局变量是否被定义为批处理作业。当全局变量第一次在当前作业中被引用时,DB2 for i 会自动的创建一个全局变量的实例。当然,在这个例子中,如果全局变量 batch_run 被定义为 ‘Y’ 的时候,WHEN 逻辑中的语句就会被忽略了。
如果没有全局变量,我们需要手工使用 CHGPFCST CL 命令去禁用触发器。这个 CL 命令有时候是难以执行的,因为它需要获得排他锁才能执行禁用触发器。上面的例子,如果不需要执行 WHEN 语句中的逻辑,那么我们不用对 batch_run 全局变量做任何操作,因为在定义之初,它的默认值就是 ‘N’。

 (未完待续)

[{"Business Unit":{"code":"BU058","label":"IBM Infrastructure w\/TPS"},"Product":{"code":"SWG60","label":"IBM i"},"Component":"","Platform":[{"code":"PF025","label":"Platform Independent"}],"Version":"","Edition":"","Line of Business":{"code":"LOB57","label":"Power"}}]

UID

ibm11146310