[SQL] Optimisation de requête sql

Optimisation de requête sql [SQL] - SQL/NoSQL - Programmation

Marsh Posté le 14-02-2012 à 09:49:04    

Bonjour à tous,
 
J'ai un problème de temps d'exécution,j'ai une table avec plusieurs champs et à peu près 1.4 Million de lignes.
je fais un insert ensuite j'update une table, j'utilise la requête suivante :
 
UPDATE T1  
SET (COL1,COL2,COL3...) = (SELECT COL1, COL2,COL3 FROM T2 WHERE T1.PK = T2.PK)  
WHERE EXISTS (SELECT 1 FROM T2 WHERE T2.PK=T1.PK)  
 
le problème c est que j'ai laissé la requête tourner toute la nuit mais ce matin je m'apperçois que ça tourne encore, ce qui n est pas normal.
 
Est ce que vous sauriez s'il y a une possibilité d'optimiser cette requête.
 
J'ai utilisé le WHERE EXISTS, car sans cette clause, il m'affiche une erreur "il peut pas remplir les champs avec NULL"
 
Merci d'avance

Reply

Marsh Posté le 14-02-2012 à 09:49:04   

Reply

Marsh Posté le 14-02-2012 à 11:44:26    

C'est de l'Oracle, du MySQL, ... ?
 
Essayer

UPDATE T1  
SET (COL1,COL2,COL3) =
 (SELECT T2.COL1, T2.COL2, T2.COL3 FROM T2 WHERE T1.PK = T2.PK
  WHERE T2.COL1 is not null and T2.COL2 is not null and T2.COL3 is nt null)

Reply

Marsh Posté le 14-02-2012 à 12:01:39    

c'est de l Oracle, j aurai du précisé en effet :)

Reply

Marsh Posté le 14-02-2012 à 12:03:41    

Alors en ce qui concerne ta proposition je l avais déjà essayé avant d utiliser le Where exists, malheureusement j avais la même erreur oracle.
D'ailleurs je n ai pas bien compris pourquoi.
 
D'ailleur merci pour ta réponse ;)

Reply

Marsh Posté le 14-02-2012 à 12:34:15    

T1 et T2 ont la même volumétrie ?
 
PK sont les clés primaires ?
 
Les index sont bien placés ?


---------------
« Ce qui ne vous tue pas vous rend plus fort » F. Nietzsche | « Vise_ la Lune. Si tu rates, au pire, t'es dans la merde » Un poète disparu dans le cercle
Reply

Marsh Posté le 14-02-2012 à 12:38:38    

Y'a pas moyen de savoir si y'a pas des verrous intempestifs qui bloquent ta mise à jour ? (y'a bcp de monde qui l'utilise cette BDD ? bcp de transactions ? )


---------------
« Ce qui ne vous tue pas vous rend plus fort » F. Nietzsche | « Vise_ la Lune. Si tu rates, au pire, t'es dans la merde » Un poète disparu dans le cercle
Reply

Marsh Posté le 14-02-2012 à 13:43:34    

Alors c'était une requête pour donner un exemple.
sinon y a pas de verrous intempestifs, y a pas mal de monde qui utilise la BDD, mnt je selectionne un id sur lequel je suis le seul à travailelr, y a 1.4 millions de lignes, ce qui fait pas mal de ligne en effet :).
 
pour etre plus concrêt si dessous la vrai requête:
 
/* 1 */ -- cette requête permet de créer une contrepartie ficitif dans ctpr_id pour chaque facilité (relation 1-1)
 
insert into optima.r_counterparty  
 select c.RUN_ID,
        f.FACILITY_ID c.CTPR_ID,
        c.CTPR_NAME,
        c.CTPR_TYPE,
        c.RESIDENCE_COUNTRY,
        c.BUSINESS_COUNTRY,
        c.BUSINESS_GROUP,
        c.ORIGIN_CTPR_ID,
        c.RSQUARED,
        c.KMV_PID,
        c.DEFAULT_DATE,
        c.ORIGIN_FLG,
        c.TURNOVER_AMT_EUR,
        c.CTI_CTE_FLG,
        c.LGD_SENIOR_UNSECURED_RATE,
        c.RATING_CODE,
        c.RATING_CODE_CAP,
        c.MICRO_PTF_CTPR_CODE,
        c.REGUL_PTF_IRBA_CTPR_CODE,
        c.REGUL_PTF_STD_CTPR_CODE,
        c.APP_CLASS_STD,
        c.IG_FLG,
        c.RATEMAP_CODE,
        c.SHAREHOLDING_NATURE_CODE,
        c.LOCAL_RATING_CODE,
        c.PDM1,
        c.PD_MODEL_NAME,
        c.BII_CTPR_TYPE,
        c.REGUL_PTF_LEXP_CTPR_CODE,
        c.ASSET_SIZE_AMT_EUR,
        c.ASSET_SIZE_DATE,
        c.DEFAULT_RATING_FLG,
        c.PDM2,
        c.PDM3,
        c.PDM4,
        c.PDM5  
        from r_counterparty c, r_facility f
        where c.run_id = 11517
        and c.run_id=f.run_id
        and c.ctpr_id in (select CTPR_ID
                   from r_facility  
                   where f.run_id = 11517
                   group by ctpr_id
                   having count(*) > 1)
        and c.ctpr_id=f.ctpr_id
 
ensuite faut que je fasse un update et c est là ou j utilise le Where exists et ça prend beaucoup de temps, d'ailleurs jusqu a mnt je n ai pas pu arriver à terme de l exécution.

Reply

Marsh Posté le 14-02-2012 à 13:55:42    

Un EXPLAIN PLAN sur la requete SQL de mise à jour devrait donner qq pistes je pense

 

Sinon j'en déduis que T2 contient moins de lignes que T1, c'est bien ça ?


Message édité par Zzozo le 14-02-2012 à 13:56:15

---------------
« Ce qui ne vous tue pas vous rend plus fort » F. Nietzsche | « Vise_ la Lune. Si tu rates, au pire, t'es dans la merde » Un poète disparu dans le cercle
Reply

Marsh Posté le 14-02-2012 à 14:03:15    

justement il y a le même nombre de lignes mais pas le même nombre de champs, donc en faisant l update il y a des champs null dans T1 qu on peut pas mettre en T2 d ou encore la clause where exists.
La j ai changé ma requête pour prendre un champs indéxé, je relance la requête et je te tiens au courant, j espère vraiment que ça plantera pas cette fois ci :).
Je t envoie en MP toute la requête

Reply

Marsh Posté le 14-02-2012 à 14:04:15    

Sinon
UPDATE T1  
SET (COL1,COL2,COL3...) = (SELECT COL1, COL2,COL3 FROM T2 WHERE T1.PK = T2.PK)  
WHERE T1.PK IN (SELECT PK FROM T2)  
 
Ca ne passerait pas mieux ( Vérifier la syntaxe, ça fait un bail que j'ai pas fait d SQL ), en supposant que j'ai bien compris la manip :
A savoir mettre à jour toutes les lignes de T2, dans T1  
?


---------------
« Ce qui ne vous tue pas vous rend plus fort » F. Nietzsche | « Vise_ la Lune. Si tu rates, au pire, t'es dans la merde » Un poète disparu dans le cercle
Reply

Marsh Posté le 14-02-2012 à 14:04:15   

Reply

Marsh Posté le 14-02-2012 à 14:05:58    

Heu j'ai pas bien compris là ...
On met à jour de T1 vers T2   ou de T2 vers T1 (en Français, sans se référer à du SQL, please) ?


---------------
« Ce qui ne vous tue pas vous rend plus fort » F. Nietzsche | « Vise_ la Lune. Si tu rates, au pire, t'es dans la merde » Un poète disparu dans le cercle
Reply

Marsh Posté le 14-02-2012 à 14:08:38    

on met à jour T2 vers T1 :)

Reply

Marsh Posté le 14-02-2012 à 14:08:39    

T'as lancé un SELECT COUNT(PK) FROM T1 et un SELECT COUNT(PK) FROM T2    à tout hasard ?


---------------
« Ce qui ne vous tue pas vous rend plus fort » F. Nietzsche | « Vise_ la Lune. Si tu rates, au pire, t'es dans la merde » Un poète disparu dans le cercle
Reply

Marsh Posté le 14-02-2012 à 14:10:54    

maalicius a écrit :

on met à jour T2 vers T1 :)


 

maalicius a écrit :

justement il y a le même nombre de lignes mais pas le même nombre de champs, donc en faisant l update il y a des champs null dans T1 qu on peut pas mettre en T2 d ou encore la clause where exists.
La j ai changé ma requête pour prendre un champs indéxé, je relance la requête et je te tiens au courant, j espère vraiment que ça plantera pas cette fois ci :).
Je t envoie en MP toute la requête


Donc si j'ai bien compris et que je reformule :
Lors de la mise à jour de T2 vers T1, certains champs de T2 ont des valeurs à NULL et sont rejetées car les colonnes correspondantes dans T1 n'acceptent pas des valeurs à NULL
 
C'est bien ça, j'ai bien compris ?


---------------
« Ce qui ne vous tue pas vous rend plus fort » F. Nietzsche | « Vise_ la Lune. Si tu rates, au pire, t'es dans la merde » Un poète disparu dans le cercle
Reply

Marsh Posté le 14-02-2012 à 14:14:21    

je viens de relancer, et je me suis trompé. pas le même nombre de ligne et c est totalement logique car une contrepartie peut avoir plus facility donc  
count(facility)=1481322
count(counterparty)=468545

Reply

Marsh Posté le 14-02-2012 à 14:15:10    

exactement, tu as très bien compris et c'est exactement ça le problème. On peut les forcer à Null mais on ne veut pas le faire.

Reply

Marsh Posté le 14-02-2012 à 14:53:01    

Ouf, enfin j'ai réussi.
Merci pour tes conseil précieux.
J'ai changé toutes mes requêtes en prenant un champs indéxé, du coup j'ai utilisé le is not null que tu m as mis dans ta réponse, et ça marche à la perfection.
 
Merci encore pour le temps que tu as pris pour me répondre, et comprendre mon problème :)

Reply

Marsh Posté le 14-02-2012 à 15:03:25    

le is null c'est pas moi qui l'ai suggéré, de rien  [:draculax]

 


Et en cas de problèmes de perfs, le EXPLAIN PLAN est vraiment ton ami ;)


Message édité par Zzozo le 14-02-2012 à 15:05:58

---------------
« Ce qui ne vous tue pas vous rend plus fort » F. Nietzsche | « Vise_ la Lune. Si tu rates, au pire, t'es dans la merde » Un poète disparu dans le cercle
Reply

Marsh Posté le 14-02-2012 à 15:08:46    

en effet, merci à olivthill pour le conseil ;)
merci encore les amis.
Et je vais me rappler de l EXPLAIN PLAN :)
 
Excellente après midi à vous

Reply

Marsh Posté le 14-02-2012 à 15:39:56    

Ca marche pas ca? :
UPDATE T1
SET COL1 = T2.COL1, COL2 = T2.COL2, COL3 = T3.COL3, ...
FROM T1
    JOIN T2 ON T1.pk = T2.pk

Reply

Marsh Posté le 14-02-2012 à 16:46:02    

Merci pour ta réponse Oliii,  Par conter pourquoi tu utilise le JOIN?
on veut juste mettre à jours T2 vers T1

Reply

Marsh Posté le 14-02-2012 à 16:48:58    

Bosser en PS avec un curseur devrai éviter de parcourir les indexes a chaque ligne à cause du exists peut-être....

Code :
  1. CREATE OR REPLACE procedure UPDATE()
  2. IS
  3.    cursor cur IS (
  4.        SELECT COL1, COL2,COL3 FROM T2 WHERE PK IN (SELECT PK FROM T1);
  5.    );
  6. begin
  7.    FOR rec IN cur
  8.    loop
  9.        UPDATE T1 SET COL1 = cur.COL1, COL2 = cur.COL2, COL3 = cur.COL3 ..;
  10.    end loop;
  11. end;


Dans l'esprit quoi...
 
Ceci dit, le where exists c'est pas un peu bullshit, parce que a forciori s'il y a eu une jointure entre T1 et T2 c'est que y'a une ligne avec PK dans T1 et dans T2... :??:


Message édité par MEI le 14-02-2012 à 16:50:15

---------------
| AMD Ryzen 7 7700X 8C/16T @ 4.5-5.4GHz - 64GB DDR5-6000 30-40-40 1T - AMD Radeon RX 7900 XTX 24GB @ 2680MHz/20Gbps |
Reply

Marsh Posté le 14-02-2012 à 16:51:36    

maalicius a écrit :

Merci pour ta réponse Oliii,  Par conter pourquoi tu utilise le JOIN?
on veut juste mettre à jours T2 vers T1


Bah tu fait ça : WHERE T1.PK = T2.PK, ça reviens au même à priori... :??:


---------------
| AMD Ryzen 7 7700X 8C/16T @ 4.5-5.4GHz - 64GB DDR5-6000 30-40-40 1T - AMD Radeon RX 7900 XTX 24GB @ 2680MHz/20Gbps |
Reply

Marsh Posté le 14-02-2012 à 17:56:24    

Alors je susi d accord avec toi MEI que le where exists c est un peu bullshit, mnt j ai pensé à utiliser pl/SQL et un curseur et je me suis dit qu il peut y avoir plus simple.
 
Là j'ai refais toutes les requêtes et j'ai remis la clause where dans l update, et miracle ça marche super bien.
Je pense que j'ai opté pour un champs indéxé alors qu'avant c'était pas le cas :).
 
Merci encore pour toutes vos réponses et d'avoir partagé avec moi ce problème qui m'a bloqué pendant deux jours quand même :D


Message édité par maalicius le 14-02-2012 à 17:58:17
Reply

Marsh Posté le 14-02-2012 à 18:27:12    

les optimiseurs de requêtes des SGBDR sont relativement limités, et on peut vite les envoyer aux "fraises" si on y fait pas gaffe :)


---------------
« Ce qui ne vous tue pas vous rend plus fort » F. Nietzsche | « Vise_ la Lune. Si tu rates, au pire, t'es dans la merde » Un poète disparu dans le cercle
Reply

Marsh Posté le 14-02-2012 à 22:57:32    

Zzozo a écrit :

les optimiseurs de requêtes des SGBDR sont relativement limités, et on peut vite les envoyer aux "fraises" si on y fait pas gaffe :)


En fait c'est plutôt l'inverse, ils sont tellement avancés qu'ils peuvent prendre des mauvais choix car les stats sont pas a jour ou pas représentative des données... :'(


---------------
| AMD Ryzen 7 7700X 8C/16T @ 4.5-5.4GHz - 64GB DDR5-6000 30-40-40 1T - AMD Radeon RX 7900 XTX 24GB @ 2680MHz/20Gbps |
Reply

Marsh Posté le 15-02-2012 à 09:16:53    

rere everybody,
 
Alors y a une chose dont je suis pas sûr:
imaginons vous avez le cas suivant:
1 ctpr_id peut avoir plusieur facility_id
on prend un exemple: 1 ctpr_id a 10 facility_id
avec la longue requête que j'ai mis ci dessus, elle me permettra de créer 1 ctpr_id_bis dans ctpr_id pour chaqu'une des 10 facility
 
à la fin il y aura 10 ctpr_id  différents pour chacune des 10 facility_id
 
La question que je me pose est ce que le premier ctpr_id va rester ou va être inclus dans les 10 ctpr_id?
 
Si je me suis mal exprimé ou vous n avez pas bien compris ma question n'hésitez pas :)


Message édité par maalicius le 15-02-2012 à 09:18:04
Reply

Marsh Posté le 15-02-2012 à 09:47:29    

J'ai trouvé une petite solution mais probablement il y a mieux.
Alors, dans ma base tout se passe par run_id
ce que je vais faire c est faire tousles changements les updates sur mon run_id actuel, ensuite je vais faire une comparaison avec un run_id qui n a pas été touché, ensuite je vais voir les ctpr_id qui sont resté ensuite les delete.
 
Pensez vous qu il y a une solution plus rapide?

Reply

Marsh Posté le 15-02-2012 à 20:40:49    

MEI a écrit :


En fait c'est plutôt l'inverse, ils sont tellement avancés qu'ils peuvent prendre des mauvais choix car les stats sont pas a jour ou pas représentative des données... :'(


Justement, je ne trouve pas ça très avancé, car en dehors de l'exploitation de stats collectées, il y a assez peu d'heuristiques présentes dans ces analyseurs.

 

Et je suis d'accord que ça s'en ressent quand les stats sont pas suffisamment à jour :/
Le pb c'est que la collecte de ces stats est pas transparente et a un coût en termes de ressources.
C'est pas la première fois que je vois cette collecte passée en manuel, quand le/les dba y pensent, et surtout que l'utilisation des serveurs le permet.

 

Enfin bon, rien ne remplace (encore) un humain bien entrainé :)


Message édité par Zzozo le 15-02-2012 à 20:41:23

---------------
« Ce qui ne vous tue pas vous rend plus fort » F. Nietzsche | « Vise_ la Lune. Si tu rates, au pire, t'es dans la merde » Un poète disparu dans le cercle
Reply

Marsh Posté le 16-02-2012 à 08:53:11    

Heu... ya 15ans peut etre ... mais la les optimisateurs tiennent en compte beaucoup plus de choses que les stats. Il y a l'etat du server, la charge CPU, la pression mémoire, les dizaines de parametres, les hints et options, sans parler des queries qui font des fois des pages et des pages avec des dizaines de join dans tout les sens.
 
Ca fait tres longtemps qu'un humain ne sais pas optimiser aussi bien qu'une machine.
Un humain est parfois nécéssaire pour ajouter des indexes (ou en enlever), ce que l'optimisateur ne fais pas evidement, mais en ce qui concerne l'optimisation d'une query en se servant des données et indexes disponible un optimisateur est pratiquement imbatable.

Reply

Marsh Posté le 16-02-2012 à 11:24:35    

Le probleme peut aussi venir de la taille de ton logfile insufisante et d'une transaction du coup trop importante. Un moyen simple en oracle est de traiter par lots en faisant simplement :

Code :
  1. UPDATE T1 
  2. SET (COL1,COL2,COL3...) = (SELECT COL1, COL2,COL3 FROM T2 WHERE T1.PK = T2.PK) 
  3. WHERE EXISTS (SELECT 1 FROM T2 WHERE T2.PK=T1.PK AND (T1.COL1 != T2.COL1 OR T1.COL2 != T2.COL2 OR.....)) 
  4. AND ROWNUM < 10000


ce qui te permettra de ne traiter "que" les 10000 premieres lignes et faire les MAJ uniquement quand tu as au moins une différence.
A voir si cela change qq chose ou non, tu peux aussi dans un premier temps évaluer les volumes a mettre a jour avec un select count(*) et la meme clause where.
 
 
Edit : clause where ecrite trop vite


Message édité par fred777888999 le 16-02-2012 à 14:11:06
Reply

Marsh Posté le 16-02-2012 à 16:24:31    

Oliiii a écrit :

Heu... ya 15ans peut etre ... mais la les optimisateurs tiennent en compte beaucoup plus de choses que les stats. Il y a l'etat du server, la charge CPU, la pression mémoire, les dizaines de parametres, les hints et options, sans parler des queries qui font des fois des pages et des pages avec des dizaines de join dans tout les sens.
 
Ca fait tres longtemps qu'un humain ne sais pas optimiser aussi bien qu'une machine.
Un humain est parfois nécéssaire pour ajouter des indexes (ou en enlever), ce que l'optimisateur ne fais pas evidement, mais en ce qui concerne l'optimisation d'une query en se servant des données et indexes disponible un optimisateur est pratiquement imbatable.


 [:rofl]  [:rofl]  [:rofl]  [:rofl]  [:rofl]


---------------
« Ce qui ne vous tue pas vous rend plus fort » F. Nietzsche | « Vise_ la Lune. Si tu rates, au pire, t'es dans la merde » Un poète disparu dans le cercle
Reply

Marsh Posté le 16-02-2012 à 16:39:41    

Déjà faudrait pas confondre tout.
Les SGBD on plusieurs étapes :
- réécriture de requête
- ordre de jointure
- meilleur chemin d'accès
 
Déjà pour sur, l'ordre de jointure est rarement optimal, toutes les combinaisons ne sont pas évaluées => une limite haute existe, et dès qu'une combinaison avec une valeur "faible" est trouvé, on ne cherche pas mieux. Pourquoi, c'est simple, on ne peut pas passer un temps infini a faire ce travail assez couteux.


---------------
| AMD Ryzen 7 7700X 8C/16T @ 4.5-5.4GHz - 64GB DDR5-6000 30-40-40 1T - AMD Radeon RX 7900 XTX 24GB @ 2680MHz/20Gbps |
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed