|
Publicité ' | ||||||||||||||||||||||||
|
|
#1 |
|
Candidat au titre de Membre du Club
![]() Gratien Inscription : octobre 2009 Messages : 65 ![]() |
Bonjour à tous,
J'ai un souci avec la performance d'une requête de calcul de chemin le plus court. Tout d'abord, je vous expose le contexte : je souhaite modéliser un graphe orienté multiple. Voila pour la théorie. Dans la pratique, cela peut s'apparenter à un GPS. J'ai une liste de sommets (les villes) et des arêtes (les routes). Mes routes sont unidirectionnelles et ont une longueur. Il existe des boucles. De plus il existe aussi des villes reliées entre elles par deux routes distinctes (mais n'ayant pas la même longueur). Je connais la ville de départ de toutes mes excursions et je cherche a rallier mon point d'arrivée qui est connu à l'avance. Mais attention, je dois impérativement passer par certaines 'routes' et je ne reviens jamais au point de départ (ce n'est pas un cycle) Voici un exemple de graphe que je modélise : ![]() (Je n'ai pas pu représenter les routes multiples) Pour modéliser tout cela, j'ai deux tables fixes : la table des villes et la table des routes. Une route a une ville de départ, une ville d'arrivée et une longueur J'ai par ailleurs une table dynamique qui contient une ligne par passage dans une route Ma technique utilisée pour résoudre le problème : Je calcule l'ensemble des chemins possibles entre la ville de départ et la ville d'arrivée. Avant de partir, je calcule la liste des chemins qui passent par toutes les routes qui m’intéressent et je prend le plus court. Je me suis aidé de l'excellent papier sur les requêtes récursives http://sqlpro.developpez.com/cours/s...te-recursives/. Dans ma base j'ai 32 villes et 60 routes. Le problème est que depuis le point de départ de mon périple vers la dernière ville me donne presque 70 000 possibilités de chemins (en presque 3 minutes). Or maintenant on me demande d'effectuer le calcul en temps réel (cad en moins d'environ 100ms) et cela pour n'importe quel point. En gros une fois que j'arrive dans une ville, je dois recalculer le chemin le plus court pour aller vers la dernière ville, tout en passant par les routes obligatoires et en éliminant celles déjà visitées. La requête récursive me donne une liste de chemins possibles. Chaque chemin comporte un varchar avec les routes empruntées séparées par une virgule. Si je décompose cette liste en lignes pour faciliter la comparaison avec les routes obligatoires, j’obtiens une liste de prés de 2milions de lignes, ce qui est incompatible avec ma performance. Pouvez vous m'aider sur la méthode employée ? Trouvez vous la technique viable ou bien dois je repenser la structure pour améliorer la performance ? Si vous le souhaitez je peux donner des définitions de tables et les requêtes utilisées pour générer tout cela. Merci d'avance ! (Désolé pour la longueur) |
|
|
00
|
|
|
#2 |
![]() ![]() |
Le problème du voyageur de commerce ! La solution réside plutôt dans les l'optimisation linéaire ou les algorithmes génétiques dont le code est plus efficace je pense dans les langages de programmation classiques.
Probablement faisable en SQL, mais en 100ms j'en doute un peu ! Néanmoins, c'est un bon sujet !
__________________
Email : http://scr.im/waldar |
|
00
|
|
|
#3 |
![]() ![]() ![]() Frédéric BROUARDExpert SGBDR & SQL Inscription : mai 2002 Messages : 10 954 ![]() |
Oubliez la programmation génétique et autres folklore du genre... Vous n'irez pas plus vite.
Plusieurs éléments doivent être pris en considération : 1) comment la table est-elle indexée ? 2) au lieu de stocker les noms des villes dans les chemins, préférez stockez les ID des villes 3) toutes ces données étant statiques, il suffit de les calculer une bonne fois pour toute puis mettre un trigger sur INSERT, UPDATE, DELETE pour lancer le calcul en bacth de nuit. Pour ce faire j'aurais modélisé comme suit : 1) tables des "puits" villes : T_VILLE_VIL (VIL_ID, VIL_NOM) 2) table des "arcs" route : T_ROUTE_RTE (RTE_ID, VIL_ID_DEPART, VIL_ID_ARRIVE, RTE_DISTANCE_ALLER, RTE_DISTANCE_RETOUR) 3) table des trajets : T_TRAJET_TRJ (TRJ_ID, VIL_ID_DEPART, VIL_ID_ARRIVE, TRJ_DISTANCE, TRJ_OK) 4) table du détail des trajets : T_TRAJET_CHEMIN_CHM (CHM_ID, TRJ_ID, VIL_ID, CHM_ORDRE, CHM_DISTANCE) Les deux dernières tables étant calculées via les triggers mais indirectement. En gros le trigger insère des lignes dans la table T_TRAJET_TRJ avec la ville de départ et d'arrivée avec TRJ_OK = 0 pour signaler que ce trajet est à calculer. A +
__________________
Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL Site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/ Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp. Blog SQL, SQL Server, modélisation données : http://blog.developpez.com/sqlpro http://www.sqlspot.com : modélisation, conseils, audit, optimisation, formation * * * * * Enseignant CNAM PACA - ISEN Toulon - CESI Aix en Provence * * * * * |
|
00
|
|
|
#4 | ||||||||||||||||
|
Candidat au titre de Membre du Club
![]() Gratien Inscription : octobre 2009 Messages : 65 ![]() |
Merci pour vos réponses
Citation:
Citation:
Dans mon cas, les villes sont des points de décision et les routes sont des convoyeurs. Citation:
Citation:
Code :
Citation:
Code :
Citation:
Pour ma part, j'ai calculé cela : Code :
Les données que je possède, me donnent déjà près de 71000 chemins possibles. (pour mémoire, j'ai seulement 32 points de décision et 60 convoyeurs). MAIS : comme mon graphe n'est pas simple, il est possible qu'un chemin nécessite de passer plusieurs fois par le même point de décision pour atteindre la sortie. J'ai donc ajouté une colonne NB_PASSAGES_CHEMIN pour limiter le nombre de fois de passage et ainsi ne pas exploser la récursivité de mon CTE. Citation:
Code :
Citation:
Mon problème est que trouver le bon chemin ne doit pas prendre de temps car la réponse doit venir en 'temps réel' (cad environ 100ms maxi de temps de réponse) et ce sur n'importe quel point de décision du graphe. La requête de recherche du chemin doit selon moi faire un produit cartésien sur les CONVOYEUR_CHEMIN et ma table de chemins imposés. Puis trouver le chemin qui correspond en faisant un 'count' du nombre de convoyeurs du chemin et trouver ceux qui on le même nombre que le nombre de mes étapes imposées. (Je sais pas si c'est très clair). Bref cette requête est à ma portée, mais faire cela avec 2M de lignes, c'est vraiment chaud pour un temps de réponse très très court. Pour l'instant, je n'ai pas évalué le temps que cela prend sur mon serveur de test. Je n'ai pas non plus ajouté le moindre index sur les tables que je vous ai présenté. J'en suis encore à la phase de modélisation et d'écriture des requêtes. Mais si cela n'est pas possible en faisant de cette manière, alors je devrais peut être revoir ma structure de données. Cela ne me pose pas de problème de tout casser pour faire différemment. Merci de ton aide ! |
||||||||||||||||
|
|
00
|
|
|
#5 | |
|
Membre Expert
![]() |
Citation:
__________________
Prendre conscience, c'est transformer le voile qui recouvre la lumière en miroir. |
|
|
|
00
|
|
|
#6 | |||||
![]() ![]() ![]() Frédéric BROUARDExpert SGBDR & SQL Inscription : mai 2002 Messages : 10 954 ![]() |
Citation:
Déjà, au moins : Code :
CREATE INDEX X_CON_FROM_TO_LONG ON dbo.CONVOYEUR (NO_POINT_DECISION_FROM, NO_POINT_DECISION_TO, LONGUEUR); Citation:
Faisons un calcul, avec 10 000 villes l'ensemble des trajets fait 10000x10000 = 100 000 000. Or dans cette table on ne conserve que 4 entiers, soit 32 octets. 32 octets par 100 millions, cela fait 3 Go pour la table. Bref du pipi de chat ! Citation:
1) c'est pas relationnel : viol de la 1FN : les données doivent être atomique 2) ça va effectivement générer des centaines de Go Commence par revoir cela on discute ensuite. 1) table des trajet 2 table des détails de trajets A +
__________________
Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL Site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/ Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp. Blog SQL, SQL Server, modélisation données : http://blog.developpez.com/sqlpro http://www.sqlspot.com : modélisation, conseils, audit, optimisation, formation * * * * * Enseignant CNAM PACA - ISEN Toulon - CESI Aix en Provence * * * * * |
|||||
|
00
|
|
|
#7 | ||||||||||
|
Candidat au titre de Membre du Club
![]() Gratien Inscription : octobre 2009 Messages : 65 ![]() |
Citation:
Ce point n'étant pas mon problème principal, j'ai suivi ce conseil et mis un index. Citation:
La table CHEMIN contient la liste des chemins depuis le point de départ jusqu’à l'arrivée. C'est une table intermédiaire. Je ne m'en sert que pour calculer le détail des chemins. Pour faire simple, dans la table chemin j'ai (extrait parmi les 71000 lignes) : Code :
et dans la table du détail des chemins j'ai : Code :
Bien sûr j'ai fait des indexes sur tout cela : Code :
Code :
Je veux pas vous noyer avec le code, alors j'ai simplifié la requête de dessus. La partie simplifiée est marquée avec un commentaire. Ce que je constate, c'est que la requete ci dessus -telle quelle- est vraiment rapide. La partie que j'ai simplifié est relativement simple et -si je l'éxécute toute seule-, est aussi très rapide. En revanche, lorsque je met les deux ensemble, cela devient vraiment lent. Ce n'est pas la première fois que je constate ce genre de phénomène. Vous prenez un requête qui semble optimisée et rapide, vous la collez dans un cte qui lui aussi est rapide tout seul, et le tout devient vraiment lent. Je peux fournir la partie simplifiée si cela est pertinent. Pour finir, j'en revient à ce que tu disais : Il me semble que c'est ce que j'ai fait. Je comprend que la table que j'ai faite n'est pas relationelle, mais je ne sais pas faire autrement. Comment faire une table des trajets ? |
||||||||||
|
|
00
|
|
|
#8 | ||
![]() ![]() ![]() Frédéric BROUARDExpert SGBDR & SQL Inscription : mai 2002 Messages : 10 954 ![]() |
C'est justement cette table là qui ne vas pas du tout :
Code :
Commencer par la décomposer en deux : Entête avec les données relationnelles, puis détail (peut être deus tables détails d'ailleurs) en reprenant la clef de la première à titre de FK et en décomposant vos LISTE_POINT_DECISION et LISTE _CONVOYEUR (comme il y en a 2 et que je ne voit pas de lien entre les deux, alors vous aurez 2 tables détail. A +
__________________
Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL Site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/ Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp. Blog SQL, SQL Server, modélisation données : http://blog.developpez.com/sqlpro http://www.sqlspot.com : modélisation, conseils, audit, optimisation, formation * * * * * Enseignant CNAM PACA - ISEN Toulon - CESI Aix en Provence * * * * * |
||
|
00
|
|
|
#9 |
![]() ![]() |
Ce qui serait super ce serait de fournir les données !
Comme ça on pourrait vraiment s'amuser.
__________________
Email : http://scr.im/waldar |
|
00
|
|
|
#10 |
|
Candidat au titre de Membre du Club
![]() Gratien Inscription : octobre 2009 Messages : 65 ![]() |
|
|
|
00
|
|
|
#11 | |
|
Candidat au titre de Membre du Club
![]() Gratien Inscription : octobre 2009 Messages : 65 ![]() |
Citation:
Merci |
|
|
|
00
|
|
|
#12 | ||
![]() ![]() |
Un script SQL classique :
Code :
__________________
Email : http://scr.im/waldar |
||
|
00
|
|
|
#13 | ||
|
Candidat au titre de Membre du Club
![]() Gratien Inscription : octobre 2009 Messages : 65 ![]() |
Voila :
J'ai ajoute la table ETAPES qui contient la liste des convoyeurs imposés pour ce cas. Code :
|
||
|
|
00
|
|
|
#14 |
![]() ![]() |
J'ai pu jouer avec vos données.
Je suis parti sur une solution calculée à la volée, pas sur la solution de SQLPro qui - puisqu'elle consiste à distribuer le calcul en tâche planifiée la nuit pour ne plus faire qu'une simple lecture en journée - sera beaucoup plus performante dès lors que la volumétrie augmentera. En fait, la requête dynamique répond mieux que ce que je ne pensais, comme vous je suis parti sur une CTE récursive. Par contre, je ne retrouve pas vos 70000 ou 2M de lignes dans les étapes intermédiaires (en fait mes étapes intermédiaires sont présentes dans le select, je n'ai rien en plus du script que vous avez fourni). Je ne suis pas tombé à 100 ms comme vous le souhaitez, mais c'est déjà pas trop mal : j'ai tronqué les 0 des "lob physical reads" et "lob read-ahead reads" pour conserver la mise en page SQL Server parse and compile time: CPU time = 47 ms, elapsed time = 82 ms. (8 ligne(s) affectée(s)) Table 'Worktable' . Scan count 2, logical reads 3726, physical reads 0, read-ahead reads 0, lob logical reads 1954. Table 'POINT_DECISION'. Scan count 1, logical reads 1416, physical reads 0, read-ahead reads 0, lob logical reads 0. Table 'ETAPES' . Scan count 355, logical reads 42836, physical reads 0, read-ahead reads 0, lob logical reads 0. Table 'CONVOYEUR' . Scan count 354, logical reads 708, physical reads 0, read-ahead reads 0, lob logical reads 0. SQL Server Execution Times: CPU time = 266 ms, elapsed time = 289 ms. chemin longueur ----------------------------------------------------------- ----------- 36,24,20,13,1,6,7,8,9,10,21,14,15,2,3,4,5,22,12,17,27,18,33 461537 Si oui, comment procède-t-on : je vous donne ma solution brut de fonderie ou j'essaie de vous y amener à partir de votre solution ?
__________________
Email : http://scr.im/waldar |
|
00
|
|
|
#15 | |
|
Membre Expert
![]() |
Citation:
Ca reste inperceptible pour l'utilisateur...
__________________
Prendre conscience, c'est transformer le voile qui recouvre la lumière en miroir. |
|
|
|
00
|
|
|
#16 | |
|
Candidat au titre de Membre du Club
![]() Gratien Inscription : octobre 2009 Messages : 65 ![]() |
Citation:
mais moi j'ai plutot ca : SQL Server \endash Temps d'exécution*: , Temps UC = 0 ms, temps écoulé = 1 ms. Temps d'analyse et de compilation de SQL Server : , Temps UC = 0 ms, temps écoulé = 1 ms. (100*ligne(s) affectée(s)) Table 'CHEMIN'. Nombre d'analyses 1, lectures logiques 1301, lectures physiques 0, lectures anticipées 0 Table 'Worktable'. Nombre d'analyses 0, lectures logiques 0, lectures physiques 0, lectures anticipées 0 Table 'DETAIL_CHEMIN'. Nombre d'analyses 10, lectures logiques 1055, lectures physiques 0, lectures anticipées 0 Table 'ETAPES'. Nombre d'analyses 2, lectures logiques 4, lectures physiques 0, lectures anticipées 0 SQL Server \endash Temps d'exécution*: , Temps UC = 453*ms, temps écoulé = 450*ms. Les trois étapes sont :
Le tout me donne le résultat.... En effet je préfèrerais vous présenter ma solution et vous m'expliquiez où sont mes erreurs, cela sera formateur... |
|
|
|
00
|
|
|
#17 | ||||
|
Candidat au titre de Membre du Club
![]() Gratien Inscription : octobre 2009 Messages : 65 ![]() |
La table CHEMIN et DETAIL_CHEMIN
Code :
Code :
|
||||
|
|
00
|
|
|
#18 | ||
|
Candidat au titre de Membre du Club
![]() Gratien Inscription : octobre 2009 Messages : 65 ![]() |
LA requête pour construire la table CHEMIN :
Code :
Temps d’exécution : (sans l'insert) !! Temps d'analyse et de compilation de SQL Server : , Temps UC = 0*ms, temps écoulé = 1*ms. SQL Server \endash Temps d'exécution*: , Temps UC = 0*ms, temps écoulé = 1*ms. Temps d'analyse et de compilation de SQL Server : , Temps UC = 47*ms, temps écoulé = 54*ms. (70968*ligne(s) affectée(s)) Table 'Worktable'. Nombre d'analyses 2, lectures logiques 5799963, lectures physiques 0, lectures anticipées 0, lectures logiques de données d'objets volumineux 2193336 Table 'POINT_DECISION'. Nombre d'analyses 1, lectures logiques 878863, lectures physiques 0, lectures anticipées 0, lectures logiques de données d'objets volumineux 0 Table 'CONVOYEUR'. Nombre d'analyses 1, lectures logiques 432555, lectures physiques 0, lectures anticipées 0, lectures logiques de données d'objets volumineux 0 SQL Server \endash Temps d'exécution*: , Temps UC = 132046*ms, temps écoulé = 135647*ms. |
||
|
|
00
|
|
|
#19 | ||
|
Candidat au titre de Membre du Club
![]() Gratien Inscription : octobre 2009 Messages : 65 ![]() |
La requete pour construire la table DETAIL_CHEMIN
Code :
Mais la encore je ne l’exécute qu'une seule fois.. |
||
|
|
00
|
|
|
#20 | ||
|
Candidat au titre de Membre du Club
![]() Gratien Inscription : octobre 2009 Messages : 65 ![]() |
Enfin la requête finale qui me donne le chemin le plus court selon les étapes dans la table ETAPES:
Code :
L'intérêt c'est que je ne fait que la derniere requête à chaque fois que je dois trouver le meilleur chemin. De plus la table ETAPES dépend de chaque élément sur mon convoyage, je doit donc connaitre le chemin pour d'autres étapes imposées sans tout recalculer.... |
||
|
|
00
|
Copyright © 2000-2012 - www.developpez.com