IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

SQL Oracle Discussion :

Question optimisation requête pourrie


Sujet :

SQL Oracle

  1. #1
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 172
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 172
    Points : 7 422
    Points
    7 422
    Billets dans le blog
    1
    Par défaut Question optimisation requête pourrie
    Bonjour,

    J'ai une requête qui, après migration, est devenue extrêmement lente.
    Je ne saurais exactement vous dire de quelle version à quelle version...

    10g vers 19c il me semble... et l'application passe par un client 12c. C'est un peu le bordel, et je n'ai pas accès aux serveurs pour vérifier.

    J'ai une requête qui fait peu ou prou ça :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    var p1 number;
    var p2 number;
     
    exec :p1 := 20181129;
    exec :p2 := 600064;
     
    SELECT *
    FROM CRM_KM KM
    WHERE EXISTS (
      SELECT * FROM CRM_A1 A1 WHERE A1.ID_KM_250 = KM.ID AND ((cast(NEU / 1000000000 as int) = :p1 AND CRM_RkzPart(4200, RKZ) = :p2) OR (cast(UPD / 1000000000 as int) = :p1 AND CRM_RkzPart(4204, RKZ) = :p2))
    );

    La colonne "RKZ" de CRM_A1 est un RAW(30) et par conséquent la fonction CRM_RkzPart() pas franchement véloce et non indexable.
    Je pense que le souci vient de là : lire NEU et UPD qui sont des entiers, et les diviser par 1000000000 avant de filtrer dessus, c'est pas top non plus.

    Pourquoi c'était super rapide avant et super lent maintenant, si quelqu'un à une idée sortie du chapeau je suis preneur, sinon je vais me contenter d'essayer d'améliorer ça.

    Je peux par exemple créer une vue (je ne peux pas modifier les tables existantes) qui simplifie les choses :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    create materialized view V_A1_EXT (ID, DTE, USR)
    REFRESH force start with (sysdate) next (sysdate+1/1440) with PRIMARY KEY
    as
    select ID, cast(NEU / 1000000000 as int), CRM_RkzPart(4200, RKZ)
    from CRM_A1
    where NEU >= TO_NUMBER(TO_CHAR(sysdate, 'YYYYMMDD')) * 1000000000
    union all
    select ID, cast(UPD / 1000000000 as int), CRM_RkzPart(4204, RKZ)
    from CRM_A1
    where UPD >= TO_NUMBER(TO_CHAR(sysdate, 'YYYYMMDD')) * 1000000000;

    Et ainsi faire évoluer la requête comme suit :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    SELECT *
    FROM CRM_KM KM
    WHERE EXISTS (
      SELECT * FROM CRM_A1 A1
      WHERE A1.ID_KM_250 = KM.ID 
      AND EXISTS (
        SELECT * FROM V_A1_EXT A1e WHERE A1e.ID = A1.ID AND ((A1e.NEU = :p1 AND A1e.NEU_ID = :p2) OR (A1e.UPD = :p1 AND A1e.UPD = :p2))
      )
    );

    A votre avis, ça peut faire le taf ?
    D'autres solutions pour améliorer la chose ?

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Responsable Données
    Inscrit en
    Janvier 2009
    Messages
    5 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable Données

    Informations forums :
    Inscription : Janvier 2009
    Messages : 5 287
    Points : 12 998
    Points
    12 998
    Par défaut
    Bonjour,
    Plutôt que de calculer cast(NEU / 1000000000 as int) puis de comparer le résultat à p1, ce qui de fait élimine toute chance d'utiliser un index, pourquoi ne pas calculer une plage à partir de p1 et utiliser un between (qui lui est potentiellement indexable) ?
    Si je ne m'abuse,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    (cast(NEU / 1000000000 as int) = :p1
    revient à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    NEU beween :p1 * 1000000000 and ((:p1 + 1) * 1000000000 - 1)
    Tatayo.

  3. #3
    Membre chevronné
    Homme Profil pro
    Développeur Oracle
    Inscrit en
    Décembre 2019
    Messages
    1 162
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur Oracle

    Informations forums :
    Inscription : Décembre 2019
    Messages : 1 162
    Points : 1 959
    Points
    1 959
    Par défaut
    Salut,

    Comme tatayo je pense qu'il vaut mieux convertir sur la valeur plutôt que sur la colonne, pour permettre l'utilisation d'un index. Mais j'ai l'impression d'après ce que tu écris que le gars qui a conçu les colonnes a stocké des dates dans du number, c'es génial. Si c'est le cas, c'est un peu plus compliqué pour la borne supérieure car on aurait une date incohérente en cas de dernier jour du mois. Quelle genre de valeurs tu as dans les colonnes NEU et UPD?

  4. #4
    Expert confirmé
    Profil pro
    Inscrit en
    Août 2008
    Messages
    2 950
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 2 950
    Points : 5 849
    Points
    5 849
    Par défaut
    Si la conversion en BETWEEN est compliquée, apparemment CAST AS INT ça fait passe à l'entier supérieur à partir de 0.5, comme ROUND.

    Donc on peut modifier le CAST en ROUND qu'on peut indexer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    CREATE INDEX round_NEU_idx ON CRM_A1 (round(NEU / 1000000000));
    CREATE INDEX round_UPD_idx ON CRM_A1 (round(UPD / 1000000000));
    Par ailleurs pour profiter du cache de sous-requête scalaire, il est intéressant d'encapsuler les appels aux fonctions dans un SELECT FROM DUAL.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    SELECT *
      FROM CRM_KM KM
     WHERE EXISTS (SELECT 1
                     FROM CRM_A1 A1 
                    WHERE A1.ID_KM_250 = KM.ID 
                      AND (    (round(A1.NEU / 1000000000) = :p1 AND (select CRM_RkzPart(4200, A1.RKZ) from dual) = :p2) 
                            OR (round(A1.UPD / 1000000000) = :p1 AND (select CRM_RkzPart(4204, A1.RKZ) from dual) = :p2)
                          )
                   );

  5. #5
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 172
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 172
    Points : 7 422
    Points
    7 422
    Billets dans le blog
    1
    Par défaut
    Bonjour,

    Merci pour vos réponses

    Je ne peux pas écrire moi-même les requêtes.
    Je passe par un outil qui fait du pseudo SQL du genre :

    where New=$curDay

    => Et ça se traduit par le filtre sur NEW / 10000000000 ou parfois un BETWEEN, ça dépend du bout de programme et de l'humeur de celui qui l'a écrit...

    En tout cas, mise à part pour la vue que je crée, je ne maîtrise pas la façon dont est construite la requête :/

    Bien noté pour le CAST vs ROUND !
    Après, les décimales ça dépasse normalement pas .245959999 donc en toute logique, il n'y avait pas de problème, mise à part peut-être une différence de performances.

    Je vais voir aussi pour modifier la sous-requête pour passer par DUAL sur la fonction.
    -- Hmpf, je peux pas faire la modif dans ma vue

    Pour le reste, je pense que la vue sera dans tous les cas de très petite taille : moins de 1000 ou 2000 lignes, ca ne concerne que les élément créés ou modifiés le jour actuel.
    Donc à priori pas de réel besoin d'index sur la date par exemple.

Discussions similaires

  1. Linq - question optimisation requête
    Par boby62423 dans le forum Linq
    Réponses: 2
    Dernier message: 07/04/2009, 19h19
  2. Question optimisation de code PHP/HTML
    Par heavenvibes dans le forum Langage
    Réponses: 7
    Dernier message: 14/08/2008, 12h57
  3. Question :optimisation du rendu Ogre3d
    Par Mytech dans le forum Ogre
    Réponses: 8
    Dernier message: 21/06/2008, 23h23
  4. une question optimisation
    Par rassmug dans le forum DB2
    Réponses: 2
    Dernier message: 20/07/2007, 10h07
  5. question optimisation
    Par Eusebius dans le forum Langage SQL
    Réponses: 8
    Dernier message: 13/05/2006, 13h46

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo