Suppose you have a table as follows:
Table Name: CUSTOMER Primary Key: CUSTOMER_ID +-------------+---------------+ | CUSTOMER_ID | CUSTOMER_NAME | +-------------+---------------+ | 1 | Bill | | 2 | Tom | +-------------+---------------+
Now, suppose you have a CUSTOMER_ATTRIBUTE
table that lets you tie key/value pairs to a particular CUSTOMER
:
Table Name: CUSTOMER_ATTRIBUTE Primary Key: (CUSTOMER_ID, ATTRIBUTE_TYPE_ID) +-------------+-------------------+-----------------+ | CUSTOMER_ID | ATTRIBUTE_TYPE_ID | ATTRIBUTE_VALUE | +-------------+-------------------+-----------------+ | 1 | FAVORITE_FOOD | Pizza | | 1 | FAVORITE_COLOR | Blue | | 2 | FAVORITE_FOOD | Taco | | 2 | NAME_OF_PET | Fido | +-------------+-------------------+-----------------+
Now, suppose you create a view that represents a customer with some of its possible attributes:
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
Now, suppose you query this view:
SELECT
CUSTOMER_ID,
CUSTOMER_NAME,
FAVORITE_COLOR
-- Notice: I did not ask for the FAVORITE_FOOD column
FROM
CUSTOMER_VIEW
According to the explain plan, Oracle is still joining favorite_food_attribute
, even though its value is not needed and it does not affect the query's cardinality (because it's LEFT OUTER JOIN
ing to a table's primary key).
Is there a way to force Oracle to avoid these unnecessary joins?
Update: Example DDL
Here is some DDL to create the example schema:
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;
Now, I run the explain plan on this query:
SELECT CUSTOMER_ID FROM HFSMMM.CUSTOMER_VIEW
The plan is:
SELECT STATEMENT, GOAL = ALL_ROWS Cost=1 Cardinality=1 Bytes=65 NESTED LOOPS OUTER Cost=1 Cardinality=1 Bytes=65 NESTED LOOPS OUTER Cost=1 Cardinality=1 Bytes=39 INDEX FULL SCAN Object owner=HFSMMM Object name=CUSTOMER_PK_INDEX Cost=1 Cardinality=1 Bytes=13 INDEX UNIQUE SCAN Object owner=HFSMMM Object name=CUSTOMER_ATTRIBUTE_PK_INDEX Cost=0 Cardinality=1 Bytes=26 INDEX UNIQUE SCAN Object owner=HFSMMM Object name=CUSTOMER_ATTRIBUTE_PK_INDEX Cost=0 Cardinality=1 Bytes=26