猜测游戏和镜屋
您的表很不错 — 为什么要从视图访问它?这样做有许多理由:
列级别安全性:排除那些您不希望用户在定义视图的 SELECT 中看到的列。
行级别安全性:除非您定义一个视图,否则 Windows/UNIX/OS/2 上的 DB2 v7 不允许您限制对表中某些行的访问(如果您希望限制对允许用户看到的内容的更新,请记得加上 check 选项):
create view london_football as
![]()
select * from football
![]()
where team in ('Arsenal','Aston Villa')
![]()
with check option
设想这一点对于“人力资源”应用程序的作用:用户可以查看薪水在 $nn,nnn 以下的雇员,给他们加薪而加薪后的薪水不会在 $nn,nnn 以上。
DROP COLUMN:DB2 不允许您删除一个列。我可以想到您希望删除列的三个理由:
回收空间:如果您希望这样做,可以导出您希望保存的数据,删除那个表,用您需要的那些列重新创建表,然后装入这个表。这是否代价
高昂?当然是,但是回收空间需要这样或者 REORG TABLE。这些本来就是代价高昂的操作。
这个列不再是行的逻辑部分:例如,您意识到您的雇员可能有两个地址,并且停止跟踪雇员(employee)表中的地址(雇员表和雇员地址(employee_address)表之间现在有 n:m 关系)。在雇员表上创建一个不包含地址列的视图。
如果您真的要用新奇的方法,可以使用 RENAME TABLE 命令给基表一个新的名称,然后将原始表名作为该视图的名称。您的视图也可以连接雇员表中的有用列和从雇员地址获得的地址。现在我们回到了关系的正道。
列变宽了。如果它是 VARCHAR,那您运气不错。DB2 允许您将 VARCHAR 列最多加宽至表空间(tablespace)中定义的页大小宽度(缺省的 4K 页大小为 4,005,而在 32K 页上最多为 32,672):
create table t2 (col1 varchar(10))
![]()
alter table t2 alter column col1 set data type varchar(12)
我很喜欢这个视图,所以我实现它
如果派生列对您来说还不够坏,整个派生表怎么样?使它与基表中的数据匹配或不匹配(以及使每个 SELECT 成为潜在的错读)的能力又如何?Oracle 称这些为实现的视图。DB2 称它们为自动汇总表,在特殊情况下称为复制汇总表。如果经常被问到一个问题(SELECT MAX(ORDERS) FROM LEADS),或者经常组装一个聚集(SELECT COUNT(FRANCHISES) FROM STORES WHERE STATE=’TEXAS’),那么或许值得将结果集存储在磁盘上,这样 DB2 就不必每天重新计算它二十次:特别当几天前的数据足以准确地支持基于查询的决策时。
让我们从想知道哪个客户订购最多的贪婪的销售经理开始。他们在名为 LEADS 的表中跟踪这一项,推断出客户过去所下订单的数目可能有助于确定哪些销售线索最有可能变为真实的销售。这个问题每天会被问几次(如果您预感这正在发生并且需要验证它,您可以使用名为 Query Patroller 的 DB2 工具查看来自用户的查询)。SELECT MAX() 通常需要一个表扫描,这会强制 DB2 查看表中的每一行。如果您有许多线索,则需要扫描许多行才能找到一个值。定义一个汇总表允许 DB2 将这个值存储在磁盘上,这样 DB2 只用读一行就可以得到答案:
create summary table leads_max
![]()
(MAX_ORDERS) as (SELECT MAX(ORDERS) FROM LEADS )
![]()
DATA INITIALLY DEFERRED
![]()
REFRESH DEFERRED
创建汇总表后,用这条命令填充它:
REFRESH TABLE LEADS_MAX
用户不必了解汇总表。DB2 优化器会决定何时使用基本表,何时使用汇总表。请注意 REFRESH DEFERRED 子句:您正在告诉 DB2 旧数据在汇总表中是可接受的。这在您不需要准确答案或当前答案时是合适的。它适合构建一个业务计划,但对于要怎样存储银行余额,它就不适合了。请参阅 SQL Reference 中的特殊寄存器 CURRENT REFRESH AGE 以及 Administration Guide 中的“Creating a Summary Table”一节,以了解在答案可以“足够接近”、无需精确时,如何为汇总表中的旧数据设置容忍度。
REFRESH DEFERRED 是总结只读表上数据的理想选择。多分区数据库的特殊汇总表称为复制汇总表。您将在 DB2 EEE 中使用它以在每个分区都有小型表(或只读表)的副本。在 EEE 中,您通常将最大的表(称为事实表)分布到所有的分区。大量使用的连接键(如客户号码)应该作为分区键使用。DB2 将数据进行散列处理以对它分区。这意味着较少使用的连接键(如国家/地区或部门)可能会以次优化方式分布。当您在多分区数据库中连接数据时,与组合的连接更快(例如,CUSTOMER 和 COUNTRY 表中所有 COUNTRY 为 Argentina 的行都在同一分区)。如果 COUNTRY 不是分区键,这是不可能的。要获得组合,您可以将较小的表限制在一个分区,然后创建一个将它复制到其它分区的复制汇总表。这一策略在所复制的表较小或很少有更改时奏效(如果您在经常更改国名的国家做生意的话,要避免这么做)。如果表确实很小(如各大洲的列表),不要费心去复制它:DB2 将把它传送到所有分区并在连接期间将它保留在内存中。不要担心通过名称连接到副本:判断副本表何时可以提高性能是 DB2 的工作。
通过使汇总表 REFRESH IMMEDIATE,可以将它们用于动态数据。这有比 REFRESH DEFERRED 更严格的规则,所以请仔细阅读 SQL Reference。在首次创建汇总表之后,您仍必须使用 REFRESH TABLE 语句:
CREATE SUMMARY TABLE LEADS_BY_STATE
![]()
(NUM_LEADS, GRP_STATE)
![]()
AS (SELECT COUNT(ORDERS), STATE FROM LEADS GROUP BY STATE)
![]()
DATA INITIALLY DEFERRED
![]()
REFRESH IMMEDIATE
![]()
REFRESH TABLE LEADS_BY_STATE
也可以这样
我们现在已研究了两种视图。作为标准视图,视图定义存储在数据库中(在 SYSCAT.VIEWS.TEXT 中)而数据只存储在基表中。我们可以通过创建汇总表使得在这个数据上执行 SELECT 操作更快,这是以冗余数据为代价,它消耗更多磁盘空间并使得 INSERT、UPDATE 和 DELETE 更慢(或让基表和汇总表不同步,至少在下一次刷新以前是这样)。还有另一个极端:创建一个仅在数据库连接期间存在的聚集,或者甚至和 SQL 语句的生命期一样短。第一个称为 DECLARED TEMPORARY TABLE,第二个称为 COMMON TABLE EXPRESSION,也称为 TEMPORARY RESULT TABLE。一个声明的临时表需要一个 USER TEMPORARY TABLESPACE,您可以用 CREATE TABLESPACE 命令创建它(请参阅 SQL Reference)。您可以将这个临时表声明为应用程序运行时数据的保留位置。
DECLARE GLOBAL TEMPORARY TABLE table1
![]()
(column1 INT, column2 INT)
![]()
NOT LOGGED
您用模式 SESSION 限定表,因为它属于您连接到数据库时创建的会话:
INSERT INTO SESSION.TABLE1 VALUES (4,5)
![]()
SELECT * FROM SESSION.TABLE1
您可能希望这个临时表与现有表匹配,因此您可以用现有表的 SELECT 语句填充它。如果这样的话,使用 LIKE 创建它:
DECLARE GLOBAL TEMPORARY TABLE TEMP_EMP
![]()
LIKE EMPLOYEE
![]()
NOT LOGGED
![]()
INSERT INTO SESSION.TEMP_EMP
![]()
SELECT * FROM EMPLOYEE
当您断开连接时,DB2 将删除这个临时表。对于某些更临时的东西,DB2 支持公共表表达式,它允许您定义只存在于一条语句的表。公共
表表达式还是另一个细微问题的答案:给一个不是以动词开始的 SQL 语句命名:
WITH COMPENSATION AS
![]()
(SELECT SUM(SALARY+COMMISSION)
![]()
AS TOTAL FROM EMPLOYEE)
![]()
SELECT TOTAL FROM COMPENSATION
您现在已被护送出红灯区。公共表表达式并不违背关系原则:它不要求 DB2 存储派生数据,也不添加人工列。如果一定要从这个故事引出一个寓意的话,假定用计算机解决一个问题有 n 种方法。一种方法可能成本最低,一种方法对您而言最快,一种方法对用户而言最快,而另一种方法对于继承您的设计以进行维护和添加新功能的开发人员而言最快。至于哪种选择最好,就作为习题留给读者吧。
