2004 年 9 月 01 日如何将代理键(surrogate key)用作 DB2 UDB 中的主键?代理键是生成惟一序列号的一种有效方法。从本文中可以了解三种实现:传统方法,使用键管理器,以及使用新的 DB2 UDB 特性。简介使用代理键解决方案是为了发现一种生成惟一序列号的有效方法。本文描述了三种实现: 使用传统方法。 使用键管理器。 使用DB2 UDB 特性。 代理键也叫 内键(internal key)。当创建一个表时,可以添加一个额外的列作为代理键。这个列应该是 NOT NULL,并且没有商业意义。可以将该代理列指定为主键列。例如可以有一个数字代理列。代理键的值从某一个数字开始,例如 "1",以这个数字作为该列在表中第一行的值,之后的每一行中该列的值都按 1 递增。 例如,如果我们有表 EMPLOYEE:CREATE TABLE EMPLOYEE ( FIRSTNAME CHAR(64), LASTNAME CHAR(64), SALARY DECIMAL(10, 2)) 那么可以添加一个代理键列 SERIALNUMBER,并将其指定为主键列。这样,这个表的定义就变为:CREATE TABLE EMPLOYEE ( SERIALNUMBER BIGINT NOT NULL, FIRSTNAME CHAR(64), LASTNAME CHAR(64), SALARY DECIMAL(10, 2), PRIMARY KEY (SERIALNUMBER)) 那么,怎样将惟一的值赋给每一行的 SERIALNUMBER 列呢?首先需要为代理键生成惟一的值。下面我将讨论三种可行的解决方案。回页首使用传统方法解决方案的思想传统方法是使用简单的 SQL 或触发器生成惟一的值。示例以表EMPLOYEE 为例。您可以在 INSERT 语句中实现代理键生成函数:INSERT INTO EMPLOYEE (SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES ((SELECT MAX(SERIALNUMBER) FROM EMPLOYEE)+1, ‘John’, ‘Smith’, 99999) SQL 语句 " (SELECT MAX(SERIALNUMBER) FROM EMPLOYEE)+1 " 将找出最大的 SERIALNUMBER 并将其加 1,这样新行就有一个惟一的 SERIALNUMBER。 这样做存在的一个问题是,当将第一行插入表中时,可能会得到如下错误: SQL0407N Assignment of a NULL value to a NOT NULL SQLSTATE=23502 得到上述错误的原因是,当表为空时," SELECT MAX(SERIALNUMBER) FROM EMPLOYEE " 的返回为 NULL。因此,我们必须使用 COALESCE() 来处理这个问题: INSERT INTO EMPLOYEE (SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES (COALESCE((SELECT MAX(SERIALNUMBER) FROM EMPLOYEE), 0)+1, ‘John’, ‘Smith’, 99999) 另一种传统方法是使用触发器来生成代理键: CREATE TRIGGER AUTOSURROGATEKEY NO CASCADE BEFORE INSERT ON EMPLOYEE REFERENCING NEW AS N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC SET NSERIALNUMBER = COALESCE((SELECT MAX(SERIALNUMBER) FROM EMPLOYEE), 0)+1; END 优点及问题传统方法易于理解,而且容易在所有系统上实现。但是,这种实现实际上会导致事务处理系统中出现并发问题。因为该实现只允许一次执行一条 INSERT *** 作。 因此,在获得最大的 SERIALNUMBER 之前," SELECT MAX(SERIALNUMBER) FROM EMPLOYEE " 必须等待其他所有事务完成对表 EMPLOYEE 的 INSERT 或 UPDATE *** 作。例如,如果有两个事务正在对 EMPLOYEE 表进行 INSERT *** 作,那么其中有一个事务会被另一个事务阻塞。显然,这种“逐次插入”的解决方案不适合多用户的事务处理系统。 回页首使用键管理器解决方案的思想很多大型的应用程序使用键管理器方法维护所有表的代理键。键管理器可以是一个助手类。每当需要向表插入一个行时,便可以调用键管理器生成新的键值,然后将获得的键值插入新行。示例首先,需要创建表 KEYS 来记录每个表的当前代理键值。键管理器类将使用该表生成新键值。CREATE TABLE KEYS ( TABLENAME CHAR(256), COLNUMNAME CHAR(256), SURROGATEKEYVALUE BIGINT, INCREMENT BIGINT, PRIMARY KEY(TABLENAME, COLNUMNAME)); 第二,将新表(例如表 EMPLOYEE)注册到表 KEYS 中。INSERT INTO KEYS (TABLENAME, COLUMNNAME, SURROGATEKEYVALUE, INCREMENT) VALUES (‘EMPLOYEE’, ‘SERIALNUMBER’, 0, 1); 第三,编写 KeyManger 类来维护每个已注册表的代理键。KeyManager 将提供两个方法:/ Intialize the KeyManger / KeyManagersingleton(); / Return the unique surrogate key value according to the input table name and column name / KeyManager GetSurrogateKey(String tableName, String columnName); 要查看更详细的 KeyManger 的代码,请参考附录。 第四,调用 KeyManger 来获得主键值: … KeyManager km = KeyManagersingleton(); Long surrogateKey = kmgetSurrogateKey("EMPLOYEE", "SERIALNUMBER"); … 优点和问题显然,键管理器是模块化设计的一个很好的例子。键管理器封装了代理键生成函数。这种实现也易于定制。您可以在 KEYS 表中为 SURROGATEKEYVALUE 或 INCREMENT 指定不同的值,以得到不同的代理键。而且,这种实现可以在大多数数据库系统上实施。但是,为了进行维护,需要一个单独的表和编写代码。所以,这种方法更适合于大型的跨数据库系统。 回页首使用DB2 UDB 特性DB2 UDB 提供了三种方法来生成惟一值。您可以使用这些方法来实现代理键。DB2 UDB Version 61 中的 GENERATE_UNIQUE() SQL 函数。 DB2 UDB Version 72 中 CREATE TABLE 语句的 IDENTITY 选项。 DB2 UDB Version 72 中的 SEQUENCE 对象。 GENERATE_UNIQUE()解决方案的思想GENERATE_UNIQUE() 最初是在 DB2 UDB Version 61 中提供的一个 SQL 函数。该函数返回当前系统时间戳。我们可以使用该函数为代理键列生成惟一值。示例CREATE TABLE EMPLOYEE ( SERIALNUMBER CHAR(13) FOR BIT DATA NOT NULL, FIRSTNAME CHAR(64), LASTNAME CHAR(64), SALARY DECIMAL(10, 2), PRIMARY KEY (SERIALNUMBER)) 然后可以用下面的 SQL 语句插入一行:INSERT INTO EMPLOYEE (SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES(GENERATE_UNIQUE(), ‘John’, ‘Smith’, 99999) 优点和问题这里需要清楚两件事情。首先,当多个事务在同一时刻插入行时,GENERATE_UNIQUE() 可能会返回相同的时间戳。在这种情况下,GENERATE_UNIQUE() 不能为每个事务生成一个惟一的返回值,因而这种方法不适合有大量事务的系统。第二,一旦系统时钟需要向后调整,那么 GENERATE_UNIQUE() 将可能返回重复的值。 由于上述限制,我决不会在生产系统中使用 GENERATE_UNIQUE()。但是,当您需要在有限的时间内完成一个原型时,这也许是一种选择。 CREATE TABLE 语句中的 IDENTITY 选项解决方案的思想IDENTITY 是 DB2 UDB Version 71 和后期版本提供的 CREATE TABLE 语句中的一个选项。在创建表时,可以将某个列指定为 IDENTITY 列。对于每条 INSERT 语句,DB2 将负责为其中的这一列生成一个惟一的值。 示例CREATE TABLE EMPLOYEE ( SERIALNUMBER BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), FIRSTNAME CHAR(64), LASTNAME CHAR(64), SALARY DECIMAL(10, 2), PRIMARY KEY (SERIALNUMBER)) 然后可以用下面的语句插入一行: INSERT INTO EMPLOYEE (FIRSTNAME, LASTNAME, SALARY) VALUES ( ‘John’, ‘Smith’, 99999) INSERT 语句不需要指定 SERIALNUMBER 列的值。DB2 UDB 将根据列的定义自动生成惟一值,即 "GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1)"。优点和问题IDENTITY 函数在大多数情况下是代理键函数的一个好的解决方案。DB2 import 和 export 实用程序也支持 IDENTITY 选项。然而,在某种情况下,这种解决方案不大方便。在运行 INSERT 语句之后,应用程序将永远都不知道放入了主键列中的是什么值。如果应用程序必须继续向子表插入一个行,那么它就不得不对父表运行一条 SELECT 语句,以得到主键值。不过,如果这一点对于您的系统不成问题的话,那么使用 IDENTITY 选项是一个好主意。 SEQUENCE 对象解决方案的思想SEQUENCE 对象是在 DB2 UDB Version 72 中引入的一个特性。用户可以在数据库中创建一个 SEQUENCE 对象,就像创建表对象或视图对象一样,然后从 SEQUENCE 中请求值。DB2 保证用户每次可以得到一个惟一的序列值。 示例您可以在数据库中创建一个 SEQUENCE 对象:CREATE SEQUENCE EMPSERIAL AS BIGINT START WITH 1 INCREMENT BY 1 如果有一个如下所示的 EMPLOYEE 表: CREATE TABLE EMPLOYEE ( SERIALNUMBER BIGINT NOT NULL, FIRSTNAME CHAR(64), LASTNAME CHAR(64), SALARY DECIMAL(10, 2), PRIMARY KEY (SERIALNUMBER)) 那么可以用下面的语句插入一个行: INSERT INTO EMPLOYEE ( SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES(NEXTVAL FOR EMPSERIAL, 'John', 'Smith', 9999) 在这里使用 " NEXTVAL FOR EMPSERIAL " 从 SEQUENCE 中获得惟一值。 您可以使用 " PREVVAL FOR EMPSERIAL " 获得当前连接会话中最近生成的序列值。应用程序就可以知道放入主键列中的是什么值,从而继续向子表插入一个行。这里,“在当前连接会话中”这一点很重要,这意味着 "PREVVAL" 将只返回在相同连接会话中生成的值。 例如,考虑这样的情况:有两个应用程序连接到数据库,并按照如下顺序运行下面的 SQL 语句。 (假设 SEQUENCE " EMPSERIAL " 的当前值是 3)。 应用程序 1:
INSERT INTO EMPLOYEE ( SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES(NEXTVAL FOR EMPSERIAL, 'Martin', 'Wong', 100000)从EMPSERIAL 生成的 " NEXTVAL " 是 4。 应用程序 2:
INSERT INTO EMPLOYEE ( SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES(NEXTVAL FOR EMPSERIAL, 'Patrick', 'Chan', 9999) 从EMPSERIAL 生成的 " NEXTVAL " 是 5。 应用程序 1:
SELECT PREVVAL FOR EMPSERIAL FROM EMPLOYEE " PREVVAL " 将返回 4,而不是 5。 而且, PREVVAL 和NEXTVAL 的值不会受事务回滚的影响。 例如,假设 SEQUENCE " EMPSERIAL " 的当前值是 30。某个应用程序开始了一个事务: INSERT INTO EMPLOYEE ( SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES(NEXTVAL FOR EMPSERIAL, 'William', 'Chen', 9999) 执行ROLLBACK *** 作。 然后,如果运行: SELECT PREVVAL FOR EMPSERIAL FROM EMPLOYEE 则" PREVVAL " 将返回 31,而不是 30。 优点和问题SEQUENCE 是最近 DB2 UDB 为生成惟一值而实现的函数。它还有一个缓存函数,用于提高性能(要了解详细信息,请参阅 IBM DB2 UDB SQL Reference)。该函数比 IDENTITY 函数更灵活,因为它是数据库中的一个独立对象。必要时候,可以通过运行 ALTER SEQUENCE 语句更改其设置。 如果系统只在 DB2 UDB 上运行,那么 SEQUENCE 也许是最好的解决方案,因为它易于使用,而且不像键管理器那样需要额外的代码,并且可以随需求的变化很轻易对其进行更改。回页首结束语本文描述了实现作为主键的代理键的三种方法。文中主要讨论了如何为代理键生成惟一的序列值。传统方法适合于简单的、单用户(非并发)系统。对于实现对于大型系统和跨平台系统,键管理器是一个好选择。
代理键是生成惟一序列号的一种有效方法。从本文中可以了解三种实现:传统方法,使用键管理器,以及使用新的 DB2 UDB 特性。简介使用代理键解决方案是为了发现一种生成惟一序列号的有效方法。本文描述了三种实现: 使用传统方法。 使用键管理器。 使用DB2 UDB 特性。 代理键也叫 内键(internal key)。当创建一个表时,可以添加一个额外的列作为代理键。这个列应该是 NOT NULL,并且没有商业意义。可以将该代理列指定为主键列。例如可以有一个数字代理列。代理键的值从某一个数字开始,例如 "1",以这个数字作为该列在表中第一行的值,之后的每一行中该列的值都按 1 递增。 例如,如果我们有表 EMPLOYEE:CREATE TABLE EMPLOYEE ( FIRSTNAME CHAR(64), LASTNAME CHAR(64), SALARY DECIMAL(10, 2)) 那么可以添加一个代理键列 SERIALNUMBER,并将其指定为主键列。这样,这个表的定义就变为:CREATE TABLE EMPLOYEE ( SERIALNUMBER BIGINT NOT NULL, FIRSTNAME CHAR(64), LASTNAME CHAR(64), SALARY DECIMAL(10, 2), PRIMARY KEY (SERIALNUMBER)) 那么,怎样将惟一的值赋给每一行的 SERIALNUMBER 列呢?首先需要为代理键生成惟一的值。下面我将讨论三种可行的解决方案。回页首使用传统方法解决方案的思想传统方法是使用简单的 SQL 或触发器生成惟一的值。示例以表EMPLOYEE 为例。您可以在 INSERT 语句中实现代理键生成函数:INSERT INTO EMPLOYEE (SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES ((SELECT MAX(SERIALNUMBER) FROM EMPLOYEE)+1, ‘John’, ‘Smith’, 99999) SQL 语句 " (SELECT MAX(SERIALNUMBER) FROM EMPLOYEE)+1 " 将找出最大的 SERIALNUMBER 并将其加 1,这样新行就有一个惟一的 SERIALNUMBER。 这样做存在的一个问题是,当将第一行插入表中时,可能会得到如下错误: SQL0407N Assignment of a NULL value to a NOT NULL SQLSTATE=23502 得到上述错误的原因是,当表为空时," SELECT MAX(SERIALNUMBER) FROM EMPLOYEE " 的返回为 NULL。因此,我们必须使用 COALESCE() 来处理这个问题: INSERT INTO EMPLOYEE (SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES (COALESCE((SELECT MAX(SERIALNUMBER) FROM EMPLOYEE), 0)+1, ‘John’, ‘Smith’, 99999) 另一种传统方法是使用触发器来生成代理键: CREATE TRIGGER AUTOSURROGATEKEY NO CASCADE BEFORE INSERT ON EMPLOYEE REFERENCING NEW AS N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC SET NSERIALNUMBER = COALESCE((SELECT MAX(SERIALNUMBER) FROM EMPLOYEE), 0)+1; END 优点及问题传统方法易于理解,而且容易在所有系统上实现。但是,这种实现实际上会导致事务处理系统中出现并发问题。因为该实现只允许一次执行一条 INSERT *** 作。 因此,在获得最大的 SERIALNUMBER 之前," SELECT MAX(SERIALNUMBER) FROM EMPLOYEE " 必须等待其他所有事务完成对表 EMPLOYEE 的 INSERT 或 UPDATE *** 作。例如,如果有两个事务正在对 EMPLOYEE 表进行 INSERT *** 作,那么其中有一个事务会被另一个事务阻塞。显然,这种“逐次插入”的解决方案不适合多用户的事务处理系统。 回页首使用键管理器解决方案的思想很多大型的应用程序使用键管理器方法维护所有表的代理键。键管理器可以是一个助手类。每当需要向表插入一个行时,便可以调用键管理器生成新的键值,然后将获得的键值插入新行。示例首先,需要创建表 KEYS 来记录每个表的当前代理键值。键管理器类将使用该表生成新键值。CREATE TABLE KEYS ( TABLENAME CHAR(256), COLNUMNAME CHAR(256), SURROGATEKEYVALUE BIGINT, INCREMENT BIGINT, PRIMARY KEY(TABLENAME, COLNUMNAME)); 第二,将新表(例如表 EMPLOYEE)注册到表 KEYS 中。INSERT INTO KEYS (TABLENAME, COLUMNNAME, SURROGATEKEYVALUE, INCREMENT) VALUES (‘EMPLOYEE’, ‘SERIALNUMBER’, 0, 1); 第三,编写 KeyManger 类来维护每个已注册表的代理键。KeyManager 将提供两个方法:/ Intialize the KeyManger / KeyManagersingleton(); / Return the unique surrogate key value according to the input table name and column name / KeyManager GetSurrogateKey(String tableName, String columnName); 要查看更详细的 KeyManger 的代码,请参考附录。 第四,调用 KeyManger 来获得主键值: … KeyManager km = KeyManagersingleton(); Long surrogateKey = kmgetSurrogateKey("EMPLOYEE", "SERIALNUMBER"); … 优点和问题显然,键管理器是模块化设计的一个很好的例子。键管理器封装了代理键生成函数。这种实现也易于定制。您可以在 KEYS 表中为 SURROGATEKEYVALUE 或 INCREMENT 指定不同的值,以得到不同的代理键。而且,这种实现可以在大多数数据库系统上实施。但是,为了进行维护,需要一个单独的表和编写代码。所以,这种方法更适合于大型的跨数据库系统。 回页首使用DB2 UDB 特性DB2 UDB 提供了三种方法来生成惟一值。您可以使用这些方法来实现代理键。DB2 UDB Version 61 中的 GENERATE_UNIQUE() SQL 函数。 DB2 UDB Version 72 中 CREATE TABLE 语句的 IDENTITY 选项。 DB2 UDB Version 72 中的 SEQUENCE 对象。 GENERATE_UNIQUE()解决方案的思想GENERATE_UNIQUE() 最初是在 DB2 UDB Version 61 中提供的一个 SQL 函数。该函数返回当前系统时间戳。我们可以使用该函数为代理键列生成惟一值。示例CREATE TABLE EMPLOYEE ( SERIALNUMBER CHAR(13) FOR BIT DATA NOT NULL, FIRSTNAME CHAR(64), LASTNAME CHAR(64), SALARY DECIMAL(10, 2), PRIMARY KEY (SERIALNUMBER)) 然后可以用下面的 SQL 语句插入一行:INSERT INTO EMPLOYEE (SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES(GENERATE_UNIQUE(), ‘John’, ‘Smith’, 99999) 优点和问题这里需要清楚两件事情。首先,当多个事务在同一时刻插入行时,GENERATE_UNIQUE() 可能会返回相同的时间戳。在这种情况下,GENERATE_UNIQUE() 不能为每个事务生成一个惟一的返回值,因而这种方法不适合有大量事务的系统。第二,一旦系统时钟需要向后调整,那么 GENERATE_UNIQUE() 将可能返回重复的值。 由于上述限制,我决不会在生产系统中使用 GENERATE_UNIQUE()。但是,当您需要在有限的时间内完成一个原型时,这也许是一种选择。 CREATE TABLE 语句中的 IDENTITY 选项解决方案的思想IDENTITY 是 DB2 UDB Version 71 和后期版本提供的 CREATE TABLE 语句中的一个选项。在创建表时,可以将某个列指定为 IDENTITY 列。对于每条 INSERT 语句,DB2 将负责为其中的这一列生成一个惟一的值。 示例CREATE TABLE EMPLOYEE ( SERIALNUMBER BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), FIRSTNAME CHAR(64), LASTNAME CHAR(64), SALARY DECIMAL(10, 2), PRIMARY KEY (SERIALNUMBER)) 然后可以用下面的语句插入一行: INSERT INTO EMPLOYEE (FIRSTNAME, LASTNAME, SALARY) VALUES ( ‘John’, ‘Smith’, 99999) INSERT 语句不需要指定 SERIALNUMBER 列的值。DB2 UDB 将根据列的定义自动生成惟一值,即 "GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1)"。优点和问题IDENTITY 函数在大多数情况下是代理键函数的一个好的解决方案。DB2 import 和 export 实用程序也支持 IDENTITY 选项。然而,在某种情况下,这种解决方案不大方便。在运行 INSERT 语句之后,应用程序将永远都不知道放入了主键列中的是什么值。如果应用程序必须继续向子表插入一个行,那么它就不得不对父表运行一条 SELECT 语句,以得到主键值。不过,如果这一点对于您的系统不成问题的话,那么使用 IDENTITY 选项是一个好主意。 SEQUENCE 对象解决方案的思想SEQUENCE 对象是在 DB2 UDB Version 72 中引入的一个特性。用户可以在数据库中创建一个 SEQUENCE 对象,就像创建表对象或视图对象一样,然后从 SEQUENCE 中请求值。DB2 保证用户每次可以得到一个惟一的序列值。 示例您可以在数据库中创建一个 SEQUENCE 对象:CREATE SEQUENCE EMPSERIAL AS BIGINT START WITH 1 INCREMENT BY 1 如果有一个如下所示的 EMPLOYEE 表: CREATE TABLE EMPLOYEE ( SERIALNUMBER BIGINT NOT NULL, FIRSTNAME CHAR(64), LASTNAME CHAR(64), SALARY DECIMAL(10, 2), PRIMARY KEY (SERIALNUMBER)) 那么可以用下面的语句插入一个行: INSERT INTO EMPLOYEE ( SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES(NEXTVAL FOR EMPSERIAL, 'John', 'Smith', 9999) 在这里使用 " NEXTVAL FOR EMPSERIAL " 从 SEQUENCE 中获得惟一值。 您可以使用 " PREVVAL FOR EMPSERIAL " 获得当前连接会话中最近生成的序列值。应用程序就可以知道放入主键列中的是什么值,从而继续向子表插入一个行。这里,“在当前连接会话中”这一点很重要,这意味着 "PREVVAL" 将只返回在相同连接会话中生成的值。 例如,考虑这样的情况:有两个应用程序连接到数据库,并按照如下顺序运行下面的 SQL 语句。 (假设 SEQUENCE " EMPSERIAL " 的当前值是 3)。 应用程序 1:
INSERT INTO EMPLOYEE ( SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES(NEXTVAL FOR EMPSERIAL, 'Martin', 'Wong', 100000)�0�2从EMPSERIAL 生成的 " NEXTVAL " 是 4。 应用程序 2:
INSERT INTO EMPLOYEE ( SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES(NEXTVAL FOR EMPSERIAL, 'Patrick', 'Chan', 9999) �0�2从EMPSERIAL 生成的 " NEXTVAL " 是 5。 应用程序 1:
SELECT PREVVAL FOR EMPSERIAL FROM EMPLOYEE �0�2" PREVVAL " 将返回 4,而不是 5。 而且, PREVVAL 和NEXTVAL 的值不会受事务回滚的影响。 例如,假设 SEQUENCE " EMPSERIAL " 的当前值是 30。某个应用程序开始了一个事务: INSERT INTO EMPLOYEE ( SERIALNUMBER, FIRSTNAME, LASTNAME, SALARY) VALUES(NEXTVAL FOR EMPSERIAL, 'William', 'Chen', 9999) 执行ROLLBACK *** 作。 然后,如果运行: SELECT PREVVAL FOR EMPSERIAL FROM EMPLOYEE 则" PREVVAL " 将返回 31,而不是 30。 优点和问题SEQUENCE 是最近 DB2 UDB 为生成惟一值而实现的函数。它还有一个缓存函数,用于提高性能(要了解详细信息,请参阅 IBM DB2 UDB SQL Reference)。该函数比 IDENTITY 函数更灵活,因为它是数据库中的一个独立对象。必要时候,可以通过运行 ALTER SEQUENCE 语句更改其设置。 如果系统只在 DB2 UDB 上运行,那么 SEQUENCE 也许是最好的解决方案,因为它易于使用,而且不像键管理器那样需要额外的代码,并且可以随需求的变化很轻易对其进行更改。回页首结束语本文描述了实现作为主键的代理键的三种方法。文中主要讨论了如何为代理键生成惟一的序列值。传统方法适合于简单的、单用户(非并发)系统。对于实现对于大型系统和跨平台系统,键管理器是一个好选择。但是,如果项目只在 DB2 UDB 上运行的话,可以考虑 DB2 UDB 提供的特性。IDENTITY 和 SEQUENCE 函数提供了一种容易的、灵活的解决方案。在创建 IDENTITY 列和 SEQUENCE 对象时,可以使用很多选择。请参阅 IBM DB2 UDB Administration Guide和IBM DB2 UDB SQL Reference,以获得完整的细节。 回页首附录import javasqlConnection; import javasqlDriverManager; import javasqlPreparedStatement; import javasqlResultSet; import javasqlSQLException; public class KeyManager { private static KeyManager singleton; private PreparedStatement getKeyStmt = null; private PreparedStatement updateKeyStmt = null; static final String db2Driver = "COMibmdb2jdbcappDB2Driver"; static final String db2UrlPfx = "jdbc:db2:"; public KeyManager Singleton() throws ClassNotFoundException, SQLException{ if (singleton == null) { if (singleton == null) singleton = new KeyManager(); } return singleton; } private KeyManager() throws ClassNotFoundException, SQLException{ ClassforName(db2Driver); Connection connection = DriverManagergetConnection(db2UrlPfx+ "dbName", "userName", "password"); getKeyStmt = connectionprepareStatement("SELECT SURROGATEKEYVALUE FROM KEYS WHERE TABLENAME = AND COLUMNNAME = "); updateKeyStmt = connectionprepareStatement("UPDATE KEYS SET SURROGATEKEYVALUE = SURROGATEKEYVALUE + INCREMENT WHERE TABLENAME = AND COLUMNNAME = "); } public Long getSurrogateKey(String tableName, String columnName) throws SQLException{ Long keyValue = null; getKeyStmtsetString(1, tableName); getKeyStmtsetString(2, columnName); updateKeyStmtsetString(1, tableName); updateKeyStmtsetString(2, columnName); updateKeyStmtexecute(); ResultSet rs = getKeyStmtexecuteQuery(); if (rsnext() == true) { keyValue = new Long(rsgetLong(1)); } return keyValue; } } 参考资料 您可以参阅本文在 developerWorks 全球站点上的 英文原文
IBM DB2 UDB Administration GuideIBM DB2 UDB SQL Reference关于作者�0�2Jason Zhang 是 IBM 加拿大多伦多的实验室的一名软件开发人员,从事电子商务开发已经有 5 年多的时间。
随着信息技术爆炸性发展,智能终端,智能穿戴设备,无人驾驶汽车等等,都宣告大数据的时代已经来临!学习成长为一名处理数据的BI工程师是实现梦想的绝佳途径!
如何入门呢?首先要有数据库基础,选择一门主流的数据库(ORACLE,DB2,SQLSERVER)学习基本的SQL开发,掌握存储过程,函数,包,SQL调优等。其次就要会一个主流的BI工具,常见后台ETL工具如:DATASTAGE,INFORMATICA,KETTLE等,主流前台报表工具如BIEE,COGNOS,BO,当然也有国产的像润乾等。之后就是要了解BI中常用的概念如:事实表,维表,切片,上钻下取,缓慢变化维,代理键等。
要熟练掌握这些工具,就要在项目中去大量的练习啦,只有通过项目的实践才能深刻体会BI的这些概念及其精髓。之后有了几年的经验就可以向某个行业去深入学习业务知识做ETL顾问,或者向数据挖掘方向深究!这个过程自学的话要很久,要想在很短的时间入门,找家有实力的机构去通过项目实践就很有必要啦。国内专注这块的有深圳的融通学苑,北京的思数,你可根据自身情况作出选择。
一、数据量过大,数据中什么情况都可能存在。
如果说有10条数据,那么大不了每条去逐一检查,人为处理,如果有上百条数据,也可以考虑,如果数据上到千万级别,甚至 过亿,那不是手工能解决的了,必须通过工具或者程序进行处理,尤其海量的数据中,什么情况都可能存在,例如,数据中某处格式出了问题,尤其在程序处理时, 前面还能正常处理,突然到了某个地方问题出现了,程序终止了。
二、软硬件要求高,系统资源占用率高。
对海量的数据进行处理,除了好的方法,最重要的就是合理使用工具,合理分配系统资源。一般情况,如果处理的数据过TB级,小型机是要考虑的,普通的机子如果有好的方法可以考虑,不过也必须加大CPU和内存,就象面对着千军万马,光有勇气没有一兵一卒是很难取胜的。
三、要求很高的处理方法和技巧。
这也是本文的写作目的所在,好的处理方法是一位工程师长期工作经验的积累,也是个人的经验的总结。没有通用的处理方法,但有通用的原理和规则。
下面我们来详细介绍一下处理海量数据的经验和技巧:
一、选用优秀的数据库工具
现在的数据库工具厂家比较多,对海量数据的处理对所使用的数据库工具要求比较高,一般使用Oracle或者DB2,微软 公司最近发布的SQL Server 2005性能也不错。另外在BI领域:数据库,数据仓库,多维数据库,数据挖掘等相关工具也要进行选择,象好的ETL工具和好的OLAP工具都十分必要, 例如Informatic,Eassbase等。笔者在实际数据分析项目中,对每天6000万条的日志数据进行处理,使用SQL Server 2000需要花费6小时,而使用SQL Server 2005则只需要花费3小时。
二、编写优良的程序代码
处理数据离不开优秀的程序代码,尤其在进行复杂数据处理时,必须使用程序。好的程序代码对数据的处理至关重要,这不仅仅是数据处理准确度的问题,更是数据处理效率的问题。良好的程序代码应该包含好的算法,包含好的处理流程,包含好的效率,包含好的异常处理机制等。
三、对海量数据进行分区 *** 作
对海量数据进行分区 *** 作十分必要,例如针对按年份存取的数据,我们可以按年进行分区,不同的数据库有不同的分区方式,不 过处理机制大体相同。例如SQL Server的数据库分区是将不同的数据存于不同的文件组下,而不同的文件组存于不同的磁盘分区下,这样将数据分散开,减小磁盘I/O,减小了系统负荷, 而且还可以将日志,索引等放于不同的分区下。
四、建立广泛的索引
对海量的数据处理,对大表建立索引是必行的,建立索引要考虑到具体情况,例如针对大表的分组、排序等字段,都要建立相应 索引,一般还可以建立复合索引,对经常插入的表则建立索引时要小心,笔者在处理数据时,曾经在一个ETL流程中,当插入表时,首先删除索引,然后插入完 毕,建立索引,并实施聚合 *** 作,聚合完成后,再次插入前还是删除索引,所以索引要用到好的时机,索引的填充因子和聚集、非聚集索引都要考虑。
五、建立缓存机制
当数据量增加时,一般的处理工具都要考虑到缓存问题。缓存大小设置的好差也关系到数据处理的成败,例如,笔者在处理2亿条数据聚合 *** 作时,缓存设置为100000条/Buffer,这对于这个级别的数据量是可行的。
六、加大虚拟内存
如果系统资源有限,内存提示不足,则可以靠增加虚拟内存来解决。笔者在实际项目中曾经遇到针对18亿条的数据进行处理, 内存为1GB,1个P424G的CPU,对这么大的数据量进行聚合 *** 作是有问题的,提示内存不足,那么采用了加大虚拟内存的方法来解决,在6块磁盘分区 上分别建立了6个4096M的磁盘分区,用于虚拟内存,这样虚拟的内存则增加为 40966 + 1024 =25600 M,解决了数据处理中的内存不足问题。
七、分批处理
海量数据处理难因为数据量大,那么解决海量数据处理难的问题其中一个技巧是减少数据量。可以对海量数据分批处理,然后处 理后的数据再进行合并 *** 作,这样逐个击破,有利于小数据量的处理,不至于面对大数据量带来的问题,不过这种方法也要因时因势进行,如果不允许拆分数据,还 需要另想办法。不过一般的数据按天、按月、按年等存储的,都可以采用先分后合的方法,对数据进行分开处理。
八、使用临时表和中间表
数据量增加时,处理中要考虑提前汇总。这样做的目的是化整为零,大表变小表,分块处理完成后,再利用一定的规则进行合 并,处理过程中的临时表的使用和中间结果的保存都非常重要,如果对于超海量的数据,大表处理不了,只能拆分为多个小表。如果处理过程中需要多步汇总 *** 作, 可按汇总步骤一步步来,不要一条语句完成,一口气吃掉一个胖子。
九、优化查询SQL语句
在对海量数据进行查询处理过程中,查询的SQL语句的性能对查询效率的影响是非常大的,编写高效优良的SQL脚本和存储 过程是数据库工作人员的职责,也是检验数据库工作人员水平的一个标准,在对SQL语句的编写过程中,例如减少关联,少用或不用游标,设计好高效的数据库表 结构等都十分必要。笔者在工作中试着对1亿行的数据使用游标,运行3个小时没有出结果,这是一定要改用程序处理了。
十、使用文本格式进行处理
对一般的数据处理可以使用数据库,如果对复杂的数据处理,必须借助程序,那么在程序 *** 作数据库和程序 *** 作文本之间选择, 是一定要选择程序 *** 作文本的,原因为:程序 *** 作文本速度快;对文本进行处理不容易出错;文本的存储不受限制等。例如一般的海量的网络日志都是文本格式或者 csv格式(文本格式),对它进行处理牵扯到数据清洗,是要利用程序进行处理的,而不建议导入数据库再做清洗。
十一、定制强大的清洗规则和出错处理机制
海量数据中存在着不一致性,极有可能出现某处的瑕疵。例如,同样的数据中的时间字段,有的可能为非标准的时间,出现的原因可能为应用程序的错误,系统的错误等,这是在进行数据处理时,必须制定强大的数据清洗规则和出错处理机制。
十二、建立视图或者物化视图
视图中的数据来源于基表,对海量数据的处理,可以将数据按一定的规则分散到各个基表中,查询或处理过程中可以基于视图进行,这样分散了磁盘I/O,正如10根绳子吊着一根柱子和一根吊着一根柱子的区别。
十三、避免使用32位机子(极端情况)
目前的计算机很多都是32位的,那么编写的程序对内存的需要便受限制,而很多的海量数据处理是必须大量消耗内存的,这便要求更好性能的机子,其中对位数的限制也十分重要。
十四、考虑 *** 作系统问题
海量数据处理过程中,除了对数据库,处理程序等要求比较高以外,对 *** 作系统的要求也放到了重要的位置,一般是必须使用服务器的,而且对系统的安全性和稳定性等要求也比较高。尤其对 *** 作系统自身的缓存机制,临时空间的处理等问题都需要综合考虑。
十五、使用数据仓库和多维数据库存储
数据量加大是一定要考虑OLAP的,传统的报表可能5、6个小时出来结果,而基于Cube的查询可能只需要几分钟,因此处理海量数据的利器是OLAP多维分析,即建立数据仓库,建立多维数据集,基于多维数据集进行报表展现和数据挖掘等。
十六、使用采样数据,进行数据挖掘
基于海量数据的数据挖掘正在逐步兴起,面对着超海量的数据,一般的挖掘软件或算法往往采用数据抽样的方式进行处理,这样 的误差不会很高,大大提高了处理效率和处理的成功率。一般采样时要注意数据的完整性和,防止过大的偏差。笔者曾经对1亿2千万行的表数据进行采样,抽取出 400万行,经测试软件测试处理的误差为千分之五,客户可以接受。
还有一些方法,需要在不同的情况和场合下运用,例如使用代理键等 *** 作,这样的好处是加快了聚合时间,因为对数值型的聚合比对字符型的聚合快得多。类似的情况需要针对不同的需求进行处理。
海量数据是发展趋势,对数据分析和挖掘也越来越重要,从海量数据中提取有用信息重要而紧迫,这便要求处理要准确,精度要高,而且处理时间要短,得到有价值信息要快,所以,对海量数据的研究很有前途,也很值得进行广泛深入的研究。
海量数据处理专题(一)——开篇
大数据量的问题是很多面试笔试中经常出现的问题,比如baidu google 腾讯 这样的一些涉及到海量数据的公司经常会问到。
下面的方法是我对海量数据的处理方法进行了一个一般性的总结,当然这些方法可能并不能完全覆盖所有的问题,但是这样 的一些方法也基本可以处理绝大多数遇到的问题。下面的一些问题基本直接来源于公司的面试笔试题目,方法不一定最优,如果你有更好的处理方法,欢迎与我讨 论。
本贴从解决这类问题的方法入手,开辟一系列专题来解决海量数据问题。拟包含 以下几个方面。
Bloom Filter
Hash
Bit-Map
堆(Heap)
双层桶划分
数据库索引
倒排索引(Inverted Index)
外排序
Trie树
MapReduce
在这些解决方案之上,再借助一定的例子来剖析海量数据处理问题的解决方案。
最简单的一点专业的事情让专业的人去做吧 招聘懂的人来做才王道
以上就是关于如何用代理键实现 DB2 UDB 主键麻烦告诉我全部的内容,包括:如何用代理键实现 DB2 UDB 主键麻烦告诉我、如何用代理键实现 DB2 UDB 主键、想向etl方向发展等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)