但其实这两种写法在语义上差别很大,结果集也可能不相同,如下:
- SQL> select id from test2 where id3 = 10 start with id = 3 connect by nocycle prior id2 = id;
-
- ID
- ----------
- 8
-
- Elapsed: 00:00:00.13
-
- SQL> select id from test2 start with id = 3 connect by nocycle prior id2 = id and id3=10;
-
- ID
- ----------
- 3
-
- Elapsed: 00:00:00.00
二、CBO估算不准确
层次查询的SQL语句频繁出现的问题,就是CBO估算返回结果集偏差,引起执行计划不准确。虽然表上收集过统计信息,但是CBO对于结果集的估算跟实际值偏差非常大(几百上千的倍的差距),但是这个也不能全怪CBO,毕竟递归查询有多少层、有多少数据要裁剪,结合起来考虑,结果确实难以估量。


对于CBO估算不准的问题,我们考虑了对结果集相对特殊的参数,在SQL文本上做区分,应用识别特殊参数运行带hint地改造SQL,通过hint来指定返回结果集。这种情况不同于普通的数据倾斜,无法通过baseline给出一个不涉及应用改造的方案。

三、并行处理
层次查询的SQL直接使用parallel的hint,会遭遇并行串行化的问题,也就是不能真正并行。对于一些重要且耗时长的层次查询,可以考虑PIPELINED TABLE FUNCTION改写SQL的方式来实现。
以下脚本测试参考了陈焕生童鞋的blog以及oracle相关文档(Doc ID 2168864.1):
- drop table t1;
- -- t1 with 100,000 rows
- create table t1
- as
- select
- rownum id,
- lpad(rownum, 10, '0') v1,
- trunc((rownum - 1)/100) n1,
- rpad(rownum, 100) padding
- from
- dual
- connect by level <= 100000
- ;
-
- begin
- dbms_stats.gather_table_stats(user,'T1');
- end;
- /
-
- select /*+ monitor */
- count(*)
- from
- (
- select
- CONNECT_BY_ROOT ltrim(id) root_id,
- CONNECT_BY_ISLEAF is_leaf,
- level as t1_level,
- a.v1
- from t1 a
- start with a.id <=1000
- connect by NOCYCLE id = prior id + 1000
- );
-
- create or replace package refcur_pkg
- AS
- TYPE R_REC IS RECORD (row_id ROWID);
- TYPE refcur_t IS REF CURSOR RETURN R_REC;
- END;
- /
-
- create or replace package connect_by_parallel
- as
- /* Naviagates a shallow hiearchy in parallel, where we do a tree walk for each root */
-
- CURSOR C1 (p_rowid ROWID) IS -- Cursor done for each subtree. This select is provided by the customer
- select CONNECT_BY_ROOT ltrim(id) root_id, CONNECT_BY_ISLEAF is_leaf, level as t1_level, a.v1
- from t1 a
- start with rowid = p_rowid
- connect by NOCYCLE id = prior id + 1000;
-
- TYPE T1_TAB is TABLE OF C1%ROWTYPE;
-
- FUNCTION treeWalk (p_ref refcur_pkg.refcur_t) RETURN T1_TAB
- PIPELINED
- PARALLEL_ENABLE(PARTITION p_ref BY ANY);
-
- END connect_by_parallel;
- /
-
- create or replace package body connect_by_parallel
- as
- FUNCTION treeWalk (p_ref refcur_pkg.refcur_t) RETURN T1_TAB
- PIPELINED PARALLEL_ENABLE(PARTITION p_ref BY ANY)
- IS
- in_rec p_ref%ROWTYPE;
- BEGIN
- execute immediate 'alter session set "_old_connect_by_enabled"=true';
- LOOP -- for each root
- FETCH p_ref INTO in_rec;
- EXIT WHEN p_ref%NOTFOUND;
- FOR c1rec IN c1(in_rec.row_id) LOOP -- retrieve rows of subtree
- PIPE ROW(c1rec);
- END LOOP;
- END LOOP;
- execute immediate 'alter session set "_old_connect_by_enabled"=false';
- RETURN;
- END treeWalk;
-
- END connect_by_parallel;
- /
-
- SELECT
- /*+ monitor */
- COUNT(*)
- FROM TABLE(connect_by_parallel.treeWalk (CURSOR
- (SELECT /*+ parallel (a 100) */
- rowid FROM t1 a WHERE id <= 100))) b;
(编辑:晋中站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|