Prayer

在一般中寻求卓越
posts - 1256, comments - 190, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

DB2中的标识列

Posted on 2010-05-12 23:53 Prayer 阅读(752) 评论(0)  编辑 收藏 引用 所属分类: DB2
标识列的基本概念
标识列(IDENTITY COLUMN)是 DB2 数据库提供的一种可自动为表中的每一行生成唯一数值的方法。比如用来表示一些定单号、流水号、文件编码等信息。一个表中只能有一个列被定义为标识列。
 
在DB2中提供了两种标识列值
    √GENERATED ALWAYS AS IDENTITY      --始终生成
    √GENERATED BY DEFAULT AS IDENTITY  --缺省情况下生成

说明:1)定义为始终生成的标识列不允许在 SQL 语句中覆盖值。它的值总是由 DB22 数据管理器生成;不允许应用程序提供显式的值。但是DB2不保证"始终生成"列中的值是唯一的。要保证该列中的值是唯一的,应该对该列定义唯一索引。

      2)定义为在缺省情况下生成的标识列向应用程序提供了显式地为标识列提供一个值的方法。若未给出任何值,则 DB2 将生成一个值,但在此情况下,也不能保证该值的唯一性。“在缺省情况下生成”用于数据传播(复制现有表的内容),或用于一个表的卸装和重新装入。
         3)在DB2的内部实际上系统自动为该标识列创建了一个SEQTYPE='I'的序列
 
标识列特别适合用于生成唯一主键值。应用程序可使用标识列来避免当一个应用程序在数据库外部生成它自己的唯一计数器时可能会导致的并发性和性能问题。例如,一种常见的应用程序级实施是维护一个只有一行的表,它包含一个计数器。每个事务都锁定此表,增大该数字,然后落实;即,每次只有一个事务可以增大计数器。相反,若通过标识列维护该计数器,因为事务不锁定该计数器,所以可以获得较高级别的并发性。一个未落实的已增大该计数器的事务不会阻止后续事务也增大该计数器。

标识列的计数器的增大(或减小)独立于事务。若给定的事务两次增大一个标识计数器,则该事务可能会在生成的两个数字之间看到一个间隔,因为可能有其他事务正在增大同一标识计数器(即,将行插入同一个表中)。若一个应用程序必须要有连续范围的数字,则该应用程序应对带有标识列的表进行互斥锁定。因为会造成丢失并发性,所以必须对此决定作权衡。此外,有可能因为生成标识列的值的事务已回滚,或因为高速缓存值序列的数据库在指定所有高速缓存的值之前被取消激活,从而导致给定的标识列出现数字之间生成间隔的情况。

标识列生成的顺序数字具有下列附加属性:

    ■值可以是任何小数位为零的精确数字数据类型;即,小数位为零的 SMALLINT、INTEGER、BIGINT 或 DECIMAL。(单精度和双精度浮点类型被认为是近似数字数据类型。)
    ■连续值之间可以有任何指定的整数增量。缺省增量是 1。
    ■标识列的计数器值是可恢复的。若发生故障,则从日志重新构造计数器值,因此可以保证继续生成唯一的值。
    ■可以将标识列值存入高速缓存,以获得更好的性能。


定义一个具有标识列的表
D:\DB2\BIN>db2 [email=-td@SQL]-td@SQL[/email]> create table test_idl1
SQL> ( no int GENERATED BY DEFAULT AS IDENTITY,  --缺省情况下生成的标识列
SQL>   name char(8)
SQL> )@
DB20000I  SQL命令成功完成。
SQL>
SQL> create table test_idl2
SQL> ( no int GENERATED ALWAYS AS IDENTITY,      --始终生成的标识列
SQL>   name char(8)
SQL> )@
DB20000I  SQL命令成功完成。

体验一下:
SQL> insert into test_idl1(name) values('one')@
DB20000I  SQL命令成功完成。
SQL> insert into test_idl1(name) values('two')@
DB20000I  SQL命令成功完成。
SQL> insert into test_idl1(no,name) values(3,'three')@

DB20000I  SQL命令成功完成。
SQL> select * from test_idl1@
NO          NAME
----------- --------
          1 one
          2 two
          3 three

  3 条记录已选择。

SQL> insert into test_idl2(name) values('one')@
DB20000I  SQL命令成功完成。
SQL> insert into test_idl2(name) values('two')@
DB20000I  SQL命令成功完成。
SQL> insert into test_idl2(no,name) values(3,'three')@
DB21034E  该命令被当作 SQL 语句来处理,因为它是无效的“命令行处理器”命令。在SQL 处理期间,它返回:SQL0798N
不能为定义为 GENERATED ALWAYS 的列 "NO" 指定值。  SQLSTATE=428C9
SQL>
SQL> select * from test_idl2@
NO          NAME
----------- --------
          1 one
          2 two
  2 条记录已选择。
SQL>


通过数据字典,我们看一下内部的标识列是怎么通过sequence定义的

继续以上面的两个测试表为例,看一下DB2内部是如何实现这两个标识列的.
首先,我们应该有一个基本概念,没看到前面介绍的同志,要清楚,在DB2中,标识列实际上在系统中定义了一个对应的sequence来实现的.

SQL> SELECT tabschema,tabname,colname,identity,generated FROM SYSCAT.COLUMNS where tabname in ('TEST_IDL1','TEST_IDL2')@

TABSCHEMA     TABNAME      COLNAME     IDENTITY  GENERATED
------------- ------------ ----------- --------- ---------
ZHANGRP       TEST_IDL1    NO          Y         D
ZHANGRP       TEST_IDL1    NAME        N         
ZHANGRP       TEST_IDL2    NO          Y         A
ZHANGRP       TEST_IDL2    NAME        N

  4 条记录已选择。
SQL>

从这个信息,可以看的出来,表TEST_IDL1,TEST_IDL2上分别定义了一个缺省情况下生成和必须生成的标识列.关于表的信息,我们可以从如下的系统目录中获取:
SELECT * FROM SYSIBM.SYSTABLES where name in ('TEST_IDL1','TEST_IDL2');
 
继续往下走,通过下面的系统目录视图,我们可以进一步看到这两张表的两个标识列对应的序列ID

SQL> SELECT tabschema,tabname, colname,seqid
SQL> FROM SYSCAT.COLIDENTATTRIBUTES
SQL> where tabschema='ZHANGRP' and tabname in ('TEST_IDL1','TEST_IDL2');

TABSCHEMA   TABNAME    COLNAME    SEQID
----------- ---------- ---------- ------
ZHANGRP     TEST_IDL1  NO         5
ZHANGRP     TEST_IDL2  NO         6

  2 条记录已选择。

SQL>

从上面的查询可以知道,这两张表上的字段名均为NO的标识列对应的SEQID分别为5和6,有了这个信息,我们就可以知道到底系统为我们定义了哪两个序列来为标识列服务的了.

SQL> SELECT seqschema, seqname,owner,seqid,seqtype FROM SYSCAT.SEQUENCES where seqid in (5,6);

SEQSCHEMA   SEQNAME             OWNER        SEQID  SEQTYPE
----------- ------------------- ----------- ------  --------
ZHANGRP     SQL090227162615120  ZHANGRP          5  I
ZHANGRP     SQL090227162615870  ZHANGRP          6  I

  2 条记录已选择。

SQL>

将上面的两个语句合并一下:

SQL> SELECT s.seqschema,s.owner,c.tabname,c.colname,s.seqname,s.seqid,s.seqtype
SQL> FROM SYSCAT.SEQUENCES s, SYSCAT.COLIDENTATTRIBUTES c
SQL> where c.tabschema='ZHANGRP' and
SQL>       c.tabname in ('TEST_IDL1','TEST_IDL2') and
SQL>       c.tabschema=s.seqschema and
SQL>       c.seqid=s.seqid;

SEQSCHEMA  OWNER      TABNAME     COLNAME   SEQNAME            SEQID SEQTYPE
---------- ---------- ----------- --------- ------------------ ----- -------
ZHANGRP    ZHANGRP    TEST_IDL1   NO        SQL090227162615120 5     I
ZHANGRP    ZHANGRP    TEST_IDL2   NO        SQL090227162615870 6     I
  2 条记录已选择。

SQL>

还有一个有用的目录视图,可以直接看到标识列与表的依赖关系:

SQL> SELECT bname,bschema,btype,dname,dschema FROM SYSIBM.SYSDEPENDENCIES where dname in ('TEST_IDL1','TEST_IDL2');

BNAME               BSCHEMA    BTYPE     DNAME      DSCHEMA
------------------- ---------- --------- ---------- -------
SQL090227162615120  ZHANGRP    Q         TEST_IDL1  ZHANGRP
SQL090227162615870  ZHANGRP    Q         TEST_IDL2  ZHANGRP
  2 条记录已选择。

SQL>



只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理