Administrateur PHPfrance |
3088 Messages
21 sept. 2007, 00:03
Salut et désolé d'arriver après la bataille
Avant d'essayer de comprendre la structure, j'ai voulu reformatter la requête d'après les conventions que j'utilise --des JOIN plutôt que des virgules, et les conditions de jointures dans le JOIN ... ON plutôt que WHERE-- et il y a un truc que j'ai mal recopié ou qui doit être incorrect dans la requête originale.
[Note: en fait non, en recopiant l'exemple j'ai compris où j'avais oublié un truc, voir plus bas
] Je précise que je fais ça avant de chercher à comprendre la structure pour démarrer avec une point de vue neutre, comme le
parser de MySQL quand il commence à lire la requête. Mon but ici n'étant pas de "trouver la solution", puisque
fab semble l'avoir déjà trouvée, mais plutôt de la vérifier.
J'ai pris la requête originale, remplacé les colonnes du SELECT, changé les virgules en JOIN et donné des aliases aux tables
Code : Tout sélectionner
SELECT *
FROM users_has_contrats uhc
JOIN gammes g
JOIN (
contrats c
LEFT JOIN contrats_temp ct ON c.id = ct.id
)
JOIN (
users u
LEFT JOIN comments_has_correspondants chc ON u.id = chc.id_user
)
WHERE c.id = uhc.id_contrat
AND u.id = uhc.id_user
AND u.droits_id = 7
AND c.gammes_id = g.id
AND g.id = chc.id_gamme
Puis j'ai mis les conditions de jointures dans les JOIN
Code : Tout sélectionner
SELECT *
FROM users_has_contrats uhc
JOIN gammes g ON g.id = chc.id_gamme
JOIN (
contrats c
LEFT JOIN contrats_temp ct ON c.id = ct.id
) ON c.id = uhc.id_contrat AND c.gammes_id = g.id
JOIN (
users u
LEFT JOIN comments_has_correspondants chc ON u.id = chc.id_user
) ON u.id = uhc.id_user
WHERE u.droits_id =7
À partir de là, puisque je vois que ta seule condition de WHERE est une constante, je devine que c'est à partir de cette table qu'on doit chercher les données donc je décide de mettre la table
users en tête de liste dans le FROM et mettre à plat le reste. De plus, même si ça n'a pas d'incidence sur la requête, je remets certaines comparaisons dans l'ordre. Par exemple, au lieu de "a JOIN b ON a.col = b.col" j'écris "a JOIN b ON b.col = a.col" parce que je le trouve plus
naturel si on le lit à voix haute : "table a jointe à la table b là où b est égal à a". Mais je digresse :
Code : Tout sélectionner
SELECT *
FROM users u
JOIN users_has_contrats uhc
ON uhc.id_user = u.id
JOIN gammes g
ON g.id = chc.id_gamme /* ?? */
JOIN contrats c
ON c.id = uhc.id_contrat
AND c.gammes_id = g.id
LEFT JOIN contrats_temp ct
ON ct.id = c.id
LEFT JOIN comments_has_correspondants chc
ON chc.id_user = u.id
WHERE u.droits_id = 7
Là mon problème c'est que la condition de jointure de
gammes est une colonne dans une table jointe en LEFT JOIN donc ça disqualifie les lignes où il n'y a pas de correspondance dans
comments_has_correspondants, le transformant en INNER JOIN. C'est là que je comprends que je n'ai pas mis les tables dans le bon ordre et les conditions de jointure dans le bon sens. Je vous passe les permutations et j'en arrive à
Code : Tout sélectionner
SELECT *
FROM users u
JOIN users_has_contrats uhc
ON uhc.id_user = u.id
JOIN contrats c
ON c.id = uhc.id_contrat
JOIN gammes g
ON g.id = c.gammes_id
LEFT JOIN contrats_temp ct
ON c.id = ct.id
LEFT JOIN comments_has_correspondants chc
ON chc.id_user = u.id
AND chc.id_gamme = g.id
WHERE u.droits_id = 7
Si je ne me suis pas planté, cette dernière requête devrait être équivalente à la tienne,
fab. J'ai retiré les parenthèses qui forçaient l'ordre de jointure mais je ne crois pas que cela change le résultat tout en donnant à MySQL un petit peu plus de libertés pour traverser les tables dans l'ordre le plus efficace.
Ce serait cool si tu pouvais vérifier que ça donne bien les mêmes résultats et me copier/coller la sortie d'un
EXPLAIN EXTENDED + SHOW WARNINGS (ou EXPLAIN tout court si ton serveur ne le supporte pas) des deux requêtes, merci
Un point intéressant, qui souligne l'utilité de certaines conventions de formattage et notamment de se placer du point de vue du serveur, c'est que j'ai pu me faire une bonne idée de la structure de la base rien qu'en lisant la requête SQL à voix haute :
Pour chaque users dont les droits sont 7,
on cherche qui possède un contrat dans users_has_contrats par id_user,
on met la main sur ce contrat dans contrats par id_contrat,
on récupère les informations de la gamme du contrat dans gammes par gammes_id,
puis on récupère des informations facultatives sur les contrats dans contrats_temp
et des commentaires sur les users dans la gamme du contrat dans comments_has_correspondants par id_user,id_gamme
Bien sûr, ça ne fonctionne que si la base a été correctement structurée, ce qui semble être le cas ici
[Note : je maintiendrai cette opinion même dans l'éventualité où je me serais planté au recopiage
]