2

次のようなテーブルがあるとします。

テーブル名:CUSTOMER
主キー:CUSTOMER_ID
+ ------------- + --------------- +
| CUSTOMER_ID | CUSTOMER_NAME |
+ ------------- + --------------- +
| 1 | ビル|
| 2 | トム|
+ ------------- + --------------- +

ここで、CUSTOMER_ATTRIBUTEキーと値のペアを特定の値に結び付けることができるテーブルがあるとしCUSTOMERます。

テーブル名:CUSTOMER_ATTRIBUTE
主キー:(CUSTOMER_ID、ATTRIBUTE_TYPE_ID)
+ ------------- + ------------------- + --------------- --+
| CUSTOMER_ID | ATTRIBUTE_TYPE_ID | ATTRIBUTE_VALUE |
+ ------------- + ------------------- + --------------- --+
| 1 | FAVORITE_FOOD | ピザ|
| 1 | FAVORITE_COLOR | ブルー|
| 2 | FAVORITE_FOOD | タコス|
| 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

Explainプランによると、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;

ここで、このクエリでExplainプランを実行します。

SELECT CUSTOMER_ID FROM HFSMMM.CUSTOMER_VIEW

計画は次のとおりです。

SELECT STATEMENT、GOAL = ALL_ROWS Cost = 1 Cardinality = 1 Bytes = 65
 ネストされたループの外側コスト=1カーディナリティ=1バイト=65
  ネストされたループの外側コスト=1カーディナリティ=1バイト=39
   INDEXFULLSCANオブジェクトの所有者=HFSMMMオブジェクト名=CUSTOMER_PK_INDEXコスト=1カーディナリティ=1バイト=13
   INDEXUNIQUESCANオブジェクト所有者=HFSMMMオブジェクト名=CUSTOMER_ATTRIBUTE_PK_INDEXコスト=0カーディナリティ=1バイト=26
  INDEXUNIQUESCANオブジェクト所有者=HFSMMMオブジェクト名=CUSTOMER_ATTRIBUTE_PK_INDEXコスト=0カーディナリティ=1バイト=26
4

3 に答える 3

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;

そしてAdamは、私がこれを毎日ビューに使用するコンテキストの切り替えにはオーバーヘッドがあることを指摘するのは正しいです。構築してキャッシュする必要のあるマルチ結合クエリをアプリケーションに送信させるのではなく、ビューとクエリを準備するためにデータベースに事前に作業を行わせたいと思います。

于 2012-10-09T17:44:58.560 に答える
1

顧客IDと属性タイプごとにエントリが1つしかないことが確実な場合は、スカラーサブクエリを実行できます。

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
于 2012-10-09T17:54:34.997 に答える
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
于 2012-10-09T18:06:53.563 に答える