2010-03-23 7 views
3

J'ai un système par lequel un utilisateur peut voir les catégories auxquelles il s'est abonné individuellement, ainsi que celles qui sont disponibles dans la région à laquelle il appartient par défaut.Comment rendre cette requête SQL plus efficace? PHP

Ainsi, les tableaux sont les suivants: Catégories UsersCategories RegionsCategories

J'interrogent les bases db pour toutes les catégories dans leur région, ainsi que toutes les catégories individuelles qu'ils ont souscrites.

Ma requête est la suivante:

 Select * FROM (categories c) 
     LEFT JOIN users_categories uc on uc.category_id = c.id 
     LEFT JOIN regions_categories rc on rc.category_id = c.id 
     WHERE (rc.region_id = ? OR uc.user_id = ?) 

Je crois au moins c'est la requête, je crée à l'aide de la couche ORM de gâteau, de sorte que l'exact est:

 $conditions = array(
          array("OR" => array (
           'RegionsCategories.region_id' => $region_id, 
           'UsersCategories.user_id' => $user_id 
          ) 
     )); 
     $this->find('all', $conditions); 

Ce tour pour être incroyablement lent (parfois environ 20 secondes environ, chaque table a environ 5 000 lignes). Mon design est-il en faute ici?

Comment puis-je récupérer les catégories individuelles des utilisateurs et celles de leur région en une seule requête sans que cela ne prenne de temps?

Merci!

Répondre

2

Assurez-vous que les colonnes categories.id, users_categories.category_id, users_categories.user_id, regions_categories.category_id et regions_categories.region_id sont toutes indexées.

+0

Vous êtes une star, travaillé comme un charme. J'aurais probablement dû le savoir. Ta v.much. –

+0

Oui, j'ai eu des requêtes qui vont de près d'une minute à un dixième de seconde en indexant les bons champs. Content que ça a marché! – jmans

1

En supposant que les suggestions de jmans étaient déjà implémentées;

IIRC requêtes MySQL ne peut utiliser un index à la fois, et ainsi ou peuvent être coûteux et il peut être plus rapide de le diviser en deux requêtes et UNION eux, permettant à chaque sous-requête d'utiliser un autre indice

1

Essayez celui-ci:

select * from 
(
select category_id from users_categories where user_id = ? 
union 
select category_id from region_categories where region_id = ? 
) as q 
inner join categories c on c.id = q.category_id 

Je suppose que cela fait le même travail, mais ne pas utilise ou (ce qui est une chose à éviter avec la plupart des SGBDR) - laissant donc les indices d'utilisation du serveur de manière plus efficace. De cause, comme l'a déjà noté jmans, vous devriez envisager d'indexer vos données.

Questions connexes