2012-10-09 108 views
2

假設你有一個表,如下所示:避免不必要的加入

 
Table Name: CUSTOMER 
Primary Key: CUSTOMER_ID 
+-------------+---------------+ 
| CUSTOMER_ID | CUSTOMER_NAME | 
+-------------+---------------+ 
| 1   | Bill   | 
| 2   | Tom   | 
+-------------+---------------+ 

現在,假設你有一個CUSTOMER_ATTRIBUTE表,讓你配合鍵/值對特定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   | 
+-------------+-------------------+-----------------+ 

現在,假設你創建一個代表客戶與一些可能的屬性的觀點:

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 

據解釋計劃,甲骨文仍然加入favorite_food_attribute,即使不需要它的價值和它不影響查詢的基數(因爲它是LEFT OUTER JOIN ING到表的主鍵)。

有沒有辦法強制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   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 
+0

根據我的經驗,Oracle通常會*避免這些不必要的連接。你能發佈一些真實的代碼和解釋計劃嗎? –

+0

您運行的是哪個版本的Oracle? –

+0

@TonyAndrews:我不能發佈* real *代碼,但我會嘗試創建一些虛擬表(希望它們會傳達相同的問題)。 –

回答

1

,如果你一定會有永遠只能是每個客戶ID一個條目和屬性類型你可以做一個標量子查詢:

SELECT 
    CUSTOMER.CUSTOMER_ID, 
    CUSTOMER.CUSTOMER_NAME, 
    (select ATTRIBUTE_VALUE from CUSTOMER_ATTRIBUTE where customer_id = CUSTOMER.CUSTOMER_ID 
     and ATTRIBUTE_TYPE_ID='F') AS FAVORITE_FOOD 
FROM 
    CUSTOMER 
+0

+1:非常好!也很簡單! –

1

雖然這種方法只是移動處理而不是消除它,但它使SQL更清潔。
創建一個用戶功能

GET_TYPE(customer_id_in NUMBER, attribute_type_id IN NUMBER) RETURN VARCHAR 2 
IS 
/* TO DO: Assertions, error handling */ 
attribute_name VARCHAR2(300); 
BEGIN 
SELECT attribute_value 
INTO attribute_name 
FROM CUSTOMER_ATTRIBUTE 
WHERE customer_id = customer_id_in 
and attribute_type_id - attribute_type_in; 


RETURN attribute_name; 

END GET_TYPE; 

,然後你的看法是

CREATE VIEW CUSTOMER_VIEW as 
SELECT 
    CUSTOMER.CUSTOMER_ID, 
    CUSTOMER.CUSTOMER_NAME, 
    GET_TYPE(1, CUSTOMER.CUSTOMER_ID) AS FOOD, 
    GET_TYPE(2, CUSTOMER.CUSTOMER_ID) AS COLOR 
FROM 
    CUSTOMER; 

和亞當是在指出存在的開銷在切換上下文 我用這對每天的意見是正確的。我寧願讓數據庫提前做好準備查看和查詢的工作,而不是讓應用程序發送必須構建和緩存的多連接查詢。

+1

這是一種可能性,但我真的希望避免拖拽PL/SQL到查詢中。由於不斷在SQL上下文和PL之間切換的開銷,我一直在困擾/ SQL上下文 –

1

除了使用外連接,使用子查詢每個屬性值的你想在視圖中看到。這假設你的數據是結構化的,這樣子查詢都不會返回多行。

CREATE VIEW CUSTOMER_VIEW AS 
SELECT CUSTOMER_ID, 
     CUSTOMER_NAME, 
     (SELECT ATTRIBUTE_VALUE FROM CUSTOMER_ATTRIBUTE ca1 
      WHERE ca1.CUSTOMER_ID = c.CUSTOMER_ID 
      AND ATTRIBUTE_TYPE_ID = 'FAVFOOD') FAVORITE_FOOD, 
     (SELECT ATTRIBUTE_VALUE FROM CUSTOMER_ATTRIBUTE ca2 
      WHERE ca2.CUSTOMER_ID = c.CUSTOMER_ID 
      AND ATTRIBUTE_TYPE_ID = 'PETNAME') PET_NAME, 
     (SELECT ATTRIBUTE_VALUE FROM CUSTOMER_ATTRIBUTE ca3 
      WHERE ca3.CUSTOMER_ID = c.CUSTOMER_ID 
      AND ATTRIBUTE_TYPE_ID = 'FAVCOLOR') FAVORITE_COLOR 
     FROM CUSTOMER c 
+0

+1:非常感謝您的回答,這與ivanatpr的回答非常相似,所以我有一段可怕的時間來接受它。做出了決定,你有尾巴,對不起。 :) –

+1

看着時間戳我看到Ivanatpr的答案是大約12分鐘前我的,所以我沒有投訴!但我今天只注意到Ivanatpr的答案,因爲當我輸入我的內容時它還不可見。 – Gigatron

相關問題