2016-11-18 1 views
1

J'ai une requête traitant 2 tables avec plus de 61 millions d'enregistrements chacune.Optimisation des performances d'une requête traitant des millions de lignes

  • WB_YH_BCUPDATE_FULL_BASE: table contenant les clients et tous les mois, ils étaient actifs. (de 2014 à maintenant)

CUSTOMERNUMBER | MOIS DE LA VOITURE


99999 | 201401
99999 | 201402
99999 | 201403
....

  • WB_YH_BCUPDATE_MATCH_MONTH: table contenant les clients et tous les mois qui étaient actifs avec + un champ supplémentaire contenant les CAR_MONTH fictive + 6 mois.

CUSTOMERNUMBER | CAR_MONTH | MATCH_MONTH_6


99999 | 201401 | 201407
99999 | 201402 | 201408
99999 | 201403 | 201409
...

Maintenant, je veux vérifier tous les clients et tous leurs correspondants CAR_MONTHS si elles étaient encore actifs (= ils apparaissent dans le tableau) après 6 mois. Pour cela, je dois utiliser le champ que j'ai créé, étant MATCH_MONTH_6.

J'utilise la requête suivante:

select distinct a.CUSTOMERNUMBER 
    , a.CAR_MONTH 
    , b.MATCH_MONTH_6 
    , CASE WHEN b.CUSTOMERNUMBER is null then 0 
      ELSE 1 
    END FL_MATCH_6   
from WB_YH_BCUPDATE_FULL_BASE a left join WB_YH_BCUPDATE_MATCH_MONTH b 
           on a.CUSTOMERNUMBER = b.CUSTOMERNUMBER  
           and a.CAR_MONTH = b.CAR_MONTH 
           and b.MATCH_MONTH_6 in (
           select CAR_MONTH 
           from WB_YH_BCUPDATE_FULL_BASE 
           where customernumber = a.customernumber 
           ); 

La performance de ma requête est vraiment pauvre comme vous pouvez le voir dans le plan d'exécution suivant:

Plan Hash Value : 3376431373 

----------------------------------------------------------------------------------------------------------------------------- 
| Id | Operation       | Name       | Rows  | Bytes  | Cost  | Time  | 
----------------------------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT     |        | 25897713 | 673340538 | 371846479 | 02:56:04 | 
| 1 | HASH UNIQUE      |        | 25897713 | 673340538 | 371846479 | 02:56:04 | 
| 2 | NESTED LOOPS OUTER    |        | 61874441 | 1608735466 | 371674345 | 02:55:59 | 
| 3 |  TABLE ACCESS STORAGE FULL  | WB_YH_BCUPDATE_FULL_BASE  | 61874441 | 742493292 |  3225 | 00:00:01 | 
| 4 |  VIEW       |        |  1 |   14 |   6 | 00:00:01 | 
| 5 |  NESTED LOOPS     |        |  1 |   31 |   6 | 00:00:01 | 
| 6 |  NESTED LOOPS     |        |  24 |   31 |   6 | 00:00:01 | 
| * 7 |  TABLE ACCESS BY INDEX ROWID | WB_YH_BCUPDATE_MATCH_MONTH  |  1 |   19 |   3 | 00:00:01 | 
| * 8 |   INDEX RANGE SCAN   | WB_YH_BCUPDATE_MATCH_MONTH_IND |  24 |   |   2 | 00:00:01 | 
| * 9 |  INDEX RANGE SCAN   | WB_YH_BCUPDATE_FULL_BASE_IND |  24 |   |   2 | 00:00:01 | 
| * 10 |  TABLE ACCESS BY INDEX ROWID | WB_YH_BCUPDATE_FULL_BASE  |  1 |   12 |   3 | 00:00:01 | 
----------------------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
------------------------------------------ 
* 7 - filter("A"."CAR_MONTH"="B"."CAR_MONTH") 
* 8 - access("A"."CUSTOMERNUMBER"="B"."CUSTOMERNUMBER") 
* 9 - access("CUSTOMERNUMBER"="A"."CUSTOMERNUMBER") 
* 10 - filter("CAR_MONTH"=TO_NUMBER("B"."MATCH_MONTH_6")) 

Avez-vous les gars avez une idée sur comment je peux optimiser cette requête ou comment je peux réécrire cette requête pour être plus performant?

Cordialement,

+0

Vous avez besoin d'index sur les tables. Les conditions "join" sont un bon point de départ. –

+0

Donc 'WB_YH_BCUPDATE_MATCH_MONTH' contient les mêmes données que' WB_YH_BCUPDATE_FULL_BASE', mais avec une colonne supplémentaire? – SQB

+0

J'ai des index sur les deux tables sur le champ CUSTOMERNUMBER. Et @SQB; C'est exact, mais je n'ai pas réussi à obtenir les résultats d'une autre manière sans dupliquer les données dans 2 tableaux. – wbaeckelmans

Répondre

2
SELECT 
    a.customernumber, 
    a.car_month, 
    b.car_month AS match_month_6, 
    CASE 
     WHEN b.customernumber IS NULL 
     THEN 0 
     END 1 
    END AS fl_match_6 
FROM WB_YH_BCUPDATE_MATCH_MONTH a 
LEFT JOIN WB_YH_BCUPDATE_MATCH_MONTH b 
    ON (a.customernumber = b.Customernumber AND a.match_month_6 = b.car_month); 

Puisque vous dites que WB_YH_BCUPDATE_MATCH_MONTH contient les mêmes données que WB_YH_BCUPDATE_FULL_BASE, mais avec une colonne supplémentaire, on peut utiliser l'ancien et ignorer ce dernier.

Nous avons maintenant quitté le rejoindre avec lui-même. Bien sur sur le numéro de client, mais aussi, nous joignons la date + 6 mois à la date. Si le client était actif 6 mois plus tard, nous trouverons une entrée; sinon, nous ne le ferons pas.Pour dupliquer complètement les résultats de votre requête, nous sélectionnons nos données pour match_month_6 dans la table jointe de gauche, car elle était NULL si nous ne pouvions pas obtenir une correspondance dans votre requête d'origine.

Vous devriez aussi mettre des index sur les champs des deux mois, puisque nous nous y joignons également.


Notez que cela ne garantit pas que le client était actif dans les mois intermédiaires. I un client était actif en janvier et en juillet, ils seront retournés par cette requête.

+0

Merci beaucoup! La requête semble faire exactement ce que je veux accomplir avec une grande performance. Je suis conscient du fait que cela ne garantit pas que le client était actif dans les mois intermédiaires. Créer un champ pour cela est la prochaine étape que je veux atteindre. :) – wbaeckelmans

+0

@wbaeckelmans juste par curiosité, quel a été le gain en performance? – SQB

0
select w1.CUSTOMERNUMBER, w1.CAR_MONTH, nvl2(w2.CUSTOMERNUMBER, 'Yes', 'No') active_in_6_months 
    from WB_YH_BCUPDATE_FULL_BASE w1 
    left outer join WB_YH_BCUPDATE_MATCH_MONTH w2 
    on (w1.CUSTOMERNUMBER = w2. CUSTOMERNUMBER and w1.CAR_MONTH = w2.MATCH_MONTH_6); 

Cette requête devrait vous donner résultat souhaité avec de meilleures performances.

+0

J'ai essayé cette requête et la performance est excellente, mais elle ne m'a pas donné le résultat escompté. J'ai recherché des clients spécifiques qui ont déclaré non actif après 6 mois, mais ils semblent avoir été actifs pendant toute la période. – wbaeckelmans

+0

Ceci renvoie les clients qui étaient actifs 6 mois auparavant. Même idée, mais d'un autre point de vue. – SQB

+0

@SQB vous avez raison je devrais rejoindre un peu bi différent. Je vous vois déjà posté requête modifiée. – Kacper