Requête SQL principale
La requête centrale du fichier index.php alimente le tableau de consultation. Elle est conçue pour éliminer les doublons issus des jointures multiples (prix, concurrents, contrats).
Principe anti-doublons
Le problème classique avec plusieurs jointures 1-N est la multiplication des lignes. La solution mise en place est de pré-sélectionner un seul enregistrement pour chaque relation N via des sous-requêtes de sélection de premier/dernier ID.
Sous-requêtes clés
(A) Dernier prix actif
LEFT JOIN (
SELECT rf2.ID_REFERENCE, MAX(pa2.ID_PRIX) AS ID_PRIX_DERNIER
FROM REFERENCES_FOURNISSEURS rf2
JOIN PRIX_ACHAT pa2 ON pa2.ID_REF_FOURNISSEUR = rf2.ID_REF_FOURNISSEUR
AND (pa2.DATE_FIN IS NULL OR pa2.DATE_FIN >= CURDATE())
WHERE rf2.ACTIF = 1
GROUP BY rf2.ID_REFERENCE
) last_ref ON last_ref.ID_REFERENCE = r.ID_REFERENCE
→ Pour chaque référence, on ne garde que le prix le plus récent (ID_PRIX le plus élevé).
(C) Premier fournisseur actif
LEFT JOIN (
SELECT rf2.ID_REFERENCE, MIN(rf2.ID_REF_FOURNISSEUR) as ID_REF_FOURNISSEUR_PREMIER
FROM REFERENCES_FOURNISSEURS rf2
WHERE rf2.ACTIF = 1
GROUP BY rf2.ID_REFERENCE
) first_fourn ON first_fourn.ID_REFERENCE = r.ID_REFERENCE
→ Le fournisseur est récupéré indépendamment du prix, sur le premier enregistrement actif.
(D/E) Premier concurrent actif
LEFT JOIN (
SELECT rc2.ID_REFERENCE, MIN(rc2.ID_REF_CONCURRENT) AS ID_REF_CONCURRENT_PREMIER
FROM REFERENCES_CONCURRENTS rc2
WHERE rc2.ACTIF = 1
GROUP BY rc2.ID_REFERENCE
) first_concurrent ON first_concurrent.ID_REFERENCE = r.ID_REFERENCE
(F/G) Premier contrat actif
LEFT JOIN (
SELECT cp2.ID_REFERENCE, MIN(cp2.ID_CONTRAT_PRIX) AS ID_CONTRAT_PRIX_PREMIER
FROM CONTRATS_PRIX cp2
JOIN CONTRATS co2 ON cp2.ID_CONTRAT = co2.ID_CONTRAT AND co2.STATUT = 1
GROUP BY cp2.ID_REFERENCE
) first_contract ON first_contract.ID_REFERENCE = r.ID_REFERENCE
Indicateur HAS_CONTRAT
Le champ HAS_CONTRAT est calculé en ligne via un CASE :
CASE
WHEN cp.ID_CONTRAT IS NOT NULL THEN 'O'
ELSE 'N'
END as HAS_CONTRAT
Pagination
La pagination est gérée en PHP avec trois paramètres :
$items_per_page = isset($_GET['limit']) ? intval($_GET['limit']) : 10;
$current_page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
$offset = ($current_page - 1) * $items_per_page;
Le total est calculé via une requête COUNT(*) enveloppant la requête principale :
$count_sql = "SELECT COUNT(*) FROM (" . $sql . ") as c";
Puis la requête principale est complétée :
ORDER BY r.CODE_MACHINE, f.TRIGRAMME
LIMIT $items_per_page OFFSET $offset
Recherche globale
La fonction applyGlobalSearchOptimized() ajoute une condition AND (...) avec 16 paramètres de recherche LIKE couvrant :
- Champs directs :
CODE_MACHINE,DESIGNATION,NOM_ACTEUR,CATEGORIE,SOUS_CATEGORIE,QUALITE,RAISON_SOCIALE,TRIGRAMME,REF_EXTERNE,PLATEFORME,NOM_CONTRAT,CLIENT - Sous-requêtes
EXISTSsurREFERENCES_CONCURRENTSpourREF_EQUIVALENTE,REF_REMPLACEMENT, et les acteurs concurrents
Filtres multi-valeurs
La fonction applyNewFilters() génère dynamiquement des clauses IN (?, ?, ...) avec autant de placeholders que de valeurs sélectionnées :
$placeholders = str_repeat('?,', count($filter_categorie) - 1) . '?';
$sql .= " AND c.CATEGORIE IN ($placeholders)";
$params = array_merge($params, $filter_categorie);
Ordre d'application
1. applyGlobalSearchOptimized() → Recherche globale
2. applyNewFilters() → Filtres dropdown
3. COUNT(*) sur la requête → Pagination
4. ORDER BY + LIMIT + OFFSET → Résultat final