在本教程中,您将了解如何利用强大的物化视图和查询重写功能。
大约 2 个小时
本教程包括下列主题:
概述 | ||
情景 | ||
前提条件 | ||
为销售历史模式实施模式更改 | ||
启用查询重写 | ||
分析物化视图更新和重写功能 |
||
Oracle 的查询重写功能 | ||
带有 GROUP BY 扩展项的增强的重写功能 | ||
使用重写或错误来控制语句的执行 | ||
分区与物化视图 | ||
使用带有 PMARKER 信息的物化视图 | ||
使用查询重写和部分陈旧的物化视图 | ||
使用 TUNE_MVIEW 使物化视图快速刷新 | ||
重置环境 | ||
总结 |
将鼠标置于此图标上以加载和查看本教程的所有屏幕截图。(警告:因为此操作会同时加载所有屏幕截图,所以网速较慢时,响应时间可能会比较长。)
注意:此外,您还可以在下列步骤中将鼠标放在每个单独的图标上,从而仅加载和查看与该步骤相关的屏幕截图。您可以单击单个屏幕截图将其隐藏。
通过使用概要管理 (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 操作全面启用快速刷新功能。 系统建议的聚合函数是:
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; /
|
返回主题列表
优化程序使用许多不同方法来重写查询。第一个,也是最重要的步骤,是确定查询所请求的全部或部分结果是否可以从存储在物化视图的预计算结果中获得。
最简单的情况是,存储在物化视图中的结果与查询所请求的结果完全匹配。Oracle 优化程序通过将查询的文本与物化视图定义的文本相比较来作出这种类型的决定。这种方法是最简单的,但符合这种查询重写类型的查询的数量最少。
当文本比较测试失败后,Oracle 优化程序将基于联接、选择、分组、聚合以及抓取的列数据执行一系列通用检查。这是通过将查询的不同子句(如 SELECT、FROM、WHERE、HAVING 或 GROUP BY)与物化视图的不同子句分别进行比较而完成的。
使用部分文本匹配重写 | ||
在预先建立的表上创建物化视图 | ||
使用简单的联回 (Join Back) 重写 | ||
分析重写过程 | ||
使用联回和卷积重写 | ||
使用复杂的联回和卷积重写 | ||
在数据的子集上创建物化视图 | ||
使用多物化视图重写 | ||
估计物化图的大小 | ||
分析重写过程 | ||
用联回和聚合卷积重写 |
返回主题列表
最简单的重写机制是文本匹配重写。在全文本匹配中,查询的整个文本会与物化视图定义的整个文本(即整个 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 的联接进行完整扫描。 注意:由于时间限制,不运行此查询。
|
返回主题
在数据仓库环境中,有已经创建好的概要表或聚合表是很普遍的。您无须通过创建新的物化视图来重复此工作。这个解决方案将改善物化视图的性能,但是,它不能:
为所有 SQL 应用程序提供透明查询重写 | ||
在一个应用程序中透明地访问在另一个应用程序中定义的物化视图 | ||
普遍支持快速并行或快速物化视图刷新 |
由于这些限制,而且由于现有的物化视图可能极为巨大且重建起来费用高昂,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 优化程序将基于联接、选择、分组、聚合以及抓取的列数据执行一系列通用检查。这是通过将查询的不同子句( SELECT、FROM、WHERE、HAVING 或 GROUP 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 维定义:
所请求的 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 的维定义摘录。它显示了这个查询的重要部分:
Oracle 数据库必须确定,它是否可以基于存储在该物化视图中的信息导出所有被请求的属性。查询中正在请求 countries.country_name 和 customers.cust_state_province,而该物化视图只包含 cust_id 信息。 根据 customers_dim 维中的信息,Oracle 数据库可以确定:
Oracle 数据库通过所有这些信息,不但将物化视图与 customers 表联接,而且还将 customers 与 countries 联接,以获得查询的结果,并保证查询结果正确。
|
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);
|
返回主题
返回主题列表
通过以 GROUPING SETS、ROLLUP 及其连接的形式对 GROUP BY 子句进行扩展,您可以有选择地在查询的 GROUP BY 子句中指定所需的分组。
带有扩展 GROUP BY 的物化视图必须满足两个附加条件后,方可用于重写:
它必须包含分组区分符 (Grouping Distinguisher) — 所有 GROUP BY 表达式上的 GROUPING_ID 函数。例如,如果物化视图的 GROUP BY 子句是 GROUP BY CUBE(a, b),那么 SELECT 列表应该包含 GROUPING_ID(a, b)。 | ||
物化视图的 GROUP BY 子句不应导致任何重复分组。例如,GROUP BY GROUPING SETS ((a, b), (a, b)) 将使物化视图不能进行通用重写。 |
带有扩展 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 SETS。GROUPING 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),因为这是将行映射到陈旧分区最简单的方法。在使用部分陈旧的物化视图时,要点是:
如果用于回答查询的来自物化视图的行已知是 FRESH,那么,查询重写可以通过 ENFORCED 或 TRUSTED 模式使用物化视图。 | ||
物化视图中的新行是通过将选择谓词添加到物化视图的 WHERE 子句来识别的。如果一个查询的回答是包含在这个(受限制的)物化视图内的,那么,可用这个物化视图将其重写。请注意,支持带有选择谓词的物化视图是这种重写类型的先决条件。 |
在数据仓库中,对从表的更改通常会招致分区维护操作,如 DROP、EXCHANGE、MERGE 及 ADD 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%'
|
返回主题
要将一些数据添加到第二个事实表 (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%';
|
返回主题
您已经看到,物化视图通常是陈旧的。但是,只要陈旧数据未被触及,它就可以被用于重写。很快您就会有所尝试。此外,分区变化跟踪使您能够快速刷新 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_2002 和 costs_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. | 在登录到 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%';
|
返回主题
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); 注意:与包含分区键的物化视图本身不同,只有在谓词条件完全匹配分区表的边界时,包含分区标记信息的部分陈旧的物化视图才可以用于重写。这一折衷是为了将基数从所有不同分区键值降低到每个分区一个值,这使得按每个分区键值进行区分成为可能。
|
返回主题
要刷新并显示实际数据,执行以下步骤:
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 使物化视图快速刷新 |
||
要使用 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(*) 被添加到了 |
返回主题
要使用 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 目录中。 如果这个目录不存在,您将收到一个 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 使物化视图快速刷新 |
返回主题列表