使用物化视图和查询重写功能

OBE 主页 > 10gR2 单实例 > 商务智能

使用物化视图和查询重写功能

在本教程中,您将了解如何利用强大的物化视图和查询重写功能。

大约 2 个小时

主题

本教程包括下列主题:

将鼠标置于此图标上以加载和查看本教程的所有屏幕截图。(警告:因为此操作会同时加载所有屏幕截图,所以网速较慢时,响应时间可能会比较长。)

注意:此外,您还可以在下列步骤中将鼠标放在每个单独的图标上,从而仅加载和查看与该步骤相关的屏幕截图。您可以单击单个屏幕截图将其隐藏。

概述

通过使用概要管理 (Summary Management) 特性,您可以减轻数据库管理员的工作负荷,这是因为您无需再手动创建概要,而且最终用户也不再必须知道已经定义的概要。一旦您创建了一个或多个物化视图(它们和概要等效),最终用户就可以查询数据库中的表和视图。Oracle 服务器中的查询重写机制自动重写 SQL 查询以使用概要表。这一机制减少了从查询返回结果的响应时间。数据仓库内的物化视图对于最终用户或数据库应用程序来说是透明的。

Oracle 数据库为您提供用于更先进的重写和刷新机制的增强功能,以及全面的顾问与调整框架。这为您的环境带来了更加优化的物化视图,在占用额外空间最小的情况下提升了性能。

注意:本教程不是物化视图的介绍。它假定读者对物化视图功能有基本了解。如果您需要更多关于某些主题的背景信息,请参看 Oracle 数据仓库指南。

返回主题列表

您将使用 SALES HISTORY (SH) 示例模式创建、修改并分析物化视图和重写功能。此研讨会依赖于在默认安装上对 SH 模式进行的一些小修改。

返回主题列表

前提条件

开始本教程之前,您应该:

1.

完成了教程在 Windows 上安装 Oracle 数据库 10g

2.

下载 mv.zip 并将其解压缩到您的工作目录 (c:\wkdir) 中。

返回主题列表

为销售历史模式实施模式更改

在您启动物化视图功能前,需要对现有的 Sales History 模式对象进行一些更改,还必须将一些额外的系统权限授予用户 SH。使用 SQL 文件 modifySH_10gR2.sql 来应用这些更改。

1.

启动一个 SQL*Plus 会话。选择开始 > 程序 > Oracle-OraDB10g_home > Application Development > SQL Plus

(请注意:本教程假设您拥有 c:\wkdir 文件夹。如果没有,则需要创建此文件夹,并将 mv.zip 的内容解压缩到此文件夹中。在执行脚本时,可指定路经。)

 

2.

SH 用户的身份登录。输入 SH 作为 User Name,并输入 SH 作为 Password。然后单击 OK

 

3.

从 SQL*Plus 会话运行 modifySH_10gR2.sql 脚本。

@c:\wkdir\modifySH_10gR2.sql

获得的输出的底部应与以下图像匹配。

4.

执行 xrwutl.sql 脚本。

@c:\wkdir\xrwutl.sql

获得的输出的底部应与以下图像匹配。

返回主题列表

启用查询重写

要启用查询重写,需要满足以下条件:

单个物化视图必须有 ENABLE QUERY REWRITE 子句。
必须将 QUERY_REWRITE_ENABLED 初始化参数设置为 TRUE(在 10g 中是默认设置)。或者,您可以将此参数设置为 FORCE,这将停用对重写计划的任何成本评估,并在任何可能的时候重写查询。
重写完整性模式和特定的物化视图状态必须匹配,以启用对这一特定物化视图的重写。

在这一部分中,将启用查询重写。首先,需要确保有对数据库实例的基本初始化设置。为此,请执行下列步骤:

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 set_rewrite_session.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\set_rewrite_session

ALTER SESSION SET query_rewrite_integrity=TRUSTED;
ALTER SESSION SET query_rewrite_enabled=FORCE;
show parameters query

您将启用查询重写,并使用“trusted”模式。这是最常用的完整性级别。在 trusted 模式中,优化程序相信物化视图中的数据是最新的,在维度中声明的关系和 RELY 约束条件是正确的。在这一方式中,优化程序还将使用预先建立的物化视图或基于视图的物化视图,并使用未执行和已经得到执行的关系。在这一模式中,优化程序还“信任”已经声明但尚未 ENABLED VALIDATED 的主/唯一键约束条件和通过维度指定的数据关系。

有关 query_rewrite_integrity 的级别和 query_rewrite_enabled 的更多详细信息,请参考 Oracle 据仓库指南。


返回主题列表

分析物化视图更新和重写功能

Oracle 数据库 10g 提供用于分析现有的和潜在的物化视图的过程。这使您能够充分利用所有强大的物化视图功能。

要分析其潜在物化视图的刷新和重写功能,请执行以下步骤:

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 create_mv1.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\create_mv1.sql

DROP MATERIALIZED VIEW cust_sales_mv ;

CREATE MATERIALIZED VIEW cust_sales_mv
   BUILD IMMEDIATE
   REFRESH FAST ON COMMIT
   ENABLE QUERY REWRITE
   AS
   SELECT c.cust_id,
          SUM(amount_sold) AS dollar_sales
   FROM   sales s, customers c
   WHERE  s.cust_id= c.cust_id
   GROUP  BY c.cust_id;

这个语句失败并引发下面的错误:

ORA-23413: table "SH"."CUSTOMERS" does not have 
           a materialized view log.

您可以尝试手动纠正这个错误并尝试再次创建此物化视图。但是,这是一个循环反复而且耗时的过程。而使用带有 dbms_mview package 的 Oracle 数据库 10g 功能,您可以轻松修复这个错误。您马上就会看到这一点。


2.

即便这个语句成功,您也不能了解其快速刷新功能的详细信息。通过使用 dbms_mview.explain_mview package,您对潜在物化视图的功能会有更深入的了解,从而能够在其创建之前解决所有问题。

@c:\wkdir\explain_mv1.sql

truncate table mv_capabilities_table;

exec dbms_mview.explain_mview( -
   'SELECT   c.cust_id, SUM(amount_sold) AS dollar_sales -
    FROM     sales s, customers c -
    WHERE    s.cust_id= c.cust_id -
    GROUP BY c.cust_id');

set serveroutput on

begin
   for crec in ( select capability_name, possible, 
                        related_text, msgtxt
                 from   mv_capabilities_table order by 1) loop
    dbms_output.put_line(crec.capability_name ||': '||crec.possible);
    dbms_output.put_line(crec.related_text||': '||crec.msgtxt);
   end loop;
end;
/

在输出中,您会看到系统指向 SALES 表上的另外一个缺少的物化视图日志。建议始终在潜在物化视图创建之前,用上述 dbms_mview.explain_mview 包对其进行分析,而非对系统进行反复试验 (trial-and-error)。

除了 CUSTOMERS SALES 上缺少的物化视图日志,系统还检测到您需要向此物化视图添加额外的聚合函数,来为所有类型的 DML 操作全面启用快速刷新功能。

系统建议的聚合函数是:

  • COUNT(*)
  • COUNT(amount_sold)

dbms_mview.explain_mview 包的输出在此显示。您会发现,它不仅包括物化视图的刷新功能,还包括物化视图的重写和分区变化跟踪(Partition-Change-Tracking,PCT)功能我们会在后面讨论这些功能。

 

3.

要纠正这一点,首先创建上面识别出的物化视图日志。

@c:\wkdir\create_mv_logs1.sql

DROP MATERIALIZED VIEW LOG ON sales;

CREATE MATERIALIZED VIEW LOG ON sales
  WITH ROWID, SEQUENCE
  (prod_id, cust_id, time_id, channel_id, promo_id, 
   quantity_sold, amount_sold)
  INCLUDING NEW VALUES ;

DROP MATERIALIZED VIEW LOG ON customers;

CREATE MATERIALIZED VIEW LOG ON customers
  WITH ROWID, SEQUENCE
  (cust_id,cust_first_name,cust_last_name,cust_gender,cust_year_of_birth
  ,cust_marital_status,cust_street_address,cust_postal_code,cust_city
  ,cust_state_province,country_id,cust_main_phone_number,cust_income_level
  ,cust_credit_limit,cust_email)
  INCLUDING NEW VALUES;

这一个在后面会使用。

DROP MATERIALIZED VIEW LOG ON products;
CREATE MATERIALIZED VIEW LOG ON products
  WITH ROWID, SEQUENCE
  (prod_id,prod_name,prod_desc,prod_subcategory,prod_subcategory_desc
  ,prod_category,prod_category_desc,prod_weight_class,prod_unit_of_measure
  ,prod_pack_size,supplier_id,prod_status,prod_list_price,prod_min_price)
  INCLUDING NEW VALUES;

4.

再次检查潜在物化视图的功能。

@c:\wkdir\explain_mv1a.sql

TRUNCATE TABLE mv_capabilities_table;

EXEC dbms_mview.explain_mview( -
   'SELECT   c.cust_id, SUM(amount_sold) AS dollar_sales, -
             COUNT(amount_sold) AS cnt_dollars, COUNT(*)  -
    FROM     sales s, customers c -
    WHERE    s.cust_id= c.cust_id -
    GROUP BY c.cust_id');

set serveroutput on

BEGIN
   for crec in (select capability_name, possible, 
                       related_text, msgtxt
                from   mv_capabilities_table order by 1) loop
    dbms_output.put_line(crec.capability_name ||': '||crec.possible);
    dbms_output.put_line(crec.related_text||': '||crec.msgtxt);
   end loop;
END;
/

此潜在物化视图的快速刷新功能已经按期望进行了更改。

 

5.

现在创建物化视图。

@c:\wkdir\create_mv1b.sql

DROP MATERIALIZED VIEW cust_sales_mv ;

CREATE MATERIALIZED VIEW cust_sales_mv
   BUILD IMMEDIATE
   REFRESH FAST ON COMMIT
   ENABLE QUERY REWRITE
   AS
   SELECT c.cust_id,
          SUM(amount_sold) AS dollar_sales,
          COUNT(amount_sold) AS cnt_dollars,
          COUNT(*) AS cnt
   FROM   sales s, customers c
   WHERE  s.cust_id= c.cust_id
   GROUP BY c.cust_id;

 

6.

explain_mview 过程也使用现有的物化视图。

@c:\wkdir\explain_mv1b.sql

TRUNCATE TABLE mv_capabilities_table;

EXEC dbms_mview.explain_mview('cust_sales_mv');

set serveroutput on

begin
   for crec in (select capability_name, possible, 
                       related_text, msgtxt
                from   mv_capabilities_table order by 1) loop
     dbms_output.put_line(crec.capability_name ||': '||crec.possible);
     dbms_output.put_line(crec.related_text||': '||crec.msgtxt);
    end loop;
end;
/


从用于物化视图的潜在 SQL 语句开始,您已经能够无须创建物化视图就能对其功能进行完全的分析了。

 

返回主题列表

Oracle 的查询重写功能

优化程序使用许多不同方法来重写查询。第一个,也是最重要的步骤,是确定查询所请求的全部或部分结果是否可以从存储在物化视图的预计算结果中获得。

最简单的情况是,存储在物化视图中的结果与查询所请求的结果完全匹配。Oracle 优化程序通过将查询的文本与物化视图定义的文本相比较来作出这种类型的决定。这种方法是最简单的,但符合这种查询重写类型的查询的数量最少。

当文本比较测试失败后,Oracle 优化程序将基于联接、选择、分组、聚合以及抓取的列数据执行一系列通用检查。这是通过将查询的不同子句(如 SELECTFROMWHEREHAVINGGROUP BY)与物化视图的不同子句分别进行比较而完成的。

返回主题列表

使用部分文本匹配重写

最简单的重写机制是文本匹配重写。在全文本匹配中,查询的整个文本会与物化视图定义的整个文本(即整个 SELECT 表达式)相比较 — 在文本比较过程中,忽略空白。在全文本匹配失败后,优化程序会尝试进行部分文本匹配。在这种方法中,以查询的 FROM 子句开始的文本会与以物化视图定义的 FROM 子句开始的文本进行比较。

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 explain_rewrite1.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\explain_rewrite1.sql

Rem REWRITE
DELETE FROM plan_table;
COMMIT;

EXPLAIN PLAN FOR
   SELECT c.cust_id,
          SUM(amount_sold) AS dollar_sales
   FROM   sales s, customers c
   WHERE  s.cust_id= c.cust_id
   GROUP BY c.cust_id;

set linesize 132

set pagesize 999
select * from table(dbms_xplan.display);

该计划显示,使用部分文本匹配重写机制,通过 cust_sales_mv materialized 视图重写了查询。以 FROM 子句开始,SQL 语句和物化视图是相同的。

在重写查询的同时,以与访问普通表相同的方法对物化视图的访问计划进行了研究,以便可以使用所有现有索引。


2.

执行查询。

@c:\wkdir\do_rewrite1.sql

set timing on

SELECT c.cust_id,
       SUM(amount_sold) AS dollar_sales
FROM   sales s, customers c
WHERE  s.cust_id= c.cust_id
GROUP BY c.cust_id;

这一查询的执行很快就有结果,因为它只访问在 cust_sales_mv 中已经联接和聚合的信息。

 

3.

可以使用 NOREWRITE 提示来实施针对非编写语句的计划。这使对一个查询是否重写的控制能下至语句级别。

@c:\wkdir\explain_norewrite.sql

DELETE FROM plan_table;
COMMIT;

EXPLAIN PLAN FOR
   SELECT   /*+ norewrite */
            c.cust_id,
            SUM(amount_sold) AS dollar_sales
   FROM     sales s, customers c
   WHERE    s.cust_id= c.cust_id
   GROUP BY c.cust_id;

set linesize 132
set pagesize 999
select * from table(dbms_xplan.display);

如果没有查询重写功能,您必须从 SALES 和与 CUSTOMERS 的联接进行完整扫描。

注意:由于时间限制,不运行此查询。

 

返回主题

在预先构建的表上创建物化视图:

在数据仓库环境中,有已经创建好的概要表或聚合表是很普遍的。您无须通过创建新的物化视图来重复此工作。这个解决方案将改善物化视图的性能,但是,它不能:

由于这些限制,而且由于现有的物化视图可能极为巨大且重建起来费用高昂,Oracle 数据库为您提供了将这些已经存在的概要表注册为物化视图的功能,从而避免了上述所有缺陷。您可以用 CREATE MATERIALIZED VIEW ...ON PREBUILT TABLE 语句注册物化视图。注册之后,物化视图可以用于查询重写,由刷新方法之一或二者一起维护。

Oracle 为其现有客户群实现了这一功能,以提供安全的移植路径并保护投资。移植带有“手工制造”的概要表和刷新过程的现有数据仓库环境,可以用单个 DDL 命令来利用重写功能,不会影响任何现有代码。

例如,MyCompany 最近从 Oracle9i 移植到了 Oracle10g,而且确实有这种手动创建的聚合表,在现有的所有数据仓库系统中 90% 以上也是如此。要将现有的 cust_id_sales_aggr 表注册为物化视图,请执行以下步骤:

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 create_mv2.sql,或将以 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\create_mv2.sql

DROP MATERIALIZED VIEW cust_sales_aggr ;

CREATE MATERIALIZED VIEW cust_sales_aggr
  ON PREBUILT TABLE
  REFRESH FORCE
  ENABLE QUERY REWRITE
  AS
    SELECT c.cust_id,
      c.cust_last_name,
      c.cust_first_name,
      SUM(amount_sold) AS dollar_sales,
      COUNT(amount_sold) AS cnt_dollars,
      COUNT(*) AS cnt
    FROM sales s, customers c
    WHERE s.cust_id= c.cust_id
    GROUP BY c.cust_id,
      c.cust_last_name,
      c.cust_first_name; 

这个语句相当快。它完全不会触及任何数据。只是创建物化视图的元信息:哪些联接,哪些聚合,涉及到哪些表和列。

对于查询重写的最高级别的数据完整性 (query_rewrite_integrity=ENFORCED)而言,在预先建立的表上使用物化视图是不可能的,因为对于数据完整性,系统“信任”您是创建者。只要您开始使用 Oracle 的刷新功能,系统就会知晓数据的完整性。但是,在任何情况下,第一次刷新都会是一次完整的刷新。

 

返回主题

使用简单的联回重写

当文本比较测试失败后,Oracle 优化程序将基于联接、选择、分组、聚合以及抓取的列数据执行一系列通用检查。这是通过将查询的不同子句( SELECTFROMWHEREHAVINGGROUP BY) 与物化视图的不同子句分别进行比较而完成的。要使查询重写发生,查询不必始终都完全匹配。例如,假定您的物化视图是按 cust_id 分组,而查询按 cust_last_name 分组。使用一种称为联回的方法,仍可以进行查询重写。

下面是联回重写的一个简单实例。cust_sales_mv 物化视图存储 cust_id 联接列,以与 sales 表和 customers 表确定 cust_credit_limit 值相同的方法与 customers 联接。

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 explain_rewrite2.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\explain_rewrite2.sql

DELETE FROM plan_table;
COMMIT;

EXPLAIN PLAN FOR
  SELECT c.cust_last_name,
    c.cust_credit_limit,
    SUM(amount_sold) AS dollar_sales
  FROM sales s, customers c 
  WHERE s.cust_id= c.cust_id
  GROUP BY c.cust_last_name,
    c.cust_credit_limit
  ORDER BY 1;

set linesize 140
SELECT * FROM TABLE(dbms_xplan.display);

在此计划中,您可以看到,通过使用 cust_id 列(它是物化视图的组成部分,并表示 sales 表与 customers 表之间的主键-外键关系),Oracle 使用了 cust_sales_mv 物化视图并将其联接回 customers 表。此外,查询中所请求的属性 cust_last_name 是一个来自 cust_id 的确定属性,因而系统知道不必在维点上进行额外聚合。它只须将物化视图联回到 customers 表。关于层次结构和确定属性的信息是 customers_dim(用于 customers 维的 Oracle 维对象)的组成部分。维定义 customers_dim 的重要部分显示如下:

LEVEL customer IS (customers.cust_id)
…
ATTRIBUTE customer DETERMINES
(cust_first_name, cust_last_name, 
 cust_credit_limit, cust_gender,...

注意:关于维对象的更多信息,请参看 Oracle 数据仓库指南。


2.

执行查询。请注意,只计算结果,而且实际上并不输出 (spool out) 所有返回的记录。

@c:\wkdir\do_rewrite2.sql

SELECT COUNT(*) 
FROM (SELECT c.cust_last_name, 
        c.cust_credit_limit,
        SUM(amount_sold) AS dollar_sales
      FROM sales s, customers c
      WHERE s.cust_id= c.cust_id
      GROUP BY c.cust_last_name,
        c.cust_credit_limit
       ORDER BY 1;

 

3.

用于非重写查询的计划可以通过以下语句显示:

@c:\wkdir\explain_norewrite2.sql

DELETE FROM  plan_table;
COMMIT;

EXPLAIN PLAN FOR
  SELECT /*+ NOREWRITE */ c.cust_last_name,
    c.cust_credit_limit,
    SUM(amount_sold) AS dollar_sales
  FROM sales s, customers c
  WHERE s.cust_id= c.cust_id
  GROUP BY c.cust_last_name,
    c.cust_credit_limit
  ORDER BY 1;

set linesize 120
select * from table(dbms_xplan.display);

若不重新查询,则需要处理完整的 sales 事实表与 customers 维表之间的联接。

 

返回主题

分析重写过程

若要了解系统中查询重写的可能候选对象的详细信息,可以使用 dbms_mview.explain_rewrite 过程。要分析之前执行的查询并更深入地了解重写过程,请执行以下步骤:

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 analyze_rewrite2.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\analyze_rewrite2.sql

TRUNCATE TABLE rewrite_table;

DECLARE
  querytxt VARCHAR2(1500) := 'select c.cust_last_name, c.cust_credit_limit,'
    ||
    ' SUM(amount_sold) AS dollar_sales ' ||
    'FROM sales s, customers c ' ||
    'WHERE s.cust_id= c.cust_id ' ||
    'GROUP BY c.cust_last_name, c.cust_credit_limit';
BEGIN
  dbms_mview.Explain_Rewrite(querytxt, NULL, 'ID1');
END;
/

SELECT message
FROM   rewrite_table
ORDER BY sequence DESC;

请注意,上面使用的物化视图并不是唯一适用的;基于预先建立的表的 cust_sales_aggr_id 物化视图也可进行重写了。在这种情况下,优化程序会基于成本做出决策。

 

返回主题

使用联回和卷积重写

除了物化视图的简单联回(请求相同聚合级的信息),物化视图还可以聚合到一个更高的级别,即所谓的 ROLLUP(卷积)操作。请看以下查询:

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 explain_rewrite3.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\explain_rewrite3.sql

DELETE FROM plan_table;
COMMIT;

EXPLAIN PLAN FOR
   SELECT   c.cust_state_province,
            SUM(amount_sold) AS dollar_sales
   FROM     sales s, customers c
   WHERE    s.cust_id= c.cust_id
   GROUP BY c.cust_state_province;

set linesize 132
set pagesize 999
select * from table(dbms_xplan.display);

在此计划中,您可以看到,通过使用 cust_id 列(它是物化视图的组成部分,并表示 sales 表与 customers 表之间的主键-外键关系),Oracle 使用 cust_sales_mv 物化视图并将其联接回到 customers 表。

但是,可能的联接键列的存在并不一定代表了
查询重写机制保证数据完整性所需的全部信息。

检查 customers_dim 维定义:

LEVEL customer  IS (customers.cust_id)
LEVEL city      IS (customers.cust_city)
LEVEL state     IS (customers.cust_state_province)
. . .
HIERARCHY geog_rollup (
      customer     CHILD OF
      city         CHILD OF
      state        CHILD OF
. . .

所请求的 cust_state_province 属性表示州级,这是比用户级(由 cust_id 表示)更高的聚合级。级别和层次代表一种声明方法,用来表示一个表内的 1:n 关系。在这里,它表示在不影响数据完整性的情况下,将所有客户信息汇总到州级的有效性。每个不同的客户值都将获得一个唯一的州值。


2.

现在提交查询。其运行速度相当快。

@c:\wkdir\do_rewrite3.sql

SELECT COUNT(*) 
FROM (SELECT   c.cust_state_province,
         SUM(amount_sold) AS dollar_sales
      FROM     sales s, customers c
      WHERE    s.cust_id= c.cust_id
      GROUP BY c.cust_state_province);

 

返回主题

使用复杂的联回和卷积重写

下面的示例演示了查询重写功能的强大能力和灵活性。以下示例不但进行了联回,而且还使用 customers_dim 维中的息在雪花模式中的两个表上进行了联回。为此,执行下列步骤:

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 explain_rewrite4.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\explain_rewrite4.sql

DELETE FROM plan_table;
COMMIT;

EXPLAIN PLAN FOR
   SELECT   co.country_name, 
            c.cust_state_province, SUM(amount_sold) AS dollar_sales 
   FROM     sales s, customers c, countries co
   WHERE    s.cust_id= c.cust_id 
     AND    c.country_id = co.country_id 
   GROUP BY co.country_name, c.cust_state_province 
   ORDER BY 1,2;

set linesize 132
set pagesize 999
select * from table(dbms_xplan.display);

优化程序将重写查询以利用 cust_sales_mv 物化视图,将其联回到 customers 并将 customers 联接到 countries 以满足查询。利用与在不需重写的语句级别上强制执行查询相同的方法,可以强制使用特定的物化视图。

必须进行各种数据完整性检查,以保证使用此物化视图的有效性。除了检查丢失或非复制联接外,对维信息的评估在重写过程中也扮演着重要角色。

检查优化程使用什么来重写这个查询。以下是 customers_dim 的维定义摘录。它显示了这个查询的重要部分:

LEVEL customer    IS (customers.cust_id)
LEVEL . . .
LEVEL state       IS (customers.cust_state_province)
LEVEL country     IS (countries.country_id)
. . .
HIERARCHY geog_rollup (
       customer      CHILD OF
       . . .
       state         CHILD OF
       country       CHILD OF
. . .
JOIN KEY (customers.country_id) REFERENCES country
)
. . .
ATTRIBUTE country DETERMINES (countries.country_name)

Oracle 数据库必须确定,它是否可以基于存储在该物化视图中的信息导出所有被请求的属性。查询中正在请求 countries.country_name customers.cust_state_province,而该物化视图只包含 cust_id 信息。

根据 customers_dim 维中的信息,Oracle 数据库可以确定:

  • customers.cust_state_province 可以通过物化视图中的 cust_id
    确定。它表示维中比客户级更高的聚合级。
  • countries.country_id 也可以通过物化视图中的 cust_id 确定。countries.country_id 描述比客户级更高的聚合级。
  • countries.country_name 是国家级层次的确定属性,
    因此可以基于 countries.country_id 来确定。
  • customers_dim 维描述了两个表之间的层次相关性。
    联接条件是维信息的组成部分。

Oracle 数据库通过所有这些信息,不但将物化视图与 customers 表联接,而且还将 customerscountries 联接,以获得查询的结果,并保证查询结果正确。


2.

重写的 SQL 语句如下所示:

@c:\wkdir\rewrite_sel.sql

SELECT COUNT(*) 
FROM  (SELECT co.country_name, 
         c.cust_state_province,
         SUM(mv.dollar_sales) AS dollar_sales
       FROM  cust_sales_mv mv, 
         (SELECT DISTINCT cust_id, cust_state_province, 
          country_id FROM customers) c, 
          countries co   
       WHERE mv.cust_id = c.cust_id
       AND   c.country_id = co.country_id
       GROUP BY co.country_name, c.cust_state_province
       ORDER BY 1,2);



3.

现在执行该查询。

@c:\wkdir\do_rewrite4.sql

SELECT   c.cust_state_province,
         SUM(amount_sold) AS dollar_sales
FROM     sales s, customers c
WHERE    s.cust_id= c.cust_id
GROUP BY c.cust_state_province;

 

4.

如果希望无需查询重写即可获得此计划,可以运行 explain_norewrite4.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中。

@c:\wkdir\explain_norewrite4.sql

DELETE FROM plan_table;
COMMIT;

EXPLAIN PLAN FOR
   SELECT /*+ norewrite */
          co.country_name,
          c.cust_state_province,
          SUM(amount_sold) AS dollar_sales
   FROM   sales s, customers c, countries co
   WHERE  s.cust_id= c.cust_id
   AND    c.country_id = co.country_id
   GROUP BY co.country_name, c.cust_state_province;

set linesize 132
set pagesize 999
select * from table(dbms_xplan.display);

请注意,在这个计划中未出现物化视图重写。

 

返回主题

在数据的子集上创建物化视图

很多时候,只是考虑对大型事实表中的信息子集进行更多的分析。以前,在这种情况下,为了利用物化视图,您需要为事实表创建一个包含所有信息的物化视图。现在,您可以在物化视图中并入一个谓词条件,以允许 TEXTMATCH 只重写功能。

要在数据子集上创建一个物化视图,并将其创建间和大小与不带谓词的、包含相同联接、聚合以及完整数据集的物化视图进行比较,请执行以下步骤:

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 create_mv3.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\create_mv3.sql

DROP MATERIALIZED VIEW some_cust_sales_mv;

CREATE MATERIALIZED VIEW some_cust_sales_mv
   BUILD IMMEDIATE
   REFRESH COMPLETE
   ENABLE QUERY REWRITE
   AS
   SELECT   c.cust_id, sum(s.amount_sold) AS dollars, p.prod_id,
            sum(s.quantity_sold) as quantity
   FROM     sales s , customers c, products p

   WHERE    c.cust_id = s.cust_id
   AND      s.prod_id = p.prod_id
   AND      c.cust_state_province IN 
               ('Dublin','Galway','Hamburg','Istanbul')
   GROUP BY c.cust_id, p.prod_id;

记录下创建物化视图所耗费的时间。

 

2.

现在,创建相同的物化视图,只是其中无需谓词来限制结果集。

@c:\wkdir\create_mv3b.sql

DROP MATERIALIZED VIEW all_cust_sales_mv;

CREATE MATERIALIZED VIEW all_cust_sales_mv
   BUILD IMMEDIATE
   REFRESH COMPLETE  
   ENABLE QUERY REWRITE
   AS
   SELECT c.cust_id, sum(s.amount_sold) AS dollars, p.prod_id,
          sum(s.quantity_sold) as quantity
   FROM   sales s , customers c, products p
   WHERE  c.cust_id = s.cust_id
   AND    s.prod_id = p.prod_id
   GROUP BY c.cust_id, p.prod_id;

这样创建物化视图用了更长的时间,这是由于所有数据都必须被触及、联接、聚合。

 

返回主题

使用多物化视图重写

Oracle 数据库 10g 第 2 版中的新增内容是,为了解决查询,可使用多个物化视图进行查询重写。

1.

创建以下两个物化视图,其中只包含 2001 年 11 月和 12 月的数据

在登录到 SH 模式的 SQL*Plus 会话中,运行 cr_qw_mmv.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\cr_qw_mmv.sql
CREATE MATERIALIZED VIEW nov_2001 
BUILD IMMEDIATE REFRESH COMPLETE 
ENABLE QUERY REWRITE AS 
SELECT cust_id, prod_id, time_id, sum(amount_sold) AS dollars 
FROM sales s 
WHERE time_id >= TO_DATE('01-NOV-2001','DD-MON-YYYY') AND 
time_id <= TO_DATE('30-NOV-2001','DD-MON-YYYY') 
GROUP BY time_id, cust_id, prod_id; 

CREATE MATERIALIZED VIEW dec_2001 
BUILD IMMEDIATE REFRESH COMPLETE 
ENABLE QUERY REWRITE AS 
SELECT cust_id, prod_id, 
time_id, sum(amount_sold) AS dollars 
FROM sales s 
WHERE time_id >= TO_DATE('01-DEC-2001','DD-MON-YYYY') AND 
time_id <= TO_DATE('31-DEC-2001','DD-MON-YYYY') 
GROUP BY time_id, cust_id, prod_id; 

 

2.

现在执行一个查询,获取在 11 月 5 日至 12 月 15 日期间按客户分类的销售额。运行 explain_mmv.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\explain_mmv.sql
DELETE FROM plan_table; 
COMMIT; 
EXPLAIN PLAN FOR 
SELECT cust_id, sum(amount_sold) AS dollars 
FROM sales s 
WHERE time_id >= TO_DATE('05-NOV-2001','DD-MON-YYYY') AND 
time_id <= TO_DATE('15-DEC-2001','DD-MON-YYYY') 
GROUP BY cust_id;

--See how the query uses only these two materialized views to obtain the data 

set linesize 132 
set pagesize 999 

select * from table(dbms_xplan.display);

 

返回主题

估计物化视图的大小

除了创建时间加快以外,存储物化视图所使用的空间也减少了。

您可以查询数据字典来获得关于物化视图大小的信息。遗憾的是,这只有在已经创建了物化视图后才能进行。理论上,您会希望在创建物化视图之前就能获得这些信息,尤其是在非常大的环境中。利用 dbms_olap.estimate_summary_size 程序包,您不必创建物化视图,就可以获得这一信息。

要使用 Oracle 数据库来估计两个已创建的物化视图的大小,并将其与它们的真实大小相比较,请执行以下步骤:

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 estimate_mv_size1.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中。这将为您提供包含所有数据的物化视图的估计大小。

@c:\wkdir\estimate_mv_size1.sql

set serveroutput on;

DECLARE
   no_of_rows NUMBER;
   mv_size NUMBER;
BEGIN
  no_of_rows :=555;
  mv_size :=5555;
  dbms_olap.estimate_summary_size ('MV 1',
      'SELECT   c.cust_id, sum(s.amount_sold) AS dollars, p.prod_id,
                sum(s.quantity_sold) as quantity
       FROM     sales s , customers c, products p
       WHERE    c.cust_id = s.cust_id
         AND    s.prod_id = p.prod_id
       GROUP BY c.cust_id, p.prod_id' , no_of_rows, mv_size );
  DBMS_OUTPUT.put_line ( '');
  DBMS_OUTPUT.put_line ( 'Complete MV');
  DBMS_OUTPUT.put_line ( 'No of Rows: ' || no_of_rows );
  DBMS_OUTPUT.put_line ( 'Size of Materialized view (MB): ' ||
    round(mv_size/(1024*1024),2) );
  DBMS_OUTPUT.put_line ( '');
END;
/

记录下创建物化视图所耗费的时间。

 

2.

确定包含数据子集的物化视图的大小:

@c:\wkdir\estimate_mv_size2.sql

DECLARE
   no_of_rows NUMBER;
   mv_size NUMBER;
BEGIN
  no_of_rows :=555;
  mv_size :=5555;
  dbms_olap.estimate_summary_size ('MV 2',
      'SELECT c.cust_id, sum(s.amount_sold) AS dollars, p.prod_id,
              sum(s.quantity_sold) as quantity
       FROM   sales s , customers c, products p
       WHERE  c.cust_id = s.cust_id
       AND    s.prod_id = p.prod_id
       AND    c.cust_state_province IN
                 (''Dublin'',''Galway'',''Hamburg'',''Istanbul'')
       GROUP BY c.cust_id, p.prod_id' , no_of_rows, mv_size );
  DBMS_OUTPUT.put_line ( 'Partial MV');
  DBMS_OUTPUT.put_line ( 'No of Rows: ' || no_of_rows );
  DBMS_OUTPUT.put_line ( 'Size of Materialized view (MB): ' ||
    round(mv_size/(1024*1024),2) );
  DBMS_OUTPUT.put_line ( '');
END;
/

 

3.

现在,查看数据字典以获取两个新物化视图的实际大小。

@c:\wkdir\comp_mv_size.sql

COLUMN "MV name" format a20

SELECT   substr(segment_name,1,30) "MV name", bytes/1024*1024 MB
FROM     user_segments 
WHERE    segment_name in ('SOME_CUST_SALES_MV','ALL_CUST_SALES_MV')
ORDER BY segment_name ;

只包含数据子集的物化视图与包含所有数据的视图相比,前者的大小是后者的 1/9。特别是在大型环境中,这会使用户获益匪浅,并简化了物化视图针对“特殊分析”的使用,只触及数据仓库中的部分信息。

 

返回主题

分析重写过程

对于能通过只包含数据子集的物化视图满足的查询,要检查其查询重写机制的决策过程,请执行以下步骤:

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 analyze_subset_rewrite.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\analyze_subset_rewrite.sql

TRUNCATE TABLE rewrite_table;
DECLARE
   no_of_msgs NUMBER;
   Rewrite_Array SYS.RewriteArrayType := SYS.RewriteArrayType();
   querytxt VARCHAR2(1500) := 
   'SELECT c.cust_id, sum(s.amount_sold) AS dollars, p.prod_id, 
           sum(s.quantity_sold) as quantity 
    FROM sales s , customers c, products p 
    WHERE c.cust_state_province = ''Dublin'' 
    AND (c.cust_id = s.cust_id) 
    AND (s.prod_id = p.prod_id) 
    GROUP BY c.cust_id, p.prod_id'

BEGIN
   dbms_mview.Explain_Rewrite(querytxt, '', Rewrite_Array);
   no_of_msgs := Rewrite_Array.count;
   FOR i IN 1..no_of_msgs LOOP
     DBMS_OUTPUT.PUT_LINE(Rewrite_Array(i).message);
   END LOOP;
 END;
 /
                 

您可以看到,选择子集物化视图的原因在于,较之包含所有数据的视图,其成本更低。

返回主题

用联回和聚合卷积重写

像任何其他物化视图一样,包含数据子集的物化视图可以用于查询重写。只须通过数据容量检查,这些物化视图就能够用于重写。

以下示例是将子集物化视图用于查询,其中,联回和聚合卷积是必不可少的。

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 explain_subset_rewrite2.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\explain_subset_rewrite2.sql

DELETE FROM plan_table;
COMMIT;

EXPLAIN PLAN FOR
   SELECT   c.cust_last_name, sum(s.amount_sold) AS dollars,
            sum(s.quantity_sold) as quantity
   FROM     sales s , customers c, products p
   WHERE    c.cust_id = s.cust_id
     AND    s.prod_id = p.prod_id
     AND    c.cust_state_province IN ('Dublin','Galway')
   GROUP BY c.cust_last_name;

set linesize 132
set pagesize 999

SELECT * FROM TABLE(dbms_xplan.display);

 

2.

同样运行这个查询:

@c:\wkdir\run_subset_rewrite2.sql

SELECT COUNT(*) FROM (SELECT   c.cust_last_name, 
                        sum(s.amount_sold) AS dollars,
                        sum(s.quantity_sold) as quantity
                      FROM     sales s , customers c, products p
                      WHERE    c.cust_id = s.cust_id
                        AND    s.prod_id = p.prod_id
                        AND    c.cust_state_province IN ('Dublin','Galway')
                      GROUP BY c.cust_last_name);

 

返回主题

返回主题列表

带有 GROUP BY 扩展项的增强的重写功能

通过以 GROUPING SETSROLLUP 及其连接的形式对 GROUP BY 子句进行扩展,您可以有选择地在查询的 GROUP BY 子句中指定所需的分组。

带有扩展 GROUP BY 的物化视图必须满足两个附加条件后,方可用于重写:

带有扩展 GROUP BY 的物化视图包含多个分组。Oracle 将查找可从中计算查询的成本最低的分组,并将其用于重写。

1.

创建包含扩展 GROUP BY 表达式的物化视图:

在登录到 SH 模式的 SQL*Plus 会话中,执行以下 SQL 语句:

@c:\wkdir\create_gby_mv.sql

DROP MATERIALIZED VIEW sales_cube_mv;

CREATE MATERIALIZED VIEW sales_cube_mv
   ENABLE QUERY REWRITE
   AS
   SELECT calendar_year year, calendar_quarter_desc quarter, 
          calendar_month_desc month, cust_state_province state, 
          cust_city city,
          GROUPING_ID (calendar_year,calendar_quarter_desc,
                       calendar_month_desc,
                       cust_state_province,cust_city) gid,
          GROUPING(calendar_year) grp_y,
          GROUPING(calendar_quarter_desc) grp_q,
          GROUPING(calendar_month_desc) grp_m,
          GROUPING(cust_state_province) grp_s,
          GROUPING(cust_city) grp_c,
          SUM(amount_sold) sum_sales
   FROM sales s, times t, customers c
   WHERE s.time_id=t.time_id AND s.cust_id=c.cust_id
   GROUP BY GROUPING SETS ((calendar_year, cust_city),
                           (calendar_year, cust_city, 
                            cust_state_province),
                           (calendar_year, calendar_quarter_desc, 
                            calendar_month_desc,cust_city));

exec dbms_stats.gather_table_stats('SH','sales_cube_mv');

DROP MATERIALIZED VIEW sales_gby_mv;

CREATE MATERIALIZED VIEW sales_gby_mv
   ENABLE QUERY REWRITE
   AS
   SELECT   calendar_year year, cust_state_province state,
            SUM(amount_sold) sum_sales
   FROM     sales s, times t, customers c
   WHERE    s.time_id=t.time_id AND s.cust_id=c.cust_id
   GROUP BY (calendar_year, cust_state_province);

exec dbms_stats.gather_table_stats('SH','sales_gby_mv');

以下查询包含与已创建的物化视图中完全相同的 GROUPING SETS。如您所见,查询就像是根据物化视图重写的。

 

2.

在登录到 SH 模式的 SQL*Plus 会话中,执行以下 SQL 语句:

@c:\wkdir\rewrite_gby1.sql

DELETE FROM plan_table;
COMMIT;

EXPLAIN PLAN FOR
  SELECT calendar_year year, calendar_quarter_desc quarter,
         calendar_month_desc month,
         cust_state_province state,
         SUM(amount_sold) sum_sales
  FROM   sales s, times t, customers c
  WHERE  s.time_id=t.time_id
    AND  s.cust_id=c.cust_id
  GROUP BY GROUPING SETS ((calendar_year, cust_city),
           (calendar_year, cust_city, cust_state_province),
           (calendar_year, calendar_quarter_desc,
            calendar_month_desc, cust_city));

PROMPT new output, using table function
set linesize 132
set pagesize 999
SELECT * FROM TABLE(dbms_xplan.display);

当物化视图和查询都包含 GROUP BY 扩展时,Oracle 将两个策略用于重写:分组匹配和 UNION ALL 重写。首先,Oracle 尝试分组匹配。查询中的分组与物化视图中的分组进行匹配,如果所有都匹配而没有卷积,则 Oracle 从物化视图中选择它们。组匹配在此例中进行。在不使用任何筛选条件的情况下,对物化视图进行全表扫描,以满足我们的查询。以下查询包含了与物化视图定义不同的 GROUPING SETS。此外,它从不是物化视图组成部分的 customers 表选择 country_id 列。

 

3.

在登录到 SH 模式的 SQL*Plus 会话中,执行以下 SQL 语句:

@c:\wkdir\rewrite_gby2.sql

PROMPT full access AND joinback to dimension customers
PROMPT decompose of grouping levels into UNION ALL

DELETE FROM plan_table;
COMMIT;

EXPLAIN PLAN FOR
  SELECT   calendar_year year, calendar_quarter_desc quarter,
           cust_state_province state, country_id,
           SUM(amount_sold) sum_sales
  FROM     sales s, times t, customers c
  WHERE    s.time_id=t.time_id
    AND    s.cust_id=c.cust_id
  GROUP BY GROUPING SETS ((calendar_year, country_id),
                          (calendar_year, cust_state_province),
                          (calendar_year, calendar_quarter_desc,
                           cust_state_province));

PROMPT new output, using table function
set linesize 132
set pagesize 999
SELECT * FROM TABLE(dbms_xplan.display);

在本例中,分组匹配失败。Oracle 将尝试一种名为 UNION ALL 重写的通用重写机制。Oracle 首先将带有扩展 GROUP BY 子句的查询表示为一个效 UNION ALL 查询。原始查询的每个分组都被放置在独立的 UNION ALL 分支中。这些分支都将具有一个简单的 GROUP BY 子句。

为了用现有物化视图实现上述查询,Oracle 已经将 GROUPING SETS 重写到了 3 个简单的 GROUP BY 表达式中,与一个 UNION ALL 运算符组合使用。然后,它单独研究每个 UNION ALL 分支的重写功能。每个分支都可能由包含一个简单(或扩展)GROUP BY 条件的物化视图改写。

所有基本重写机制(如 JOIN BACK)都会用到。

以下查询包含与物化视图定义不同的 GROUPING SETSGROUPING SETS 之一不能通过现有的物化视图解决,因此 Oracle 必须联回从表来满足这个查询。

 

4.

在登录到 SH 模式的 SQL*Plus 会话中,执行以下 SQL 语句:

@c:\wkdir\rewrite_gby3.sql

PROMPT full access of MV and usage of SALES fact for missing level
PROMPT decompose of grouping levels into UNION ALL

DELETE FROM plan_table;
COMMIT;

EXPLAIN PLAN FOR
  SELECT   calendar_year year, calendar_quarter_desc quarter,
           week_ending_day,cust_state_province state,
           SUM(amount_sold) sum_sales
  FROM     sales s, times t, customers c
  WHERE    s.time_id=t.time_id
    AND    s.cust_id=c.cust_id
  GROUP BY GROUPING SETS ((calendar_year),
                          (calendar_year, week_ending_day),
                          (calendar_year, cust_state_province),
                          (calendar_year, calendar_quarter_desc,
                           cust_state_province));

PROMPT new output, using table function
set linesize 132
set pagesize 999
SELECT * FROM TABLE (dbms_xplan.display);

 

返回主题列表

使用重写或错误来控制语句的执行

可能会有这种情况,即如果查询不重写,则希望停止执行该查询。这种情况之一是,您预期未重写查询的执行所耗费的时间长度令人无法接受。为达到这个要求,Oracle 数据库 10g 提供了一个名为 REWRITE_OR_ERROR 的新提示。这是一个查询块级提示。例如,如果 SELECT 语句没有进行重写,则会引发显示在消息中的错误。REWRITE_OR_ERROR 提示使您能够在查询中运行 DBMS_MVIEW.EXPLAIN_REWRITE(),解决导致重写失败的问题,并再次运行查询。

1.

准备环境以确保重新可能的查询。

@c:\wkdir\prep4_roe.sql

ALTER MATERIALIZED VIEW cust_sales_aggr disable query rewrite;
ALTER MATERIALIZED VIEW  cust_sales_mv disable query rewrite;
ALTER MATERIALIZED VIEW  sales_cube_mv disable query rewrite;
ALTER MATERIALIZED VIEW  sales_gby_mv disable query rewrite;
ALTER MATERIALIZED VIEW  nov_2001 disable query rewrite;
ALTER MATERIALIZED VIEW  dec_2001 disable query rewrite;

 

2.

以下语句通过任何物化视图都不会进行重写,如计划输出所示。

@c:\wkdir\xplan4_roe.sql 
			
EXPLAIN PLAN FOR
  SELECT c.cust_last_name,
    c.cust_credit_limit,
    SUM(amount_sold) AS dollar_sales
  FROM sales s, customers c
  WHERE s.cust_id= c.cust_id
  GROUP BY c.cust_last_name,
    c.cust_credit_limit
  ORDER BY 1;

set pagesize 50
set linesize 130
SELECT * FROM TABLE(dbms_xplan.display);

 

3.

因此,当您使用新的 REWRITE_OR_ERROR 功能时,查询将会失败。

@c:\wkdir\run_roe.sql


Rem ORA-30393: a query block in the statement did not rewrite
SELECT /*+ REWRITE_OR_ERROR */ c.cust_last_name,
  c.cust_credit_limit,
  SUM(amount_sold) AS dollar_sales
FROM sales s, customers c
WHERE s.cust_id= c.cust_id
GROUP BY c.cust_last_name,
  c.cust_credit_limit
ORDER BY 1;

 

返回主题列表

具备已分区的事实表会为物化视图带来两个额外的好处。如果只有某些分区发生了变化(由于 DML 或分区维护操作),则有助于:

分区和查询重写

当更新某一从表的分区时,物化视图只有特定部分会被标记为陈旧。物化视图必须具有这样的信息 — 能够识别与物化视图的特定行或组相对应的表的分区。最简单情况是,在物化视图的 SELECT 列表中提供了分区键 (partitioning key),因为这是将行映射到陈旧分区最简单的方法。在使用部分陈旧的物化视图时,要点是:

分区和刷新

在数据仓库中,对从表的更改通常会招致分区维护操作,如 DROPEXCHANGEMERGEADD PARTITION。要在此类操作后维护物化视图,您可以使用一种称作分区变化跟踪 (PCT) 的刷新。

要使 PCT 可用,从表必须进行分区。物化视图分区本身并不带有这个功能。如果 PCT 刷新可用,它将自动进行,而且进行过程无需用户干预。

以下示例使用了第二个事实表 (costs)。

1.

确保环境干净

2.

创建包含分区键的物化视图

3.

分析包含分区键的物化视图

4. costs 表执行分区维护操作,并检查物化视图的状态
5. 添加数据并通过分区执行快速刷新
6. 执行另一个分区维护操作

返回主题列表

1. 确保环境干净

首先准备环境以进行下列测试:

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 cleanup_for_pmop1.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\cleanup_for_pmop1.sql ALTER TABLE costs DROP PARTITION costs_q1_2002;
ALTER TABLE costs DROP PARTITION costs_q2_2002;
ALTER TABLE costs DROP PARTITION costs_1_2002;

我们需要物化视图日志,用于下一个物化视图的快速刷新功能。

DROP MATERIALIZED VIEW LOG ON costs;

CREATE MATERIALIZED VIEW LOG ON costs
   WITH ROWID, SEQUENCE
   (prod_id, time_id, unit_cost, unit_price )
   INCLUDING NEW VALUES ;

可以忽略任何 ORA-2149 ORA-12002 SQL 错误。


返回主题

2. 创建包含分区键的物化视图

要利用物化视图基于分区表的增强功能,最简单的方法是将分区键并入物化视图定义中。

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 create_pkey_mv.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\create_pkey_mv.sql

DROP MATERIALIZED VIEW costs_mv;

CREATE MATERIALIZED VIEW costs_mv
   BUILD IMMEDIATE
   REFRESH FAST ON DEMAND
   ENABLE QUERY REWRITE 
   AS
   SELECT time_id, prod_name, SUM( unit_cost) AS sum_units,
          COUNT(unit_cost) AS count_units, COUNT(*) AS cnt
   FROM   costs c, products p
   WHERE  c.prod_id = p.prod_id
   GROUP BY time_id, prod_name;

请注意,您将物化视图定义为 FAST REFRESHABLE ON DEMAND。该物化视图可能会变旧。

 

2.

物化视图的状态为 FRESH

@c:\wkdir\show_status_of_pkey_mv.sql

ALTER MATERIALIZED VIEW costs_mv COMPILE;

SELECT mview_name, refresh_mode, refresh_method, staleness 
FROM   user_mviews


返回主题

3. 分析包含分区键的物化视图

1.

使用您已经了解的 dbms_mview.explain_mview 过程。

@c:\wkdir\analyze_pkey_mv.sql

TRUNCATE TABLE mv_capabilities_table;

EXEC DBMS_MVIEW.EXPLAIN_MVIEW('costs_mv');

SET SERVEROUTPUT ON

BEGIN
   FOR crec IN (SELECT capability_name, possible, related_text, msgtxt 
                FROM mv_capabilities_table ORDER BY 1) LOOP
      DBMS_OUTPUT.PUT_LINE(crec.capability_name ||': '||crec.possible);
      DBMS_OUTPUT.PUT_LINE(crec.related_text||': '||crec.msgtxt);
   END LOOP;
END;
/

您可以看到,其中启用了分区变化跟踪 (PCT),用于 COSTS 表和查询重写。


返回主题

4. 对 COSTS 表执行分区维护操作,并检查物化视图的状态

要将一些空分区添加到 costs 表,并察看其如何影响物化视图的陈旧程度,请执行下列步骤:

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 add_part_to_cost.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\add_part_to_cost.sql

ALTER TABLE costs
   ADD PARTITION costs_q1_2002 
   values less than (TO_DATE('01-APR-2002', 'DD-MON-YYYY'));

ALTER TABLE costs 
   ADD PARTITION costs_q2_2002 
   values less than (TO_DATE('01-JUL-2002', 'DD-MON-YYYY'));

 

2.

物化视图的状态为 FRESH

@c:\wkdir\show_status_of_pkey_mv_cost.sql

ALTER MATERIALIZED VIEW costs_mv COMPILE;

SELECT mview_name, refresh_mode, refresh_method, staleness 
FROM   user_mviews
WHERE  mview_name like 'COST%'


返回主题

5. 添加数据并通过分区执行快速刷新

要将一些数据添加到第二个事实表 (costs),请执行下面的步骤。请注意,您只会将数据插入到最新添加的分区中。表中的其他数据没有被更改。

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 insert_costs.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\insert_costs.sql

INSERT INTO costs VALUES (20, '02-JAN-02',999, 99, 2.50, 25.00);
INSERT INTO costs VALUES (30, '02-FEB-02',999, 99, 2, 34);
INSERT INTO costs VALUES (30, '03-MAR-02',999, 99, 2, 34);
INSERT INTO costs VALUES (40, '21-APR-02',999, 99, 1, 35);
INSERT INTO costs VALUES (40, '22-MAY-02',999, 99, 3, 36);
INSERT INTO costs VALUES (30, '22-APR-02',999, 99, 4, 37);
INSERT INTO costs VALUES (20, '12-JUN-02',999, 99, 5, 34);

COMMIT;

 

2.

查询插入的数据。

@c:\wkdir\show_insert_costs.sql

Rem what's in the new partitions

SELECT * 
FROM   costs PARTITION (costs_q1_2002);

SELECT * 
FROM   costs PARTITION (costs_q2_2002);


 

3.

正如预期,物化视图的状态变为 STALE,这是因为新插入的数据当前并未反映在物化视图中。

@c:\wkdir\show_status_of_pkey_mv_cost.sql

ALTER MATERIALIZED VIEW costs_mv COMPILE;

SELECT mview_name, refresh_mode, refresh_method, staleness 
FROM   user_mviews
WHERE  mview_name like 'COST%';


返回主题

6. 执行另一个分区维护操作

您已经看到,物化视图通常是陈旧的。但是,只要陈旧数据未被触及,它就可以被用于重写。很快您就会有所尝试。此外,分区变化跟踪使您能够快速刷新 costs_mv物化视图

1.

为了控制刷新的过程,我们可以从物化视图查询所有单元销售的 SUM

@c:\wkdir\show_sum_pkey_mv.sql

SELECT SUM(sum_units) 
FROM   costs_mv;

 

2.

进行一次快速刷新。

@c:\wkdir\fast_refresh_pkey_mv.sql

Rem Now do a fast refresh
EXEC dbms_mview.refresh('costs_mv','F');

将刷新时间与物化视图的创建时间相比较。由于您只须刷新 costs_q1_2002costs_q2_2002 分区,刷新所需时间仅为其初始创建时间的一小部分。初始创建时间是进行一次完整更新所需的时间。

 

3.

再次检查内容:

@c:\wkdir\show_sum_pkey_mv.sql

SELECT  SUM(sum_units) 
FROM    costs_mv;


 

4.

显然,物化视图的状态又是新的了。

@c:\wkdir\show_status_of_pkey_mv_cost.sql

ALTER MATERIALIZED VIEW costs_mv COMPILE;

SELECT mview_name, refresh_mode, refresh_method, staleness 
FROM   user_mviews
WHERE  mview_name like 'COST%';

 

返回主题

返回主题列表

分区变化跟踪 (PCT) 要求在物化视图中有足够的信息,以能够将每个物化视图行关联回源分区从表中其相应的从表。这可以通过将从表分区键列包含在选择列表中(而且,如果使用了 GROUP BY,也要包含在 GROUP BY 列表中)来实现,如上例中所示。取决于所需的聚合级和分区键列的不同基数,这会造成不利的影响 — 大幅度提高物化视图的基数。例如,假定一个常用指标是一种产品在给定的一年期间产生的收入。如果 sales 表是按 time_id 分区的,那么,它将是物化视图中的 SELECT 子句和 GROUP BY 子句中的必需字段。如果每天销售 1000 种不同产品,这会大大增加物化视图中的行数。在很多情况下,PCT 的优势被这种对高聚合物化视图的限制抵消了。DBMS_MVIEW.PMARKER 函数旨在大幅度降低物化视图的基数。该函数返回一个分区标识符,此标识符唯一地标识特定分区表内的特定行的分区。DBMS_MVIEW.PMARKER 函数用于替代 SELECT 子句和 GROUPBY 子句中的分区键列。下一个示例将演示 PMARKER 函数的功能。

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 create_pm_mv.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中。这将会创建一个带有 PMARKER 信息的物化视图:

@c:\wkdir\create_pm_mv.sql

DROP MATERIALIZED VIEW costs_pm_mv ;

CREATE MATERIALIZED VIEW costs_pm_mv
   BUILD IMMEDIATE
   REFRESH FAST ON DEMAND
   ENABLE QUERY REWRITE
   AS
   SELECT DBMS_MVIEW.PMARKER(c.rowid) AS pmarker_costs,
          prod_name, SUM( unit_cost) AS sum_units,
          COUNT(unit_cost) AS count_units, COUNT(*) AS cnt
   FROM   costs c, products p
   WHERE c.prod_id = p.prod_id
   GROUP BY prod_name, DBMS_MVIEW.PMARKER(c.rowid);

 

2.

与存储分区键不同,现在每个分区只有一个值,这极大地降低了基数。

@c:\wkdir\show_pm.sql

SELECT pmarker_costs, count(*) 
FROM   costs_pm_mv group by pmarker_costs;

 

3.

PCT 功能可用于这个物化视图。

@c:\wkdir\analyze_pm_mv.sql

TRUNCATE TABLE mv_capabilities_table;

EXEC DBMS_MVIEW.EXPLAIN_MVIEW('costs_pm_mv');

SET SERVEROUTPUT ON

BEGIN
   FOR crec IN (SELECT capability_name, possible, related_text, msgtxt 
                FROM   mv_capabilities_table ORDER BY 1) LOOP
      DBMS_OUTPUT.PUT_LINE(crec.capability_name ||': '||crec.possible);
      DBMS_OUTPUT.PUT_LINE(crec.related_text||': '||crec.msgtxt);
   END LOOP;
END;
/

 

返回主题列表

您需要在 COSTS 表上使用一些 DML 语句,这会导致物化视图变得陈旧。但是,只要不触及物化视图中的任何陈旧数据,查询重写仍可进行。为此,执行以下步骤:

1.

插入数据并显示状态。

2.

解释部分陈旧重写。

3.

刷新并显示实际数据。

返回主题列表

1. 插入数据并显示状态

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 insert_costs.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\insert_costs.sql

INSERT INTO costs VALUES (20, '02-JAN-02',999, 99, 2.50, 25.00);
INSERT INTO costs VALUES (30, '02-FEB-02',999, 99, 2, 34);
INSERT INTO costs VALUES (30, '03-MAR-02',999, 99, 2, 34);
INSERT INTO costs VALUES (40, '21-APR-02',999, 99, 1, 35);
INSERT INTO costs VALUES (40, '22-MAY-02',999, 99, 3, 36);
INSERT INTO costs VALUES (30, '22-APR-02',999, 99, 4, 37);
INSERT INTO costs VALUES (20, '12-JUN-02',999, 99, 5, 34);

COMMIT;

 

2.

关于最新更改的信息也显示在 COSTS 表的物化视图日志中。

@c:\wkdir\show_mv_log_costs.sql

SELECT count(*)
FROM   mlog$_costs;

 

3.

与预期一样,基于 COSTS 表的两个物化视图都呈现陈旧状态。

@c:\wkdir\show_status_costs_mv.sql

ALTER MATERIALIZED VIEW costs_mv COMPILE;
ALTER MATERIALIZED VIEW costs_pm_mv COMPILE;

SELECT mview_name, refresh_mode, refresh_method, staleness
FROM   user_mviews
WHERE  mview_name like 'COST%';


返回主题

2. 解释部分陈旧重写

1.

提交未触及任何陈旧数据的查询。在运行实际查询本身之前,通过使用 dbms_mview.explain_rewrite 对象来分析其重写功能。

@c:\wkdir\analyze_part_stale_rewrite.sql

TRUNCATE TABLE rewrite_table;

DECLARE
   querytxt VARCHAR2(1500) := 
   ' SELECT p.prod_name, sum(unit_cost) '||
   ' FROM   costs c, products p ' ||
   ' WHERE  c.prod_id = p.prod_id ' ||
   ' AND    c.time_id >= TO_DATE(''01-JAN-2000'',''DD-MON-YYYY'') ' ||
   ' AND    c.time_id < TO_DATE(''01-JAN-2002'',''DD-MON-YYYY'') ' ||
   ' GROUP BY prod_name';
BEGIN   
   dbms_mview.Explain_Rewrite(querytxt, NULL, 'ID1');
END;
/

Rem show final rewrite decision
SELECT message 
FROM   rewrite_table
ORDER BY sequence desc;

用部分陈旧的 costs_pm_mv 物化视图重写该查询。

 

2.

该计划将显示重写的查询。

@c:\wkdir\explain_part_stale_rewrite.sql

TRUNCATE TABLE plan_table;

EXPLAIN PLAN FOR
   SELECT /*+ rewrite (costs_pm_mv) */ p.prod_name, sum(unit_cost)
   FROM   costs c, products p
   WHERE  c.prod_id = p.prod_id
   AND    c.time_id >= TO_DATE('01-JAN-2000','DD-MON-YYYY')
   AND    c.time_id < TO_DATE('01-JAN-2002','DD-MON-YYYY')
   GROUP BY prod_name;

set linesize 132
set pagesize 999
SELECT * FROM TABLE(dbms_xplan.display);

 

3.

现在提交查询。

@c:\wkdir\run_part_stale_query.sql

SELECT p.prod_name, sum(unit_cost)
FROM   costs c, products p
WHERE  c.prod_id = p.prod_id
AND    c.time_id >= TO_DATE('01-JAN-2000','DD-MON-YYYY')
AND    c.time_id < TO_DATE('01-JAN-2002','DD-MON-YYYY')
GROUP BY prod_name;

PROMPT **** However, the MV is generically STALE !!
SELECT mview_name, refresh_mode, refresh_method, staleness 
FROM   user_mviews
WHERE  mview_name like 'COSTS%';

PROMPT **** but it only works if you exactly hit the range boundaries
EXPLAIN PLAN FOR
SELECT COUNT(*) 
FROM (SELECT p.prod_name, sum(unit_cost)
      FROM costs c, products p
      WHERE c.prod_id = p.prod_id
      AND c.time_id >= TO_DATE('01-JAN-2000','DD-MON-YYYY')
      AND c.time_id < TO_DATE('02-JAN-2002','DD-MON-YYYY')
      GROUP BY prod_name
);

set linesize 120
SELECT * FROM TABLE(dbms_xplan.display);

注意:与包含分区键的物化视图本身不同,只有在谓词条件完全匹配分区表的边界时,包含分区标记信息的部分陈旧的物化视图才可以用于重写。这一折衷是为了将基数从所有不同分区键值降低到每个分区一个值,这使得按每个分区键值进行区分成为可能。

 

返回主题

3. 刷新并显示实际数据

要刷新并显示实际数据,执行以下步骤:

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 check_sum_mvs1.sql,,或将以下 SQL 语句复制到 SQL*Plus 会话中。显示两个物化视图中所有信息的概要:

@c:\wkdir\check_sum_mvs1.sql

Rem   However, the MV is generically STALE !!
Rem   check data before 

SELECT 'Value Before Costs: ' "Value Before Costs:", 
       SUM(sum_units) 
FROM   costs_mv;

SELECT 'Value Before Cost with PM: ' "Value Before Cost with PM:", 
       SUM(sum_units) 
FROM   costs_pm_mv;

 

2.

您只更改了 COSTS 表中的数据,因此,只需刷新所有依赖于这个表的物化视图。

要只刷新相关物化视图,有一个专用刷新过程:

@c:\wkdir\refresh_costs_mv.sql

Rem Now do a fast refresh using REFRESH_DEPENDENT upon a table
DECLARE
   failures INTEGER;
BEGIN
   -- set to 555 so that we can see that it changes
   failures:= 555;
   dbms_mview.refresh_dependent ( failures, 'COSTS', 'F', '',
      FALSE,FALSE);
   DBMS_OUTPUT.put_line ( 'No of Failures: ' || failures );
END;
/

 

3.

再次检查物化视图中的概要。

@c:\wkdir\check_sum_mvs2.sql

SELECT 'Value After Costs: ', SUM(sum_units)
FROM   costs_mv;

SELECT 'Value After Cost with PM: ', SUM(sum_units)
FROM   costs_pm_mv;

4.

costs_pm_mv costs_pm 物化视图又是新的了。

@c:\wkdir\show_status_costs_mv.sql

ALTER MATERIALIZED VIEW costs_pm_mv COMPILE;
ALTER MATERIALIZED VIEW costs_mv COMPILE;

SELECT mview_name, refresh_mode, refresh_method, staleness 
FROM   user_mviews
WHERE  mview_name like 'COST%';

 

返回主题

返回主题列表

新的 TUNE_MVIEW API 给出了需要对物化视图进行的更改,以使其能够快速刷新并适用于高级查询重写技术。在本小节中,我们将检查:

使用 Tune_Mview 生成物化视图建议

要使用 Tune_Mview 生成物化视图建议,请按以下步骤进行:

1.

使用新的调整功能来获得关于您将要创建的潜在物化视图的建议。

在登录到 SH 模式的 SQL*Plus 会话中,运行 tune_mv01.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\tune_mv01.sql

Rem *** START TUNE_MVIEW section 10g
VARIABLE name_01 varchar2(30)


Rem tunemview01
EXECUTE DBMS_ADVISOR.TUNE_MVIEW -
( :name_01 -
, 'CREATE MATERIALIZED VIEW prod_mv -
REFRESH FAST WITH ROWID -
ENABLE QUERY REWRITE -
AS -
SELECT DISTINCT -
prod_name, prod_category -
FROM products' -
) ;

 

2.

查看物化视图的结果。

@c:\wkdir\mvtune_result01.sql

Rem mvtune_results01.sql
column statement format a70 word
set long 999
SELECT statement
FROM DBA_TUNE_MVIEW
WHERE task_name = :name_01
ORDER BY script_type, action_id;

请注意,DISTINCT 子句被 GROUP BY 所替代,而且 COUNT(*) 被添加到了
SELECT 子句。这使的物化视图可以进行快速更新,并可用于一般重写。

返回主题

使用 Tune_Mview 使物化视图快速刷新

要使用 Tune_Mview 使物化视图快速刷新,请按以下步骤进行:

1.

需要清除环境,以专门构建一个不能在此创建可快速刷新的物化视图的环境。运行下列脚本来清理环境。

@c:\wkdir\cleanup4tune_mv02.sql

DROP MATERIALIZED VIEW LOG ON customers;
DROP MATERIALIZED VIEW LOG ON countries;
DROP MATERIALIZED VIEW cuco_mv;


CREATE MATERIALIZED VIEW LOG ON customers
WITH SEQUENCE, ROWID INCLUDING NEW VALUES;


CREATE MATERIALIZED VIEW LOG ON countries
WITH SEQUENCE, ROWID INCLUDING NEW VALUES;

 

2.

尝试创建一个可快速更新的物化视图。尝试将失败。

@c:\wkdir\cr_fr_mv.sql
Rem fails - not fast refreshable
CREATE MATERIALIZED VIEW cuco_mv
  REFRESH FAST
  ENABLE QUERY REWRITE
  AS
    SELECT cu.cust_last_name
          , co.country_name
     FROM customers cu
          , countries co
    WHERE cu.country_id = co.country_id;




3.

使用 SQL Access Advisor 的调整功能来修复上面显示的错误。在登录到 SH 模式的 SQL*Plus 会话中,运行 tune_mv02.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\tune_mv02.sql

VARIABLE name_02 varchar2(30)
Rem tunemview02.sql
EXECUTE DBMS_ADVISOR.TUNE_MVIEW -
( :name_02 -
  , 'CREATE MATERIALIZED VIEW cuco_mv -
     REFRESH FAST -
     ENABLE QUERY REWRITE -
     AS -
       SELECT cu.cust_last_name -
             , co.country_name -
       FROM customers cu -
           , countries co -
       WHERE cu.country_id = co.country_id' -
) ;

与用于第一个示例的建议一样,SQL Access Advisor 的建议存储在数据库中。

column statement format a70 word
set long 999


SELECT script_type as type, statement
FROM   DBA_TUNE_MVIEW
WHERE  task_name = :name_02
ORDER BY script_type, action_id; 


4.

与其他 SQL Access Advisor 功能相同,您可以生成现成的 SQL 脚本,以用于必要操作。运行脚本 tunemv_script.sql

@c:\wkdir\tunemv_script.sql

CREATE DIRECTORY advisor_results AS 'c:\wkdir';

 EXECUTE DBMS_ADVISOR.CREATE_FILE -
( dbms_advisor.get_task_script(:name_02) -
, location => 'ADVISOR_RESULTS' -
, filename => 'advisor_tune_mv_script.sql' -
) ;
      

您生成了名为 advisor_tune_mv_script.sql 的脚本,该脚本存放在 /tmp 目录中。
它是为 Linux 系统设置的。ADVISOR_RESULTS 目录对象必须存在并指向系统上的有效位置。

如果这个目录不存在,您将收到一个 Oracle 错误消息。或创建一个具有相应权限的目录对象,或更改代码中的目录路径,使其指向系统上的现有目录。


返回主题

返回主题列表

重置环境

您在环境中做了很多修改,包括删除了一些维。要重置环境,以免影响任何其他教程,请执行以下步骤:

1.

在登录到 SH 模式的 SQL*Plus 会话中,运行 cleanup_mod4.sql,或将以下 SQL 语句复制到 SQL*Plus 会话中:

@c:\wkdir\cleanup_mod4.sql

DROP MATERIALIZED VIEW LOG ON sales;
DROP MATERIALIZED VIEW LOG ON customers;
DROP MATERIALIZED VIEW LOG ON products;
DROP MATERIALIZED VIEW LOG ON costs;

ALTER TABLE costs DROP PARTITION costs_1_2002;

CREATE DIMENSION products_dim
  LEVEL product IS (products.prod_id)
  LEVEL subcategory IS (products.prod_subcategory)
  LEVEL category IS (products.prod_category)
  HIERARCHY prod_rollup (
  product CHILD OF
  subcategory CHILD OF
  category
  )
  ATTRIBUTE product DETERMINES
  (products.prod_name, products.prod_desc,
  prod_weight_class, prod_unit_of_measure,
  prod_pack_size,prod_status, prod_list_price, prod_min_price)
  ATTRIBUTE subcategory DETERMINES
  (prod_subcategory, prod_subcategory_desc)
  ATTRIBUTE category DETERMINES
  (prod_category, prod_category_desc)
;

CREATE DIMENSION promotions_dim
  LEVEL promo IS (promotions.promo_id) 
  LEVEL subcategory IS (promotions.promo_subcategory)
  LEVEL category IS (promotions.promo_category)
  HIERARCHY promo_rollup (
  promo CHILD OF
  subcategory CHILD OF
  category
)
ATTRIBUTE promo DETERMINES
  (promo_name, promo_cost,
  promo_begin_date, promo_end_date)
  ATTRIBUTE subcategory DETERMINES (promo_subcategory)
  ATTRIBUTE category DETERMINES (promo_category)
;

CREATE DIMENSION channels_dim
  LEVEL channel IS (channels.channel_id)
  LEVEL channel_class IS (channels.channel_class)
  HIERARCHY channel_rollup (
  channel CHILD OF
  channel_class
  )
  ATTRIBUTE channel DETERMINES (channel_desc)
  ATTRIBUTE channel_class DETERMINES (channel_class)
;

COMMIT;

 

返回主题

返回主题列表

在本教程中,您学习了如何:

启用查询重写
用多种方法执行物化视图刷新和重写
使用分区和物化视图
使用 TUNE_MVIEW 使物化视图快速刷新

返回主题列表

 

你可能感兴趣的:(使用物化视图和查询重写功能)