【发布时间】:2012-09-30 02:21:12
【问题描述】:
假设你有一个如下表:
表名:客户 主键:CUSTOMER_ID +-------------+--------------+ |客户 ID |客户名称 | +-------------+--------------+ | 1 |比尔 | | 2 |汤姆 | +-------------+--------------+现在,假设您有一个 CUSTOMER_ATTRIBUTE 表,可让您将键/值对绑定到特定的 CUSTOMER:
现在,假设您创建了一个视图来表示具有某些可能属性的客户:
CREATE VIEW CUSTOMER_VIEW AS
SELECT
CUSTOMER.CUSTOMER_ID,
CUSTOMER.CUSTOMER_NAME,
FAVORITE_FOOD_ATTRIBUTE.ATTRIBUTE_VALUE AS FAVORITE_FOOD,
FAVORITE_COLOR_ATTRIBUTE.ATTRIBUTE_VALUE AS FAVORITE_COLOR
FROM
CUSTOMER
LEFT OUTER JOIN CUSTOMER_ATTRIBUTE favorite_food_attribute
ON customer.customer_id = favorite_food_attribute.customer_id
AND favorite_food_attribute.attribute_type_id = FAVORITE_FOOD
LEFT OUTER JOIN CUSTOMER_ATTRIBUTE favorite_color_attribute
ON customer.customer_id = favorite_color_attribute.customer_id
AND favorite_color_attribute.attribute_type_id = FAVORITE_COLOR
现在,假设您查询这个视图:
SELECT
CUSTOMER_ID,
CUSTOMER_NAME,
FAVORITE_COLOR
-- Notice: I did not ask for the FAVORITE_FOOD column
FROM
CUSTOMER_VIEW
根据解释计划,Oracle 仍在加入favorite_food_attribute,尽管它的值不需要并且不影响查询的基数(因为它是LEFT OUTER JOIN 对表的主键)。
有没有办法强制 Oracle 避免这些不必要的连接?
更新:DDL 示例
这里是一些用于创建示例架构的 DDL:
CREATE TABLE CUSTOMER
(
CUSTOMER_ID NUMBER NOT NULL,
CUSTOMER_NAME VARCHAR2(100)
);
CREATE UNIQUE INDEX CUSTOMER_PK_INDEX
ON CUSTOMER(CUSTOMER_ID);
ALTER TABLE CUSTOMER
ADD CONSTRAINT CUSTOMER_PK
PRIMARY KEY (CUSTOMER_ID)
USING INDEX CUSTOMER_PK_INDEX;
CREATE TABLE CUSTOMER_ATTRIBUTE
(
CUSTOMER_ID NUMBER NOT NULL,
ATTRIBUTE_TYPE_ID NUMBER NOT NULL,
ATTRIBUTE_VALUE VARCHAR2(1000)
);
CREATE UNIQUE INDEX CUSTOMER_ATTRIBUTE_PK_INDEX
ON CUSTOMER_ATTRIBUTE(CUSTOMER_ID, ATTRIBUTE_TYPE_ID);
ALTER TABLE CUSTOMER_ATTRIBUTE
ADD CONSTRAINT CUSTOMER_ATTRIBUTE_PK
PRIMARY KEY (CUSTOMER_ID, ATTRIBUTE_TYPE_ID)
USING INDEX CUSTOMER_ATTRIBUTE_PK_INDEX;
CREATE OR REPLACE VIEW CUSTOMER_VIEW AS
SELECT
CUSTOMER.CUSTOMER_ID,
CUSTOMER.CUSTOMER_NAME,
favorite_food_attribute.attribute_value AS favorite_food,
favorite_color_attribute.attribute_value AS favorite_color
FROM
CUSTOMER
LEFT OUTER JOIN CUSTOMER_ATTRIBUTE favorite_food_attribute
ON customer.customer_id = favorite_food_attribute.customer_id
AND favorite_food_attribute.attribute_type_id = 5
LEFT OUTER JOIN CUSTOMER_ATTRIBUTE favorite_color_attribute
ON customer.customer_id = favorite_color_attribute.customer_id
AND favorite_color_attribute.attribute_type_id = 6;
现在,我对这个查询运行解释计划:
SELECT CUSTOMER_ID FROM HFSMMM.CUSTOMER_VIEW
计划是:
SELECT STATEMENT, GOAL = ALL_ROWS 成本=1 基数=1 字节=65 嵌套循环外部成本=1 基数=1 字节=65 嵌套循环外部成本=1 基数=1 字节=39 INDEX FULL SCAN 对象所有者=HFSMMM 对象名称=CUSTOMER_PK_INDEX 成本=1 基数=1 字节=13 INDEX UNIQUE SCAN 对象所有者=HFSMMM 对象名称=CUSTOMER_ATTRIBUTE_PK_INDEX 成本=0 基数=1 字节=26 INDEX UNIQUE SCAN 对象所有者=HFSMMM 对象名称=CUSTOMER_ATTRIBUTE_PK_INDEX 成本=0 基数=1 字节=26【问题讨论】:
-
根据我的经验,Oracle 通常确实避免这些不必要的连接。能发一些真实的代码和解释计划吗?
-
你运行的是什么版本的 Oracle?
-
@TonyAndrews:我无法发布真实代码,但我会尝试创建一些虚拟表(希望它们会传达同样的问题)。
-
@Annjawn:不,Oracle 的优化器应该能够对视图中引用的表进行连接消除,至少在 11g 中是这样。 Oracle optimizer blog:“一组表可能会作为一个视图公开,其中包含一个连接。连接可能是检索视图公开的所有列所必需的。但是视图的某些用户可能只访问视图的一个子集列,在这种情况下,可以消除连接表。”
-
@Annjawn:我明白了。我正在回复您的评论,您说您认为 Oracle 会始终使用视图中说明的联接。它没有;它可以从视图中消除连接。
标签: sql oracle lazy-evaluation