Bonjour à tous,
Situation
Je suis chef de projet pour un logiciel de comptabilité immobilière en ligne.
Nous avons ~200 tables, la plupart étant d'une manière ou d'une autre reliée entre-elles.
Lorsque je vais chercher une donnée (exemple : une facture), je dois rapidement aller dans une trentaine de tables (exemple : coordonnées du fournisseur, les articles, les prix, on a une table pour gérer toutes les références, une pour gérer les taux de TVA, etc.), avec des sous-requêtes sur trois niveaux, etc.
Je me suis donc retrouvé avec un dilemme :
1/ Soit faire une seule grande requête qui va tout chercher ;
2/ Soit faire une multitude de petites requêtes.
La solution 1 m'a semblé la plus évidente : les jointures. Nous travaillons avec Symfony et Doctrine.
Le problème
Le problème est que j'arrive très très très rapidement à des requêtes SELECT MySQL de plus de 1000 lignes (en copier/coller), parfois sur trois niveau (Subquery) ; difficile cependant ici d'expliquer pourquoi sans vous perdre. C'est vraiment notre logique métier qui veut ça : tout est découpé, avec des répartitions sur des centaines de clients, etc.
Je ne peux pas mettre en cache le résultat de la totalité de cette requête puisque plein d'informations peuvent-être modifiées d'ailleurs (exemple : coordonnées du fournisseur).
Exemple concret
J'ai dévéloppé (enfin demandé à un collègue) rapidement un mini-site (totalement inutile, c'est pour illuster) : http://burger.gaylord-poillon.com/commande/
C'est un site internet qui permet d'ajouter une commande de frites et nuggets. Chaque commande a un nombre de [ frites + poids ] et de [ nuggets + poids ]. Le but étant d'afficher le poids total de la commande.
Pour avoir ce résultat en une fois, nous avons du faire une requête, elle est très simple à comprendre (Et rebalancer tout ça avec SetResultMapping pour que les entités soient bien hydratés) :
SELECT
*,
(poids_frites + poids_nuggets) AS poids_total
FROM
(
SELECT
m.id,
m.id AS m_id,
c.id AS c_id,
c.nom,
c.prenom,
(
SELECT
COALESCE(
SUM(f.poids),
0
) AS poids_frites
FROM
frite f
WHERE
f.menu_id = m_id
) AS poids_frites,
(
SELECT
COALESCE(
SUM(n.poids),
0
) AS poids_nuggets
FROM
nugget n
WHERE
n.menu_id = m_id
) AS poids_nuggets
FROM
menu m
INNER JOIN client c ON m.client_id = c.id
) tmp
Bon la, ça va, c'est pour illuster ... mais dans mon cas, les requêtes font 20 fois ça.
Comment feriez-vous, vous, pour optimiser cela ?
J'ai envisagé de ne jamais faire de jointure, et d'aller chercher les données une à une, toujours une table à la fois, et mettre en cache donnée par donnée mais bon, c'est super sale et je me dis que j'aurais bien une surprise qui va me tomber dessus (ça arrive toutes les semaines ...)
Merci d'avance :)
PS : Notre projet fonctionne très bien, mais je commence à porter à la réflexion l'optimisation et la relecture.