【发布时间】:2017-03-22 06:35:30
【问题描述】:
我能够让联接消除适用于简单的情况,例如一对一关系,但不适用于稍微复杂的情况。 最终我想尝试锚建模,但首先我需要找到解决这个问题的方法。我正在使用 Oracle 12c Enterprise Edition Release 12.1.0.2.0。
我的测试用例的 DDL:
drop view product_5nf;
drop table product_color cascade constraints;
drop table product_price cascade constraints;
drop table product cascade constraints;
create table product(
product_id number not null
,constraint product_pk primary key(product_id)
);
create table product_color(
product_id number not null references product
,color varchar2(10) not null
,constraint product_color_pk primary key(product_id)
);
create table product_price(
product_id number not null references product
,from_date date not null
,price number not null
,constraint product_price_pk primary key(product_id, from_date)
);
一些示例数据:
insert into product values(1);
insert into product values(2);
insert into product values(3);
insert into product values(4);
insert into product_color values(1, 'Red');
insert into product_color values(2, 'Green');
insert into product_price values(1, date '2016-01-01', 10);
insert into product_price values(1, date '2016-02-01', 8);
insert into product_price values(1, date '2016-05-01', 5);
insert into product_price values(2, date '2016-02-01', 5);
insert into product_price values(4, date '2016-01-01', 10);
commit;
5NF 观点
第一个视图无法编译 - 它失败并出现 ORA-01799:列可能没有外部连接到子查询。不幸的是,当我查看锚建模的在线示例时,大多数历史视图都是这样定义的……
create view product_5nf as
select p.product_id
,pc.color
,pp.price
from product p
left join product_color pc on(
pc.product_id = p.product_id
)
left join product_price pp on(
pp.product_id = p.product_id
and pp.from_date = (select max(pp2.from_date)
from product_price pp2
where pp2.product_id = pp.product_id)
);
以下是我修复它的尝试。当使用此视图并简单选择 product_id 时,Oracle 设法消除 product_color 但不 product_price。
create view product_5nf as
select product_id
,pc.color
,pp.price
from product p
left join product_color pc using(product_id)
left join (select pp1.product_id, pp1.price
from product_price pp1
where pp1.from_date = (select max(pp2.from_date)
from product_price pp2
where pp2.product_id = pp1.product_id)
)pp using(product_id);
select product_id
from product_5nf;
----------------------------------------------------------
| Id | Operation | Name | Rows |
----------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 |
|* 1 | HASH JOIN OUTER | | 4 |
| 2 | INDEX FAST FULL SCAN| PRODUCT_PK | 4 |
| 3 | VIEW | | 3 |
| 4 | NESTED LOOPS | | 3 |
| 5 | VIEW | VW_SQ_1 | 5 |
| 6 | HASH GROUP BY | | 5 |
| 7 | INDEX FULL SCAN | PRODUCT_PRICE_PK | 5 |
|* 8 | INDEX UNIQUE SCAN | PRODUCT_PRICE_PK | 1 |
----------------------------------------------------------
我找到的唯一解决方案是使用标量子查询,如下所示:
create or replace view product_5nf as
select p.product_id
,pc.color
,(select pp.price
from product_price pp
where pp.product_id = p.product_id
and pp.from_date = (select max(from_date)
from product_price pp2
where pp2.product_id = pp.product_id)) as price
from product p
left join product_color pc on(
pc.product_id = p.product_id
)
select product_id
from product_5nf;
---------------------------------------------------
| Id | Operation | Name | Rows |
---------------------------------------------------
| 0 | SELECT STATEMENT | | 4 |
| 1 | INDEX FAST FULL SCAN| PRODUCT_PK | 4 |
---------------------------------------------------
现在,Oracle 成功地删除了 product_price 表。但是,标量子查询的实现方式与连接不同,它们的执行方式根本无法让我在现实世界的场景中获得任何可接受的性能。
TL;DR
如何重写视图product_5nf 以便Oracle 成功消除两个依赖表?
【问题讨论】:
-
你确定标量子查询方法的性能真的很差吗?在 Oracle 12.1 中,优化器可以并且经常会取消嵌套标量子查询。因此,您可能会得到您正在寻找的东西:CBO 可以在不需要它们的查询中省略它们,并在需要时将它们取消嵌套以获得合理的性能。您可能会尝试标量子查询的变体,看看您是否无法诱使 Oracle 为您解除嵌套。
-
@MatthewMcPeak,我没有想到这一点,所以我只尝试了一种编写子查询的方法。绝对值得研究!我确实比较了标量 qry 与连接之间的执行计划和时间,差异是我构建的涉及数百 k 记录的稍微大一点的测试用例中执行时间的 1/3。
-
我刚刚确认这是可能的。我会发布另一个答案。
标签: sql database oracle cost-based-optimizer anchor-modeling