Besoin d'aide pour écrire une requête SQL compliquée [résolu] - SQL/NoSQL - Programmation
Marsh Posté le 09-11-2006 à 09:51:47
si vous avez besoin de plus de précision, y'a qu'à demander
Marsh Posté le 09-11-2006 à 12:01:09
Bon, j'ai trouvé une requête qui fonctionne mais si vous trouvez une requête plus optimisée (notamment pour dégager les IN qui consomment pas de temps si je me souviens bien), je suis preneur
SELECT ConfigurationID FROM Configurations WHERE (ConfigurationParentID, ConfigurationInternalVersion) IN (SELECT c.ConfigurationParentID, MAX(c.ConfigurationInternalVersion) AS MaxInternalVersion FROM Configurations c WHERE
c.ConfigurationDate IS NOT NULL GROUP BY c.ConfigurationParentID)
UNION DISTINCT SELECT ConfigurationID FROM Configurations WHERE ConfigurationParentID IS NULL AND ConfigurationDate IS NOT NULL AND ConfigurationID NOT IN (SELECT DISTINCT c.ConfigurationParentID FROM Configurations c WHERE c.ConfigurationParentID IS NOT NULL)
Marsh Posté le 09-11-2006 à 15:58:25
mais arrêtez avec vos "IN" à tout bout de champ
EXISTS vin Dieu !
et juste en passant "UNION" est obligatoirement "DISTINCT", donc pas besoin de le spécifier (sinon, on écrit UNION ALL si on veut les doublons).
Marsh Posté le 13-11-2006 à 11:03:30
MagicBuzz a écrit : mais arrêtez avec vos "IN" à tout bout de champ |
pour le coup du union distinct, je m'en suis rendu compte ce WE en lisant un bonquin sur SQL. Par contre, j'ai dû mal à voir la tête de la requête avec des EXISTS à la place des IN.
Marsh Posté le 13-11-2006 à 14:58:35
la syntaxe ne change pas d'un poil avec EXISTS.
Je vais pas réécrire pour la 1000 fois ce type de requête avec un EXISTS à la place des IN. Cherche juste "sodo gravier" ou "seau d'eau gravier" (ça dépend de mon humeur) sur le forum, tu trouveras forcément ces exemples que j'ai posté. Ca doit faire au moins 1 an que je passe mon temps à dire que le IN n'est pas fait pour ce genre de choses, j'en ai marre de prêcher dans le désert.
Marsh Posté le 14-11-2006 à 16:03:26
Une question : est-ce-que le c.ConfigurationParentID de la première aprtie de ma requête est le même que le c.ConfigurationParentID qui se trouve dans la 2ième partie (la 2ième partie étant celle après le UNION). En d'autres termes, est-ce que l'alias "c" que j'utilise est local à ma sous-requête ou il est commun à toute la requête, y compris la sous-requête se trouvant après le UNION?
Marsh Posté le 14-11-2006 à 16:44:09
il est différent de chaque côté du union.
par contre, pour plus de lisibilité, tu as raison, il vaudrais mieux trouver deux alias différents. style "c" et "cp" pour "configyationparent" par exemple
Marsh Posté le 14-11-2006 à 18:25:27
J'ai un soucis, toujours sur la même requête. Quand j'execute cette requête, ça va, ça répond vite (ma BD ne fait que 182 ko)
SELECT COUNT(DISTINCT c.ConfigurationID) FROM Configurations c, Components cmp0, ComponentsAttributes cmpa0, Attributes attr0, Components cmp1, ComponentsAttributes cmpa1, Attributes attr1 WHERE c.ConfigurationID IN (SELECT sc1.ConfigurationID FROM Configurations sc1 WHERE (sc1.ConfigurationParentID, sc1.ConfigurationInternalVersion) IN (SELECT sco1.ConfigurationParentID, MAX(sco1.ConfigurationInternalVersion ) AS MaxInternalVersion FROM Configurations sco1 WHERE sco1.ConfigurationDate IS NOT NULL GROUP BY sco1.ConfigurationParentID) |
Par contre, en exécutant cette requête, mysql part en live et se met à pomper 1 Go de RAM et j'ai mon résultat au bout de plusieurs minutes!!!!! Pourquoi?
SELECT DISTINCT c.ConfigurationID, c.ConfigurationName, c.ConfigurationVersion, c.ConfigurationInternalVersion, c.ConfigurationDate, c.ConfigurationComment, c.ConfigurationParentID FROM Configurations c, Components cmp0, ComponentsAttributes cmpa0, Attributes attr0, Components cmp1, ComponentsAttributes cmpa1, Attributes attr1 WHERE c.ConfigurationID IN (SELECT sc1.ConfigurationID FROM Configurations sc1 WHERE (sc1.ConfigurationParentID, sc1.ConfigurationInternalVersion) IN (SELECT sco1.ConfigurationParentID, MAX(sco1.ConfigurationInternalVersion) AS MaxInternalVersion FROM Configurations sco1 WHERE sco1.ConfigurationDate IS NOT NULL GROUP BY sco1.ConfigurationParentID) UNION SELECT usc1.ConfigurationID FROM Configurations usc1 WHERE usc1.ConfigurationParentID IS NULL AND usc1.ConfigurationDate IS NOT NULL AND usc1.ConfigurationID NOT IN (SELECT DISTINCT sco2.ConfigurationParentID FROM Configurations sco2 WHERE sco2.ConfigurationParentID IS NOT NULL)) AND c.ConfigurationID = cmp0.ConfigurationID AND cmp0.ComponentID = cmpa0.ComponentID AND cmpa0.AttributeID = attr0.AttributeID AND attr0.AttributeName LIKE "%nom%" AND attr0.AttributeValue LIKE "%fd%" AND c.ConfigurationID = cmp1.ConfigurationID AND cmp1.ComponentID = cmpa1.ComponentID AND cmpa1.AttributeID = attr1.AttributeID AND attr1.AttributeName LIKE "%modele%" AND attr1.AttributeValue LIKE "%drive%" ORDER BY c.ConfigurationName LIMIT 0, 8 |
C'est la même requête, sauf que dans le 1er cas, je récupère ne nb de configurations totales correspondant à mes critères et dans le second, je récupère des infos sur qq confs correspondant à mes critères.
ps : vous connaissez un bon tuto sur EXPLAIN de MySQL? Merci
Marsh Posté le 14-11-2006 à 19:16:46
T'ain, tu bosserais pas sous MySQL, je jureraus que tu bosses pour Econocom
Le même modèle des données abominable
Est-ce qu'il y a moyen de nous la refaire au ralenti ta requête ?
C'est à dire, la structure des tables, et ce que tu veux récupérer (avec si possible un jeu de données de quelques lignes).
Parceque là comme ça, à froid, et sans indentation, je t'avoue que j'ai même pas lu jusqu'au bout
Marsh Posté le 15-11-2006 à 10:24:26
Voici un topic sur le même sujet : http://forum.hardware.fr/forum2.ph [...] w=0&nojs=0
Il s'agit d'un gestionnaire de configurations (matériel et logiciel de machines). Voici mes tables :
Configurations (ConfigurationID, ConfigurationName, ConfigurationDate, ConfigurationVersion, ConfigurationInternalVersion, ConfigurationParentID)
Components (ComponentID, ConfigurationID)
ComponentsAttributes (ComponentAttributeID, ComponentID, AttributeID)
Attributes (AttributeID, AttributeName, AttributeType, AttributeValue, AttributeCompareType)
Qq règles :
1) une configuration ayant sa version interne à 1 (la première version d'une famille de configurations) à ConfigurationParentID à NULL
2) une configuration ayant sa version interne > 1 à un ConfigurationParentID <> NULL
3) toutes les configurations d'une même famille ont le même ConfigurationParentID (à l'exception de la configuration parente qui a son ConfigurationParentID à NULL, cf. règle 1)
4) une configuration a des composants, 1 composant n'appartient qu'à une conf
5) un composant à des attributs
6) un attribut peut être partagé par plusieurs composants
Avec ma requête bourrine, je veux récupérer la dernière configuration de chaque famille de confs (celle qui a sa version interne la plus élevée) qui a des composants dont l'un possède un attribut dont le nom = "%nom%" et a pour valeur = "%fd%" et l'autre possède un attribut dont le nom = "%modele%" et a pour valeur = "%drive%".
Marsh Posté le 15-11-2006 à 10:34:20
Voici la requête indentée et le résultat du EXPLAIN :
Code :
|
|
Marsh Posté le 15-11-2006 à 11:10:45
Ok.
Je vais tenter de réécrire ta requête étape par étape.
Au départ je voulais faire des EXISTS pour remplacer tes IN qui n'ont aucun rapport avec la problématique, mais je m'apperçois que des jointures simples font parfaitement l'affaire aussi pour la première du moins.
Question : qu'est-ce que "ComponentAttributeID" ?
Etape 1 :
Toutes les configurations qui un attribut "%nom%" ayant pour valeur "%fd"% ainsi qu'un attribut "%modele%" ayant pour valeur "%drive%"
|
Etape 2 :
Je veux l'ID de la dernière version de chaque configuration
|
Etape 3 :
Le mixe de tout ce joyeux bordel, et résultat final
|
Notes :
- Afin d'éviter le UNION inutile, il suffit de faire un configurationparentid = configurationid pour le premier niveau (nécessite la mise à jour des données)
- Pourquoi des like ?
- Une vue regroupant l'ensemble des attributs d'une configuration permettrait à la fois d'alléger la requête, mais aussi d'accélérer les traîtements
Marsh Posté le 15-11-2006 à 11:15:01
Par contre, le seul souci, c'est que je me rends compte que ma requête n'est pas vraiment plus simple que la tienne. Mise à part pour obtenir la liste des configs les plus récentes, on a la même... J'espère que ça va quand même aider à accélérer tout ça.
Marsh Posté le 15-11-2006 à 11:21:12
C'est bon, j'ai trouvé une requête qui va bien. Elle est largement encore optimisable mais au moins, elle va vite (pour l'instant)
Code :
|
Marsh Posté le 15-11-2006 à 11:27:32
je pige pas tout... y'as juste rajouté un niveau de sous-jointure pour récupérer les infos de la config à partir de la liste des id retrouvés par la sous-requête géante ?
et mysql va plus vite avec ça ?
quel sgbd de merde
Marsh Posté le 15-11-2006 à 11:27:48
En fait, j'ai l'impression que l'interpréteur de requêtes de MySQL effectuait d'abord les LIKE et créait une table temporaire contenant comme tous les champs du SELECT du début de la requête ainsi que tous les champs de la table attributes et ce pour chaque attribut correspondant aux critères des LIKE!
Marsh Posté le 15-11-2006 à 11:28:19
ps : c'est vrai que j'avais pas remarqué un truc... c'est quoi ce DISTINCT de misère ?
Marsh Posté le 15-11-2006 à 11:28:57
MagicBuzz a écrit : |
oui, ça va plus vite (sans commune mesure : < 1s contre plusieurs minutes et 1Go de RAM bouffé)
Marsh Posté le 15-11-2006 à 11:29:03
nan, cherchepas plus loin, c'est donc distinct sur 25 champs qui fout la merde... ça kill n'importe quel sgbd dès 4 lignes...
Marsh Posté le 15-11-2006 à 11:30:25
ceci dit, t'es sur que t'as des doublons si tu vire le distinct ?
parceque je ne vois pas où tu pourrais en avoir...
Marsh Posté le 15-11-2006 à 11:33:58
J'avais aps vu la requête que tu avais écrite. Je vais tester pour voir si elle va plus vite et surtout, si elle retourne bien le bon résultat (ça peut aider )... En tout cas, merci d'avoir consacré du temps à mon pb
ps : on m'a dit que les JOIN étaient préférables à des IN ou des EXISTS. Si ta requête peut me faire gagner encore du temps, ça sera utile quand il y aura une montée en charge de la BD...
Marsh Posté le 15-11-2006 à 11:37:07
MagicBuzz a écrit : ceci dit, t'es sur que t'as des doublons si tu vire le distinct ? |
Je peux virer les DISTINCT de mes sous-requêtes, mais il faut alors que j'en mette un dans le premier SELECT, sinon, j'ai des doublons.
Marsh Posté le 15-11-2006 à 11:39:44
MagicBuzz a écrit : Ok. |
l'utilisateur ne va pas connaître le nom exacte de chaque attribut d'une conf ni sa valeur (y'en a qui font plus de 100 caractères!)
Marsh Posté le 15-11-2006 à 11:51:31
Bon, après qq corrections de syntaxe de ta requête, elle donne bien de même résultat et à première vue, de manière plus rapide
Par contre, il faut que je fasse la même bidouille que pour la dernière requête que j'ai écrite (mettre ta requête en tant que sous-requête du SELECT c.ConfigurationName...) car ta requête ne renvoie que l'ID de la conf et celui de son parent, mais pas son nom, sa version, sa date, etc...
Marsh Posté le 15-11-2006 à 11:55:16
d'accord pour le coup du distinct. en fait, tu peux avoir plusieurs attributs contenant "drive" par exemple c'est ça ?
dans ce cas, en effet, le mieux à faire, c'est ton histoire de sous-requête avec un distinct sur le id uniquement.
sinon, à la limite, tu peux tenter de faire des EXISTS à la place des deux jointures sur les attributs. en effet, à la base, un EXISTS c'est ni plus ni moins qu'un test de validité d'une jointure (d'où ma grande préférence par rapport à un IN, qui exécute réellement la sous-requête et crée un lot de résultats).
=> ça évitera le DISTINCT, qui de toute façon n'est pas mieux que des EXISTS. A tester en tout cas.
Marsh Posté le 15-11-2006 à 12:00:31
en fait, ça donnerait :
|
Marsh Posté le 15-11-2006 à 12:01:27
au final, ça donne :
Code :
|
ouf, on y est!
Marsh Posté le 15-11-2006 à 12:02:45
ReplyMarsh Posté le 15-11-2006 à 15:06:34
MagicBuzz a écrit : nan, ça y est pas, teste ma dernière requête |
toujours le même pb. Dans ta table "cf", il n'y a que le champ ConfigurationID. Tu oublies de faire une jointure sur la table Configurations pour pouvoir récupérer les autres infos sur les confs. Au final, on retombe à peu de chose près sur ton avant-dernière requête que j'ai modifiée.
Je viens de tester, côté temps, c'est kif kif...
Marsh Posté le 15-11-2006 à 15:13:30
arf, oui, c'est vrai, j'ai oublié que mon cf est un sous-select en fait
ceci dit, tu peux rajouter les champs dans les cfs2 (la sous-requête) afin d'éviter de joindre pour rien avec cf à la fin.
mais bon, ça changera pas grand chose aux perfs
Marsh Posté le 15-11-2006 à 16:04:32
Après des tests plus poussés, il faut mettre un DISTINCT dans le 1er SELECT sinon, y'a des doublons
Marsh Posté le 15-11-2006 à 16:08:19
par contre, j'ai finalement utilisé ta dernière version de requête car celle-ci est construite dynamiquement par mon appli et entre autre, on active ou pas la récupération de la dernière version de chaque conf. Or les LIKE, qui sont aussi d'autres paramètres activables ou pas, sont inclus dans la sous-requête qui récupère la dernière version de conf ce qui est génant si l'utilisateur veut activer les LIKE et pas la récupération de la dernière version de conf. Ta dernière requête de prête mieux à ma construction dynamique ... et après test, ça marche nickel. Merci beaucoup pour ton aide.
Marsh Posté le 15-11-2006 à 16:21:26
une dernière question : c'est vrai que pour MySQL, mettre un index sur un champ où on utilise LIKE, ça ne sert à rien car LIKE n'utilise pss les index?
Marsh Posté le 15-11-2006 à 16:55:47
vérifie, mais j'abonde plutôt dans ce sens.
disons que l'index pourra être utilisé, mais n'apportera vraiment pas grand chose.
d'où le fait qu'il est très fortement déconseillé d'utiliser un like (et c'est pour ça que je t'ai demandé au début si c'était vraiment nécessaire )
Marsh Posté le 15-11-2006 à 17:49:59
oui, la recherche d'infos partielles dans des chaînes de caractères est nécessaire. Maintenant, à voir si l'utilisation de MATCH serait mieux?
Marsh Posté le 15-11-2006 à 17:52:36
qq chose me dit que j'aurais sans doute à nouveau besoin de tes lumières pour l'optimisation de 3 requêtes permettant de comparer de configurations entre elles : trouvers les attributs en +, en - et les attributs de même nom mais de valeur différente. J'ai déjà écrit les requêtes mais j'ai peur qu'avec la monté en charge de la BD, ça ne devienne très lent...
Marsh Posté le 16-11-2006 à 09:34:35
pour ces trucs là, je pense que le mieux, c'est de faire une requête "toute bête" qui ramène l'intégralité des attributs d'une config.
la lancer pour chacune des deux configs.
puis comparer dans ton appli, ou à la limite dans une procédure stockée.
mais en requête, à mon avis tu vas être hyper limité (trop de cas possibles, donc complexité accrue de ta requête) d'autant plus que pour l'affichage, tu risques de vouloir aussi les attributs communs.
Marsh Posté le 16-11-2006 à 09:35:59
sinon, le coup du MATCH pour retrouver des mots exacts, je suis pas certain que ce soit une super idée. MATCH (et dérivés) ça sert à faire des moteurs de recherche tapant dans de gros volumes d'infos. dans ton cas, ça me semble peu justifié, et surtout, MATCH peut faire de l'approximation d'orthographe, ce que tu ne veux pas forcément.
Marsh Posté le 08-11-2006 à 16:46:32
Bonjour,
Je sollicite votre aide pour écrire une requête SQL (pour MySQL) qui me paraît compliquée.
Voilà la structure de table que je souhaite interroger :
Configurations (ConfigurationID, ..., ConfigurationInternalVersion, ConfigurationDate, ConfigurationParentID);
ConfigurationID = clé primaire, entier
ConfigurationInternalVersion = n° de version de la configuration (interne à l'appli), entier allant de 1 à n
ConfigurationDate = date et heure à laquelle la configuration a été figée (NULL si elle ne l'est pas), datetime
ConfigurationParentID = ID de la configuration parente, NULL quand ConfigurationInternalVersion = 1
Ce que je veux récupérer, c'est pour chaque "famille" de configurations, la version interne la plus élevée ayant sa date non NULL.
ex :
| ID | InternalVer | Date | ParentID |
+----+-----------+-----------+----------+
| 1 | 1 |2006-11-01| NULL |
| 2 | 2 |2006-11-03| 1 |
| 3 | 1 |2006-11-02| NULL |
| 4 | 3 | NULL | 1 |
+---+------------+-----------+----------+
Je souhaite avoir en sortie :
| ID | InternalVer | Date | ParentID |
+----+-----------+-----------+----------+
| 2 | 2 |2006-11-03| 1 |
| 3 | 1 |2006-11-02| NULL |
+---+------------+-----------+----------+
Merci par avance
Message édité par rufo le 07-12-2006 à 14:25:51