How can I make sure that a materialized view is refreshed using the optimizer_mode = ALL_ROWS?
Background: I'm migrating an mview from an ALL_ROWS database to a FIRST_ROWS database and don't want to lose the setting, as the refresh takes orders of magnitude longer with FIRST_ROWS / nested loops compared to ALL_ROWS / hash joins.
The mview is build on top of a view, and a couple of similar mviews are refreshed in a PL/SQL procedure.
I've tried out some minimal examples, it looks like the precedence is
- Hint
/*+ ALL_ROWS */in materialized view - If that is not set, a hint in the view is observed
- If neither mview nor view have such a hint, the session setting is observed
Is this correct?
I've tried out thee views without and with hints:
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;
And five mviews without and with hints:
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;
When I refresh the mviews with a procedure ...
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;
/
... I get the following results:
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
The setting of optimizer_mode came from the query:
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)');
So, I need to protect the mviews from the database setting FIRST_ROWS, right?
I can do this either in the PL/SQL procedure that refreshes the mviews with an ALTER SESSION statement, hoping that nobody else will ever refresh the mviews directly. Or I change the queries of the mviews, adding a hint /*+ ALL_ROWS */, right?