2011-01-31 4 views
3

j'ai deux tables comme celui-ciindice fonctionnel référence croisée dans Oracle

create table A_DUMMY (
     TRADE_ID  VARCHAR2(16) 
     TRADE_DATA VARCHAR2(500) 
    ) 

    create table B_DUMMY (
     EXT_TRADE_ID VARCHAR2(16) 
     EXT_DATA  VARCHAR2(250) 
    ) 

Et une vue qui est de construire quelque chose comme ça

CREATE OR REPLACE VIEW DUMMY_VIEW("TRADE_DATA", "EXT_DATA") 
    AS 
    SELECT A.TRADE_DATA, B.EXT_DATA FROM A_DUMMY A, B_DUMMY B 
    WHERE 
      GET_TRADE_NUMBER(A.TRADE_ID,'-') = GET_TRADE_NUMBER(B.EXT_TRADE_ID,'_') 
     OR 
      GET_TRADE_NUMBER(A.TRADE_ID,'-') = B.EXT_TRADE_ID 

Pour optimiser ce que j'ai créé un indice fonctionnel sur TRADE_ID dans A_DUMMY et EXT_TRADE_ID dans B_DUMMY.

La fonction ressemble à ceci:

create or replace function 
     GET_TRADE_NUMBER(trade in varchar2, separator in varchar2) 
    return varchar2 
    deterministic 
    as 
    begin  
     return SUBSTR(trade, 0, INSTR(trade, separator, 1, 1) - 1); 
    end; 

index fonctionnels ressemblent à ce

create index A_DUMMY_IDX ON A_DUMMY(GET_TRADE_NUMBER(TRADE_ID,'-')); 
    create index B_DUMMY_IDX ON B_DUMMY(GET_TRADE_NUMBER(EXT_TRADE_ID,'_')); 

données ressemble à ceci:

INSERT INTO a_dummy VALUES ('7874-LND', 'item1'); 
    INSERT INTO a_dummy VALUES ('7845-NY', 'item2'); 
    INSERT INTO a_dummy VALUES ('7844-NY', 'item3'); 

    INSERT INTO b_dummy VALUES ('7844', 'item4'); 
    INSERT INTO b_dummy VALUES ('7845_LND', 'item5'); 
    INSERT INTO b_dummy VALUES ('7874_LND', 'item5'); 

Comment puis-je faire utiliser ces index Oracle Requête fournie pour DUMMY_VIEW? Parce que, semble-t-il, tout ce que je fais selon le plan d'explication Oracle les ignore.

Répondre

4

Sans données d'exemple et les détails de la fonction, il est difficile de diagnostiquer pourquoi Oracle n'utilise pas vos index basés sur la fonction.

Je la démonstration d'un cas où l'indice est utilisé:

/* Setup */ 
CREATE OR REPLACE FUNCTION fnc (trade_id VARCHAR2) 
    RETURN VARCHAR2 
    DETERMINISTIC IS 
BEGIN 
    RETURN LOWER(trade_id); 
END fnc; 
/

INSERT INTO a_dummy VALUES ('a', 'item1'); 
INSERT INTO a_dummy VALUES ('A', 'item2'); 
INSERT INTO a_dummy VALUES ('b', 'item3'); 

INSERT INTO b_dummy VALUES ('a', 'item4'); 
INSERT INTO b_dummy VALUES ('B', 'item5'); 
INSERT INTO b_dummy VALUES ('C', 'item5'); 

Avec cette configuration, on remarque que l'index est utilisé avec des requêtes simples:

SQL> SELECT A.TRADE_DATA, B.EXT_DATA 
    2 FROM A_DUMMY A, B_DUMMY B 
    3 WHERE fnc(A.TRADE_ID) = B.EXT_TRADE_ID;  

Execution Plan 
---------------------------------------------------------- 
    0  SELECT STATEMENT Optimizer=ALL_ROWS (Cost=8 Card=3 Bytes=1197) 
    1 0 NESTED LOOPS (Cost=8 Card=3 Bytes=1197) 
    2 1  TABLE ACCESS (FULL) OF 'B_DUMMY' (TABLE) (Cost=5 Card=3 Bytes=411) 
    3 1  TABLE ACCESS (BY INDEX ROWID) OF 'A_DUMMY' (TABLE) (Cost=1 [...] 
    4 3  INDEX (RANGE SCAN) OF 'A_DUMMY_IDX' (INDEX) (Cost=0 Card=1) 

.. mais pas avec votre unfornately exemple de requête. L'opérateur OR peut empêcher l'optimiseur d'utiliser les index. Je vous suggère d'utiliser une requête équivalente:

SQL> SELECT A.TRADE_DATA, B.EXT_DATA 
    2 FROM A_DUMMY A, B_DUMMY B 
    3 WHERE fnc(A.TRADE_ID) = fnc(B.EXT_TRADE_ID) 
    4 UNION ALL 
    5 SELECT A.TRADE_DATA, B.EXT_DATA 
    6 FROM A_DUMMY A, B_DUMMY B 
    7 WHERE fnc(A.TRADE_ID) = B.EXT_TRADE_ID 
    8  AND fnc(A.TRADE_ID) != fnc(B.EXT_TRADE_ID); 

Execution Plan 
---------------------------------------------------------- 
    0  SELECT STATEMENT Optimizer=ALL_ROWS (Cost=16 Card=5 Bytes=1995) 
    1 0 UNION-ALL 
    2 1  NESTED LOOPS (Cost=8 Card=3 Bytes=1197) 
    3 2  TABLE ACCESS (FULL) OF 'A_DUMMY' (TABLE) (Cost=5 Card=3 Byt[...] 
    4 2  TABLE ACCESS (BY INDEX ROWID) OF 'B_DUMMY' (TABLE) (Cost=1 [...] 
    5 4   INDEX (RANGE SCAN) OF 'B_DUMMY_IDX' (INDEX) (Cost=0 Card=1) 
    6 1  NESTED LOOPS (Cost=8 Card=2 Bytes=798) 
    7 6  TABLE ACCESS (FULL) OF 'B_DUMMY' (TABLE) (Cost=5 Card=3 Byt[...] 
    8 6  TABLE ACCESS (BY INDEX ROWID) OF 'A_DUMMY' (TABLE) (Cost=1 [...] 
    9 8   INDEX (RANGE SCAN) OF 'A_DUMMY_IDX' (INDEX) (Cost=0 Card=1) 

Comme une note de côté: vous joindrez les deux tables Sans filtre, une HASH sans se joindre à l'indice est peut-être le meilleur moyen de calculer la jointure. Full scans are not always evil, indexes are not always good.

+0

Merci, j'ai mis à jour la question pour être plus clair, sur la fonction et les données. – mavarazy

+2

@anton_oparin: Avec les données que vous avez fournies, il serait probablement plus lent d'utiliser un index. Oracle récupère toujours les données en blocs (de 2 Ko à 32 Ko) et vos données d'échantillon tiennent dans un bloc. Il est plus rapide de lire un seul bloc au lieu de chercher dans un b-tree et ensuite de lire le même bloc. Essayez d'insérer plusieurs milliers de lignes (avec quelque chose comme INSERT INTO a_dummy, sélectionnez 'a', 'item1' à partir de dual connect par niveau <100000), puis rassemblez les statistiques sur la table et voyez si elle utilise un index. –

Questions connexes