服务器 频道

当DB2优化器非正常工作时该怎么办?

  为什么有时计算 MIN 比计算 MAX 快很多

  查询 SELECT MIN(TOTAL_AMOUNT) FROM CUSTOMER 查找 TOTAL_AMOUNT 上的现有索引中的值,并立即返回答案。但是,一个非常相似的查询 SELECT MAX(TOTAL_AMOUNT) FROM CUSTOMER 却需要耗费多得多的时间。执行计划指出优化器选择了扫描整个索引来计算 MAX。为什么?

  在这种特殊情况下,没有更好的选择。TOTAL_AMOUNT 上的索引不允许反向扫描:

  SELECT REVERSE_SCANS FROM SYSCAT.INDEXES WHERE

  INDNAME = ''CUSTOMER_AMT''

  REVERSE_SCANS

  -------------

  N

  在删除索引并用选项 ALLOW REVERSE SCANS 重新创建它之后,这两个查询开始运行得一样快了。

  CREATE INDEX CUSTOMER_AMT ON CUSTOMER(TOTAL_AMOUNT) ALLOW REVERSE SCANS

  RUNSTATS ON TABLE MYSCHEMA.CUSTOMER FOR INDEX MYSCHEMA. CUSTOMER_AMT

  缺省情况下,DB2 索引不允许反向扫描。

  提示:每当您在 CREATE TABLE 语句中创建 PRIMARY KEY、FOREIGN KEY 或 UNIQUE 约束时,就会隐式地创建一个索引。该索引不允许反向扫描。

  您可以覆盖缺省行为:

  创建一个没有约束的表(或删除现有约束)。

  创建适当的索引。

  使用 ALTER TABLE SQL 语句创建约束。

  例如:

  ALTER TABLE CUSTOMER DROP PRIMARY KEY;

  --or create a table not defining a primary key

  CREATE UNIQUE INDEX CUSTOMER_ID ON CUSTOMER(ID) ALLOW REVERSE SCANS;

  ALTER TABLE CUSTOMER ADD PRIMARY KEY(ID);

  DB2 会给出一条警告,并重用第 2 步中创建的索引。

  正如我们所见,在一些十分常见的情况下,允许反向扫描的索引是必要的。

  消除不必要的连接

  让我们考虑以下视图:

  CREATE VIEW CUSTOMER_ORDER_LIST

  AS

  SELECT

  CUSTOMER_ORDER.CUSTOMER_ID

  CUSTOMER.LAST_NAME

  CUSTOMER.FIRST_NAME

  CUSTOMER.PHONE

  CUSTOMER.EMAIL

  CUSTOMER_ORDER.ORDER_DT

  CUSTOMER_ORDER.AMOUNT

  CUSTOMER_ORDER.STATUS

  FROM CUSTOMER JOIN CUSTOMER_ORDER

  ON CUSTOMER.ID = CUSTOMER_ORDER.CUSTOMER_ID

  CUSTOMER_ORDER 表中的所有记录在 CUSTOMER 表中都有父记录。该业务规则是由触发器维护的,而不是由外键约束维护的。(不要问我为什么。我能说的就是我在生产数据库中已经很多次看到它了。)

  考虑查询:

  SELECT CUSTOMER_ID, ORDER_DT, AMOUNT, STATUS FROM CUSTOMER_ORDER_LIST

  您可能会认为根本不需要访问 CUSTOMER 表,因为所有必需的信息都在 CUSTOMER_ORDER 表的视图中,对吗?

  事实并非这样。出于某些原因,优化器选择访问 CUSTOMER 表上的索引:

  Estimated Cost = 25693

  Access Table Name = DB2INST1.CUSTOMER ID = 2,5

  | #Columns = 1

  | Index Scan: Name = SYSIBM.SQL021126111001110 ID = 3

  | | Index Columns:

  | | | 1: ID (Ascending)

  | | #Key Columns = 0

  | | | Start Key: Beginning of Index

  | | | Stop Key: End of Index

  | | Index-Only Access

  | | Index Prefetch: Eligible 199

  | Lock Intents

  | | Table: Intent Share

  | | Row : Next Key Share

  Merge Join

  | Access Table Name = DB2INST1.CUSTOMER_ORDER ID = 2,6

  (这只是部分输出。)

  究竟为什么要访问 CUSTOMER 表呢?优化器的选择实际上非常有道理:您可能轻易地删除了触发器,将一条违反引用完整性的记录插入 CUSTOMER_ORDER 表中,并重新创建了触发器。记录将保留在 CUSTOMER_ORDER 表中,这意味着存在这种情况:触发器不保证引用完整性。这就意味着优化器必须假设 CUSTOMER_ORDER 表中可能有一些记录在 CUSTOMER 表中没有匹配的记录,因此查找 CUSTOMER 表上的记录是必要的。

  现在,让我们创建适当的约束,看看会发生什么:

  ALTER TABLE CUSTOMER_ORDER ADD FOREIGN KEY(CUSTOMER_ID) REFERENCES CUSTOMER(ID)

  如果有任何记录违反了该约束,那么这条语句就会失败。现在,优化器能消除不必要的连接,而且查询可以运行得更快:

  Estimated Cost = 18067

  Access Table Name = DB2INST1.CUSTOMER_ORDER ID = 2,6

  | #Columns = 1

  | Relation Scan

  | | Prefetch: Eligible

  | Lock Intents

  | | Table: Intent Share

  | | Row : Next Key Share

  | Return Data to Application

  | | #Columns = 1

  Return Data Completion

  正如我们所见,添加外键约束向优化器提供了一些非常有用的数据。优化器则向我们提供更有效的执行计划作为报答。

0
相关文章