使用类型时的Oracle CBO

cos*_*van 5 oracle oracle11g

我对Oracle 11g R2上的CBO有一个有趣的问题,我似乎无法解决.假设如下(注意这是为了简洁而减少DDL);

create or replace object o_primary_key ( id number(38,0) );
create or replace type t_primary_key table of o_primary_key;

create table customer (
  customer_id number(38, 0) primary key,
  parent_customer_id number(38, 0),
  ... additional columns ommitted ...
)

create table orders (
  order_id number(38, 0) not null primary key,
  customer_id number(38, 0) not null,
  date_applied date not null,
  deleted_on date null,
  amount number(18, 9) not null,
  ... additional columns ommitted ...
)

index idx_orders_1 on orders (customer_id, date_applied, deleted_on, order_id);

index idx_customers_1 on customer (parent_customer_id, case when parent_customer_id is not null then customer_id else null end) compute statistics;
Run Code Online (Sandbox Code Playgroud)

CUSTOMERS表有大约200,000行,ORDERS表有大约600万行.我有一个查询,如下所示,此查询的目的是显示给定客户ID的所有订单,或者如果客户ID是父项,那么其子项的所有订单.

with c_ids as (
  select 1 as id from dual union all
  select 2 as id from dual union all
  select 3 as id from dual
),
c_customer_ids as (
  select t.id customer_id,
         c.parent_customer_id joining_customer_id
  from   c_ids t
  join   customers c
  on     t.id = c.customer_id
  or     t.id = c.parent_customer_id
)
select   *
from     c_customer_ids
join     orders o
on       o.customer_id = c.joining_customer_id
where    o.date_applied bewteen '10/MAY/15' and '13/MAY/15'
and      o.deleted_on is null
order by o.customer_id,
         o.date_applied;
Run Code Online (Sandbox Code Playgroud)

当我运行这个查询时,CBO正确地(我相信)决定使用CONCATENATION操作重写查询的OR部分,使得它部分地针对CUSTOMER_ID运行,然后针对PARENT_CUSTOMER_ID运行作为第二次运行.这两个使用查询都使用索引,并且一致获取的数量非常低(大约200).

请注意,在我测试的情况下,我将从总计600万行中抽出大约1,800行.

但是,当我运行这样的查询时,它会变得有趣;

with c_customer_ids as (
  select t.id customer_id,
         c.parent_customer_id joining_customer_id
  from   table(t_primary_key(o_primary_key(1), o_primary_key(2), o_primary_key(3))) t
  join   customers c
  on     t.id = c.customer_id
  or     t.id = c.parent_customer_id
)
select   *
from     c_customer_ids
join     orders o
on       o.customer_id = c.joining_customer_id
where    o.date_applied bewteen '10/MAY/15' and '13/MAY/15'
and      o.deleted_on is null
order by o.customer_id,
         o.date_applied;
Run Code Online (Sandbox Code Playgroud)

当使用类型来定义数字时,CBO决定错误地(我相信)使用ORDERS表上的全表扫描将其作为单个查询运行,因此consisent获得大约130,000并且查询运行得更慢.

为什么使用这种类型会造成这样的差异?

谢谢,该隐