Besoin conseil pour recherche multicritères d'utilisateurs

Eléphant du PHP | 59 Messages

10 janv. 2011, 13:28

Bonjour,

Voilà mon problème j'essaie de mettre dans mon site une recherche multicritères des utilisateurs. ex: par age, genre, goûts musicaux, loisir....

Voilà comment j'ai organisé ma table. 3 colonnes: id/cat/value

id= id de l'utilisateur
cat= catégorie rechercher (ex: 1 =age, 3=goûts musicaux....)
value= value de la sous-catégorie (1= jazz, 2= rock....)

J'ai choisi cette solution, plutôt que de créer une ligne par utilisateur avec toutes les sous-catégories, de cette façon si dans le futur je dois ajouter des sous-catégories je n'aurais pas besoin d'ajouter de nouvelles colonnes.

Voilà à quoi ressemble une requête:
SELECT * FROM  `user` WHERE (cat =3 AND val IN  (1, 2, 3, 5 )) OR (cat =4 AND val =6 )
GROUP BY id HAVING COUNT( id ) =5
Mon problème c'est dès que j'arrive à plus d'un million de lignes dans ma table la requête devient très longue (environ 10 sec), et si j'ajoute plus de critères à ma requête c'est encore pire.
J'ai essayé plusieurs combinaisons d'index. Clé primaire sur id/cat/value ou clé primaire sur id index sur cat/value ....

Donc je voulais savoir si d'après vous je m'y prend de la bonne manière.
PS: je suis sur Mysql

devlop78
Invité n'ayant pas de compte PHPfrance

10 janv. 2011, 20:29

Regarde du côté des index pour accélérer la recherche, et une pagination car il doit certainement te retourner beaucoup de résultats.

Eléphant du PHP | 59 Messages

11 janv. 2011, 02:16

J'ai essayé les index et la pagination mais rien à faire.

Est ce qu'il y en a qui ont déjà utilisé cette méthode?

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 13231 Messages

11 janv. 2011, 08:42

La méthode que tu utilises est la bonne, le problème, c'est la volumétrie ... et le SGBD.
MySQL galère vite avec de grosses tables.

La solution tourne pourtant autour des index.
Essaye de faire un EXPLAIN PLAN de ta requête pour voir ce qu'il faut, ou ne fait pas :
exécute ton select en rajoutant "EXPLAIN" juste avant le "SELECT" et poste nous le résultat.
(il faut que tu nous montres également les index qui sont en place lorsque tu exécutes cette requête.

Mais, ne pas oublier que sur une telle volumétrie, la requête ne sera jamais instantanée.
Connaître son ignorance est la meilleure part de la connaissance
Pour un code lisible : n'hésitez pas à sauter des lignes et indenter

twitter - site perso - Github - Zend Certified Engineer

Eléphant du PHP | 59 Messages

11 janv. 2011, 10:36

Salut,

J'ai déjà vérifié mes requêtes avec les explain. Elles prenaient bien en compte les index.
J'ai essayé dans tous les sens, donc comme tu le dis Zeus le problème doit venir de Mysql.

Postgre serait t'il plus efficace ou faut t'il que je m'oriente directement vers Oracle et autres solution payante?

Merci

Modérateur PHPfrance
Modérateur PHPfrance | 2575 Messages

11 janv. 2011, 12:56

Bonjour,

Pour optimiser le temps de réponse de ta requête SELECT dans MySQL il faut utiliser la clause LIMIT:
SELECT * FROM  `user` WHERE (cat =3 AND val IN  (1,  2, 3, 5 )) OR (cat =4 AND val =6 )
GROUP BY id HAVING COUNT( id ) =5
LIMIT 0, 30
Ce qui permet l'exécution par lot exemple de 0 à 30 enregistrements, puis de 31 à N etc...
Dans ton cas un traitement par lot de 30 enregistrements à la fois permet de traiter 30 id user par résultat de lot.
--------//////----//---//----//////
-------//---//----//---//----//---//
------//////----//////-----//////
-----||--------||--||---||
Prendre le recul n'est pas une perte de temps.


ps: Affrontez moi dans l'arène

devlop78
Invité n'ayant pas de compte PHPfrance

11 janv. 2011, 23:45

Bonjour,

Pour optimiser le temps de réponse de ta requête SELECT dans MySQL il faut utiliser la clause LIMIT:
SELECT * FROM  `user` WHERE (cat =3 AND val IN  (1,  2, 3, 5 )) OR (cat =4 AND val =6 )
GROUP BY id HAVING COUNT( id ) =5
LIMIT 0, 30
Ce qui permet l'exécution par lot exemple de 0 à 30 enregistrements, puis de 31 à N etc...
Dans ton cas un traitement par lot de 30 enregistrements à la fois permet de traiter 30 id user par résultat de lot.

Apparemment, il a déjà essayé, sans succès. Ca peut ne rien faire (si MySQL fait le limite après avoir vu toute la table), comme ça peut largement aider. Mais j'imagine que dans le cas où le limite ne convient pas, une procédure stockée pourrait éventuellement traiter la table par petit bout et s'arrêter. Mais je n'y crois guère plus.

PostgreSQL est réputé comme tenant mieux la charge, Oracle lui, n'est conseillé justement que pour de gros volumes ! Enfin, ce sont des oui dire, puisque, perso, je souhaiterais m'orienter vers PostgreSQL qui est gratuit, plutôt puissant, et surtout offre de superbes fonctionnalités INDISPENSABLES à tout SGBDR (les contraintes CHECK par exemple ...).