Oracle 10g中的不需要的查询合并

时间:2014-07-22 06:35:32

标签: sql oracle

我正在使用Oracle数据库10g 10.2.0.5.0版。我有这样的看法:     

CREATE OR REPLACE VIEW some_view
(
    A,
    B
)
AS
    SELECT A, B
    FROM table_a
    WHERE condition_a
UNION ALL
    SELECT A, B
    FROM table_b
    WHERE condition_b;

和一些数据库函数some_db_package.foo()。我的问题是当我执行查询时:

SELECT A, some_db_package.foo(B) val
FROM some_view
WHERE some_db_package.foo(B) = 0;

Oracle正在从查询和some_view合并条件,所以我得到的结果是:

SELECT A, some_db_package.foo(B) val
FROM table_a
WHERE some_db_package.foo(B) = 0 AND condition_a
UNION ALL
SELECT A, some_db_package.foo(B) val
FROM table_b
WHERE some_db_package.foo(B) = 0 AND condition_b;

some_db_package.foo()table_atable_b的所有行上执行,我希望仅在过滤后执行some_db_package.foo()condition_a和{{1}行)。假设我不能在查询中使用优化器提示,有没有办法做到这一点(即通过更改sql查询或condition_b定义)?


问题解决了。总结一下:

  1. some_view - 对于给定的事件和日期范围计算事件在日期(foo()访问表之间发生的错误),因此仅在some_db_package.foo()时才是确定性的。

    < / LI>
  2. sysdate > dateTo没有任何区别。

  3. 实际上我不需要select * from ( SELECT A, some_db_package.foo(B) val FROM some_view )而我使用UNION ALL进行测试,但结果相同。

  4. UNION没有任何区别。

  5. 我使用优化器提示进行测试,不幸的是Oracle忽略了它们。

  6. with some_view_set as (select A, B from some_view) select * from ( select A, some_db_package.foo(B) val from some_view_set ) where val = 0中使用ROWNUM >= 1是解决我问题的方法。

  7. 感谢您的帮助,我真的很感激。

1 个答案:

答案 0 :(得分:2)

ROWNUM通常是停止优化程序转换的最佳方法。提示很难正确 - 语法很奇怪且有问题,并且有许多潜在的转换需要停止。还有其他方法可以重新编写查询,但ROWNUM通常是最好的方法,因为它是以这种方式记录的。 ROWNUM必须评估最后用于前N个查询,您始终可以依赖它来防止合并查询块。

示例架构

drop table table_a;
drop table table_b;

create table table_a(a number, b number);
create table table_b(a number, b number);

insert into table_a select level, level from dual connect by level <= 10;
insert into table_b select level, level from dual connect by level <= 10;

begin
    dbms_stats.gather_table_stats(user, 'table_a');
    dbms_stats.gather_table_stats(user, 'table_b');
end;
/

--FOO takes 1 second each time it is executed.
create or replace function foo(p_value number) return number is
begin
    dbms_lock.sleep(1);
    return 0;
end;
/

--BAR is fast, but the optimizer doesn't know it.
create or replace function bar(p_value number) return number is
begin
    return p_value;
end;
/

--This view returns 2 rows.
CREATE OR REPLACE VIEW some_view AS
    SELECT A, B
    FROM table_a
    WHERE a = bar(1)
UNION ALL
    SELECT A, B
    FROM table_b
    WHERE a = bar(2);

查询缓慢

此查询需要20秒才能运行,这意味着该功能将被评估20次。

SELECT A, foo(B) val
FROM some_view
WHERE foo(B) = 0;

解释计划显示条件已合并,并且似乎从左到右评估条件(但不依赖于此始终为真!)。

explain plan for
SELECT A, foo(B) val
FROM some_view
WHERE foo(B) = 0;

select * from table(dbms_xplan.display);


Plan hash value: 4139878329

---------------------------------------------------------------------------------
| Id  | Operation           | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |           |     1 |     6 |     5   (0)| 00:00:01 |
|   1 |  VIEW               | SOME_VIEW |     1 |     6 |     5   (0)| 00:00:01 |
|   2 |   UNION-ALL         |           |       |       |            |          |
|*  3 |    TABLE ACCESS FULL| TABLE_A   |     1 |     6 |     3   (0)| 00:00:01 |
|*  4 |    TABLE ACCESS FULL| TABLE_B   |     1 |     6 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - filter("FOO"("B")=0 AND "A"="BAR"(1))
   4 - filter("FOO"("B")=0 AND "A"="BAR"(2))

Note
-----
   - automatic DOP: skipped because of IO calibrate statistics are missing

快速查询

添加一个看似多余的ROWNUM谓词,除了阻止转换之外什么都不做。

CREATE OR REPLACE VIEW some_view2 AS
    SELECT A, B
    FROM table_a
    WHERE a = bar(1)
    AND ROWNUM >= 1 --Prevent optimizer transformations, for performance.
UNION ALL
    SELECT A, B
    FROM table_b
    WHERE a = bar(2)
    AND ROWNUM >= 1 --Prevent optimizer transformations, for performance.
;

现在查询只需要4秒,该功能只运行4次。

SELECT A, foo(B) val
FROM some_view2
WHERE foo(B) = 0;

在新的解释计划中,很明显,在大部分过滤完成后,FOO函数会被最后评估。

explain plan for
SELECT A, foo(B) val
FROM some_view2
WHERE foo(B) = 0;

select * from table(dbms_xplan.display);


Plan hash value: 4228269064

------------------------------------------------------------------------------------
| Id  | Operation             | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |            |     2 |    52 |     6   (0)| 00:00:01 |
|*  1 |  VIEW                 | SOME_VIEW2 |     2 |    52 |     6   (0)| 00:00:01 |
|   2 |   UNION-ALL           |            |       |       |            |          |
|   3 |    COUNT              |            |       |       |            |          |
|*  4 |     FILTER            |            |       |       |            |          |
|*  5 |      TABLE ACCESS FULL| TABLE_A    |     1 |     6 |     3   (0)| 00:00:01 |
|   6 |    COUNT              |            |       |       |            |          |
|*  7 |     FILTER            |            |       |       |            |          |
|*  8 |      TABLE ACCESS FULL| TABLE_B    |     1 |     6 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("FOO"("B")=0)
   4 - filter(ROWNUM>=1)
   5 - filter("A"="BAR"(1))
   7 - filter(ROWNUM>=1)
   8 - filter("A"="BAR"(2))

Note
-----
   - automatic DOP: skipped because of IO calibrate statistics are missing

Ben想要创建函数DETERMINISTIC的想法也可能有助于减少函数调用。