如何在Oracle中为物化视图设置ALL_ROWS?
How to set ALL_ROWS for a materialized view in Oracle?
如何确保使用 optimizer_mode = ALL_ROWS
刷新实体化视图?
背景:我正在将 mview 从 ALL_ROWS 数据库迁移到 FIRST_ROWS 数据库并且不想丢失设置,因为 [= 刷新需要更长的数量级54=] / 嵌套循环与 ALL_ROWS / 散列连接相比。
mview 建立在视图之上,并且在 PL/SQL 过程中刷新了几个相似的 mview。
我尝试了一些最小的例子,看起来优先级是
- 在物化视图中提示
/*+ ALL_ROWS */
- 如果未设置,则会在视图中观察到提示
- 如果mview和view都没有这样的提示,则观察session设置
这是正确的吗?
我已经在不带提示的情况下尝试了这些视图:
CREATE OR REPLACE VIEW v_def AS SELECT /*+ */ * FROM all_objects;
CREATE OR REPLACE VIEW v_all AS SELECT /*+ ALL_ROWS */ * FROM all_objects;
CREATE OR REPLACE VIEW v_first AS SELECT /*+ FIRST_ROWS */ * FROM all_objects;
还有五个不带提示的 mviews:
CREATE MATERIALIZED VIEW mv_def BUILD DEFERRED AS SELECT /*+ */ * FROM v_def;
CREATE MATERIALIZED VIEW mv_all BUILD DEFERRED AS SELECT /*+ */ * FROM v_all;
CREATE MATERIALIZED VIEW mv_first BUILD DEFERRED AS SELECT /*+ */ * FROM v_first;
CREATE MATERIALIZED VIEW mv_all_first BUILD DEFERRED AS SELECT /*+ ALL_ROWS */ * FROM v_first;
CREATE MATERIALIZED VIEW mv_first_all BUILD DEFERRED AS SELECT /*+ FIRST_ROWS */ * FROM v_all;
当我使用程序刷新 mviews 时...
CREATE OR REPLACE PROCEDURE p_def IS
BEGIN
dbms_mview.refresh('mv_def', atomic_refresh=>FALSE);
dbms_mview.refresh('mv_all', atomic_refresh=>FALSE);
dbms_mview.refresh('mv_first', atomic_refresh=>FALSE);
dbms_mview.refresh('mv_all_first', atomic_refresh=>FALSE);
dbms_mview.refresh('mv_first_all', atomic_refresh=>FALSE);
END;
/
CREATE OR REPLACE PROCEDURE p_all IS
BEGIN
EXECUTE IMMEDIATE 'ALTER SESSION SET optimizer_mode = ALL_ROWS';
p_def;
END;
/
CREATE OR REPLACE PROCEDURE p_first IS
BEGIN
EXECUTE IMMEDIATE 'ALTER SESSION SET optimizer_mode = FIRST_ROWS';
p_def;
END;
/
...我得到以下结果:
mview p_dev p_all p_first
------------- ---------- ---------- ----------
MV_DEF first_rows all_rows first_rows
MV_ALL all_rows all_rows all_rows
MV_FIRST first_rows first_rows first_rows
MV_ALL_FIRST all_rows all_rows all_rows
MV_FIRST_ALL first_rows first_rows first_rows
optimizer_mode的设置来自查询:
SELECT e.value as optimizer_mode, c.sql_id, substr(c.sql_text,1,100) as sql
FROM v$sql c
LEFT JOIN v$sql_optimizer_env e
ON e.sql_id = c.sql_id ANd e.name = 'optimizer_mode'
WHERE regexp_like(c.sql_text, 'BYPASS.*(v_def|v_all|v_first)');
所以,我需要保护 mviews 不受数据库设置 FIRST_ROWS
的影响,对吧?
我可以在使用 ALTER SESSION
语句刷新 mviews 的 PL/SQL 过程中执行此操作,希望没有其他人会直接刷新 mviews。或者我更改 mviews 的查询,添加提示 /*+ ALL_ROWS */
,对吗?
你是对的,优化器设置的优先顺序是:
- 系统参数
- 会话设置
- 查询块级提示
- 语句级或父查询块级提示
如果在最外层查询中设置了 /*+ ALL ROWS*/
提示,该提示将覆盖其他提示和设置。
我们如何证明优先规则是正确的?
虽然我在官方文档中找不到对上述规则的明确引用,但大多数规则都是相当明显的。前三个规则是有道理的,我们之前可能都见过它们的实际应用。在较高级别设置和应用配置,然后选择性地在较低级别覆盖配置是有意义的:首先针对整个系统,然后针对特定会话,最后针对单个查询。
不寻常的优先级是最后一个,高级语句提示覆盖低级查询块提示。幸运的是,我们可以使用 19c 提示报告来证明这个规则是正确的。
简单提示测试
以下测试用例显示 FIRST_ROWS
提示正在使用并显示在执行计划的“提示报告”部分。
--drop table test_table;
create table test_table(a number);
explain plan for select /*+ first_rows */ * from test_table;
select * from table(dbms_xplan.display(format => 'basic +hint_report'));
Plan hash value: 3979868219
----------------------------------------
| Id | Operation | Name |
----------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS FULL| TEST_TABLE |
----------------------------------------
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1
---------------------------------------------------------------------------
0 - STATEMENT
- first_rows
父提示覆盖子提示
虽然您已经创建了一个测试用例,其中父查询块中的 ALL_ROWS
提示覆盖了子块中的 FIRST_ROWS
提示,但以下测试用例清楚地表明该行为不只是意外。 “提示报告”非常清楚地解释了“first_rows / 提示被父查询块中的另一个覆盖”。
explain plan for select /*+ all_rows */ * from (select /*+ first_rows */ * from test_table);
select * from table(dbms_xplan.display(format => 'basic +hint_report'));
Plan hash value: 3979868219
----------------------------------------
| Id | Operation | Name |
----------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS FULL| TEST_TABLE |
----------------------------------------
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 2 (U - Unused (1))
---------------------------------------------------------------------------
0 - STATEMENT
U - first_rows / hint overridden by another in parent query block
- all_rows
虽然这个答案不是行为的明确证据,但我相信它足以让我们相信在最外层查询中使用 ALL_ROWS
提示将始终有效。
如何确保使用 optimizer_mode = ALL_ROWS
刷新实体化视图?
背景:我正在将 mview 从 ALL_ROWS 数据库迁移到 FIRST_ROWS 数据库并且不想丢失设置,因为 [= 刷新需要更长的数量级54=] / 嵌套循环与 ALL_ROWS / 散列连接相比。
mview 建立在视图之上,并且在 PL/SQL 过程中刷新了几个相似的 mview。
我尝试了一些最小的例子,看起来优先级是
- 在物化视图中提示
/*+ ALL_ROWS */
- 如果未设置,则会在视图中观察到提示
- 如果mview和view都没有这样的提示,则观察session设置
这是正确的吗?
我已经在不带提示的情况下尝试了这些视图:
CREATE OR REPLACE VIEW v_def AS SELECT /*+ */ * FROM all_objects;
CREATE OR REPLACE VIEW v_all AS SELECT /*+ ALL_ROWS */ * FROM all_objects;
CREATE OR REPLACE VIEW v_first AS SELECT /*+ FIRST_ROWS */ * FROM all_objects;
还有五个不带提示的 mviews:
CREATE MATERIALIZED VIEW mv_def BUILD DEFERRED AS SELECT /*+ */ * FROM v_def;
CREATE MATERIALIZED VIEW mv_all BUILD DEFERRED AS SELECT /*+ */ * FROM v_all;
CREATE MATERIALIZED VIEW mv_first BUILD DEFERRED AS SELECT /*+ */ * FROM v_first;
CREATE MATERIALIZED VIEW mv_all_first BUILD DEFERRED AS SELECT /*+ ALL_ROWS */ * FROM v_first;
CREATE MATERIALIZED VIEW mv_first_all BUILD DEFERRED AS SELECT /*+ FIRST_ROWS */ * FROM v_all;
当我使用程序刷新 mviews 时...
CREATE OR REPLACE PROCEDURE p_def IS
BEGIN
dbms_mview.refresh('mv_def', atomic_refresh=>FALSE);
dbms_mview.refresh('mv_all', atomic_refresh=>FALSE);
dbms_mview.refresh('mv_first', atomic_refresh=>FALSE);
dbms_mview.refresh('mv_all_first', atomic_refresh=>FALSE);
dbms_mview.refresh('mv_first_all', atomic_refresh=>FALSE);
END;
/
CREATE OR REPLACE PROCEDURE p_all IS
BEGIN
EXECUTE IMMEDIATE 'ALTER SESSION SET optimizer_mode = ALL_ROWS';
p_def;
END;
/
CREATE OR REPLACE PROCEDURE p_first IS
BEGIN
EXECUTE IMMEDIATE 'ALTER SESSION SET optimizer_mode = FIRST_ROWS';
p_def;
END;
/
...我得到以下结果:
mview p_dev p_all p_first
------------- ---------- ---------- ----------
MV_DEF first_rows all_rows first_rows
MV_ALL all_rows all_rows all_rows
MV_FIRST first_rows first_rows first_rows
MV_ALL_FIRST all_rows all_rows all_rows
MV_FIRST_ALL first_rows first_rows first_rows
optimizer_mode的设置来自查询:
SELECT e.value as optimizer_mode, c.sql_id, substr(c.sql_text,1,100) as sql
FROM v$sql c
LEFT JOIN v$sql_optimizer_env e
ON e.sql_id = c.sql_id ANd e.name = 'optimizer_mode'
WHERE regexp_like(c.sql_text, 'BYPASS.*(v_def|v_all|v_first)');
所以,我需要保护 mviews 不受数据库设置 FIRST_ROWS
的影响,对吧?
我可以在使用 ALTER SESSION
语句刷新 mviews 的 PL/SQL 过程中执行此操作,希望没有其他人会直接刷新 mviews。或者我更改 mviews 的查询,添加提示 /*+ ALL_ROWS */
,对吗?
你是对的,优化器设置的优先顺序是:
- 系统参数
- 会话设置
- 查询块级提示
- 语句级或父查询块级提示
如果在最外层查询中设置了 /*+ ALL ROWS*/
提示,该提示将覆盖其他提示和设置。
我们如何证明优先规则是正确的?
虽然我在官方文档中找不到对上述规则的明确引用,但大多数规则都是相当明显的。前三个规则是有道理的,我们之前可能都见过它们的实际应用。在较高级别设置和应用配置,然后选择性地在较低级别覆盖配置是有意义的:首先针对整个系统,然后针对特定会话,最后针对单个查询。
不寻常的优先级是最后一个,高级语句提示覆盖低级查询块提示。幸运的是,我们可以使用 19c 提示报告来证明这个规则是正确的。
简单提示测试
以下测试用例显示 FIRST_ROWS
提示正在使用并显示在执行计划的“提示报告”部分。
--drop table test_table;
create table test_table(a number);
explain plan for select /*+ first_rows */ * from test_table;
select * from table(dbms_xplan.display(format => 'basic +hint_report'));
Plan hash value: 3979868219
----------------------------------------
| Id | Operation | Name |
----------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS FULL| TEST_TABLE |
----------------------------------------
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1
---------------------------------------------------------------------------
0 - STATEMENT
- first_rows
父提示覆盖子提示
虽然您已经创建了一个测试用例,其中父查询块中的 ALL_ROWS
提示覆盖了子块中的 FIRST_ROWS
提示,但以下测试用例清楚地表明该行为不只是意外。 “提示报告”非常清楚地解释了“first_rows / 提示被父查询块中的另一个覆盖”。
explain plan for select /*+ all_rows */ * from (select /*+ first_rows */ * from test_table);
select * from table(dbms_xplan.display(format => 'basic +hint_report'));
Plan hash value: 3979868219
----------------------------------------
| Id | Operation | Name |
----------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS FULL| TEST_TABLE |
----------------------------------------
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 2 (U - Unused (1))
---------------------------------------------------------------------------
0 - STATEMENT
U - first_rows / hint overridden by another in parent query block
- all_rows
虽然这个答案不是行为的明确证据,但我相信它足以让我们相信在最外层查询中使用 ALL_ROWS
提示将始终有效。