生成行标识
让我们从由其它数据库迁移到 DB2 的应用程序开始。如果移自 SQL Server,您可能厌倦了关于关系纯洁性的说教并希望了解 IDENTITY。
下面是如何用 DB2 v7 创建 IDENTITY 列:
CREATE TABLE T1
![]()
(C1 DECIMAL(15,0) GENERATED BY DEFAULT AS IDENTITY
![]()
(START WITH 10),
![]()
C2 INTEGER)
还有在内存中高速缓存标识值的选项,这使插入更快,但如果您的系统在生成 IDENTITY 值时遭受硬件或软件崩溃,那么将在标识序列中留下间隔。缺省情况是一次增加整数 1,但您也可以按其它值(2 和 10 等)增加。插入后,您会对生成的值自然地产生好奇。为了应用程序的下一段逻辑,您可能需要知道这个值。在发行说明(Windows 上 x:\sqllib\release.txt)中记录的名为 IDENTITY_VAL_LOCAL() 的函数可为您检索这个值。
IDENTITY 在每个表中是唯一的。那些 Oracle 迷将很高兴得知 DB2 的版本 7,修订包 3 将把 SEQUENCE 列带入 DB2。序列在整个数据库中是少有的 — 这对于在多个表中使用的值很有用。您也可以在序列中循环以重用这些值。SEQUENCE 和 IDENTITY 不是数据类型:它们使用象 SMALLINT、INTEGER 或小数位是零的 DEC 那样的现有数据类型。INT 和 BIGINT 是最好的选择,它们能给您良好的性能和适当的数值范围。还允许负值。
生成人工主键还有其它方法。如果一次只有一位用户访问表(并且一次只插入一行),则触发器很不错。将您的主键列定义为缺省非空值,这样当在 INSERT 中没有指定它时,它就得到一个虚设的值(触发器将重写这个虚设的值):
CREATE TRIGGER AutoIncrement NO CASCADE BEFORE
![]()
INSERT ON Foobar
![]()
REFERENCING NEW AS n
![]()
FOR EACH ROW MODE DB2SQL SET (n.col1) =
![]()
(SELECT COALESCE(MAX(col1),0) + 1 FROM Foobar )
DB2 还有一个名为 GENERATE_UNIQUE 的函数。这个函数将节点号(用于多分区数据库)与时间戳记结合,因此它可以与企业扩展版本(EEE)一起使用。IDENTITY 和 SEQUENCE 在 DB2 的下一个主要版本出现前还不能与 EEE 一起使用。GENERATE_UNIQUE 有两个缺点:数据类型(CHAR(13) FOR BIT DATA)不是按顺序递增,并且不象数值数据类型那样易于使用。
更简单的解决方案是标量子查询表达式:
INSERT INTO Foobar (key_col, ...)
![]()
VALUES (COALESCE((SELECT MAX(key_col) FROM Foobar) +1, 0) ...)
获得一屏数据
这些方法对于那些在数据库和应用程序投入生产以前有机会进行一些设计工作的模式和应用程序来说是不错的。但您还记得那两个以 A(ARIES(航班订票环境仿真)和 ACID(原子性、一致性、隔离和持久性))开头的 4.5 字母单词吗?如果您预定了航班,那么您希望他们在您到达机场时记得这回事。这就是持久性:有用的数据是持久的。这意味着即使您定义了一个好的主键,有些人可能会查询结果集的“前二十行”,而不管结果集中有多少行。更糟的情况是有人要求您显示第 21 行到 40 行。但等一下,您会提出异议,关系表中的行没有顺序!对于希望在他们的 Netscape 浏览器中一次看到二十行的用户而言,您就好象在说冰岛语。DB2 允许您实时地给结果集排序,并可以从该结果集的开始或结尾部分提取任意数量的行:
SELECT NAME FROM ADDRESS
![]()
ORDER BY NAME
![]()
FETCH FIRST 10 ROWS ONLY
![]()
SELECT NAME FROM ADDRESS
![]()
ORDER BY NAME DESC
![]()
FETCH FIRST 10 ROWS ONLY
ORDER BY 将强制在内存中对整个结果集进行排序,所以,为了提高 DB2 服务器性能,我们不这么做(尽管只向客户机发送 10 行可能会提高网络性能)。如果您不关心顺序并且只想知道至少有 10 行符合结果集,则清除 ORDER BY 以省去 DB2 服务器上的排序:
SELECT NAME FROM ADDRESS
![]()
FETCH FIRST 10 ROWS ONLY
于是现在我们已看到您给行编号并且任意选择了一个子集。假设我们因某些性能上的好处而给行编号,这必将破坏关系模型。我们几乎完全妥协了,并且已经犯了关系七宗罪中的六宗。还有一条关系誓约您没有触犯:让我们实时地给行编号,牺牲掉性能和关系纯洁性吧。我们如何证明这样做的正确性呢?在因特网上谴责它吧。
向使用浏览器的客户显示公司数据显然证明了违背对关系纯洁性和性能推崇所作的承诺。您可以用 rownum 或 rank 函数实时地给结果集赋予行号。下面我们为用来记录地址的表中的行排序,并选择第 11 行到第 20 行。结果集由名称和实时创建的名为 rn 的列(它给行编号)组成:
SELECT * FROM (SELECT NAME, rownumber() OVER
![]()
(ORDER BY NAME)
![]()
AS rn FROM ADDRESS)
![]()
AS tr WHERE rn BETWEEN 11 and 20
rank 更为复杂,并且它允许您以排序的顺序标识联系,对于足球联赛非常理想:
create table football (team char(10), points int)
![]()
insert into football values ('United', 20)
![]()
insert into football values ('Arsenal', 20)
![]()
insert into football values ('LivERPool', 10)
![]()
select rank() over
![]()
(order by points desc) as place,
![]()
team, points
![]()
from football
![]()
PLACE TEAM POINTS
![]()
1 United 20
![]()
1 Arsenal 20
![]()
3 Liverpool 10
清空表 — 无需通知日志记录程序(截断表)
现在你已经得到了很多精巧的方法来处理你的数据了,我们再来学习一个小把戏。其他的数据库产品有被称为“截断表”的功能,即在不进行日志记录的情况下删除表中的所有数据,而保留表的结构(如果不想保留表结构,我们就使用 DROP TABLE 命令了)。如果想在 DB2 中得到这种功能,可以执行带有 REPLACE 选项的 LOAD 命令,并使用一个 0 字节的文件作为导入数据源,由于 DB2 的 LOAD 操作是不做日志的,所以可以通过这个小骗局来达到我们的目的。
