Je suis coincé avec un problème de performance:Optimize requête SQL avec plusieurs jointures internes sur une même table
Un magasin a un filtre à l'article avec des catégories « couleur », « taille », « genre » et « fonction ». Tous ces détails sont stockés dans une table article_criterias
, qui ressemble à ceci:
La disposition de table de article_criterias
est; ce tableau a environ 36.000 lignes:
article_id | group | option | option_val
100 | "size" | "35" | 35.00
100 | "size" | "36" | 36.00
100 | "size" | "36½" | 36.50
100 | "color" | "40" | 40.00
100 | "color" | "50" | 50.00
100 | "gender" | "1" | 1.00
101 | "size" | "40" | 40.00
...
Nous avons une requête SQL qui est construit dynamiquement, en fonction des critères qui sont actuellement sélectionnés. La requête est bonne pour 2-3 critères, mais sera très lente lors de la sélection de plus de 5 options (chaque INNER JOIN supplémentaire double le temps d'exécution)
Comment pouvons-nous rendre ce SQL plus rapide, peut-être même remplacer les jointures internes avec un concept plus performant?
Ceci est la requête (la logique est correcte, juste la performance est mauvaise):
-- This SQL is generated when the user selected the following criteria
-- gender: 1
-- color: 80 + 30
-- size 36 + 37 + 38 + 39 + 42 + 46
SELECT
criteria.group AS `key`,
criteria.option AS `value`
FROM articles
INNER JOIN article_criterias AS criteria ON articles.id = criteria.article_id
INNER JOIN article_criterias AS criteria_gender
ON criteria_gender.article_id = articles.id AND criteria_gender.group = "gender"
INNER JOIN article_criterias AS criteria_color1
ON criteria_color1.article_id = articles.id AND criteria_color1.group = "color"
INNER JOIN article_criterias AS criteria_size2
ON criteria_size2.article_id = articles.id AND criteria_size2.group = "size"
INNER JOIN article_criterias AS criteria_size3
ON criteria_size3.article_id = articles.id AND criteria_size3.group = "size"
INNER JOIN article_criterias AS criteria_size4
ON criteria_size4.article_id = articles.id AND criteria_size4.group = "size"
INNER JOIN article_criterias AS criteria_size5
ON criteria_size5.article_id = articles.id AND criteria_size5.group = "size"
INNER JOIN article_criterias AS criteria_size6
ON criteria_size6.article_id = articles.id AND criteria_size6.group = "size"
INNER JOIN article_criterias AS criteria_size7
ON criteria_size7.article_id = articles.id AND criteria_size7.group = "size"
WHERE
AND (criteria_gender.option IN ("1"))
AND (criteria_color1.option IN ("80", "30"))
AND (criteria_size2.option_val BETWEEN 35.500000 AND 36.500000)
AND (criteria_size3.option_val BETWEEN 36.500000 AND 37.500000)
AND (criteria_size4.option_val BETWEEN 37.500000 AND 38.500000)
AND (criteria_size5.option_val BETWEEN 38.500000 AND 39.500000)
AND (criteria_size6.option_val BETWEEN 41.500000 AND 42.500000)
AND (criteria_size7.option_val BETWEEN 45.500000 AND 46.500000)
vérifier l'indexation, les champs utilisés dans INNER JOIN état, et où l'état. L'indexation accélérera la recherche. –
Le problème auquel vous êtes confronté est "Valeur d'attribut d'entité" ou "EAV". Vous avez l'anti-pattern de l'enfer ici en ce qui concerne les bases de données relationnelles. Vous avez besoin d'une base de données différente ou d'une approche différente. – LoztInSpace
Même si je suis d'accord avec l'astronaute, êtes-vous sûr que la requête fonctionne vraiment comme prévu?Votre requête va actuellement trouver des chaussures pour un sexe spécifique qui existe en couleurs 80 OR (!) 30 et existe en tailles 36 ET (!) 37 (et le reste), donc pas de résultat si vous l'avez dans toutes les tailles sauf 39. Vous pourrait déjà économiser beaucoup de jointures/temps ici si vous utilisez 'ou' pour les tailles aussi, semblable aux couleurs: seulement joindre une fois, par exemple en utilisant 'join ... criteria_size2 sur ... criteria_size2.group =" size "et (criteria_size2.option_val entre 35.5 et 36.5 ou criteria_size2.option_val entre 36.5 et 37.5 ou ...)'. – Solarflare