2009-08-16 7 views
1

J'ai une table MySQL qui agit comme un ensemble imbriqué afin de contenir une hiérarchie de catégories. Le schéma de la table ressemble à:Recherche un ensemble imbriqué

CREATE TABLE IF NOT EXISTS `categories` (
    `id` int(11) NOT NULL auto_increment, 
    `name` varchar(200) NOT NULL, 
    `parent_id` int(11) default NULL, 
    `lft` int(11) default NULL, 
    `rgt` int(11) default NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `index_categories_on_parent_id_and_name` (`parent_id`,`name`) 
) 

lft et rgt définissent gauche et à droite limites d'un noeud (la façon dont fonctionne un ensemble imbriqué est que l'identifiant de chaque nœud se situe dans les limites de son parent) et parent_id spécifie le nœud parent . L'index unique permet d'avoir plusieurs catégories avec le même nom, tant qu'elles n'ont pas le même parent.

J'essaie de trouver une façon de trouver un nœud spécifique dans l'ensemble, sur la base de hiérarchie. Par exemple, si je cherche foo/bar/baz, je veux récupérer le nœud nommé baz, dont le parent est nommé bar, dont le parent est nommé foo. Évidemment, je ne peux pas faire une recherche par nom, car il pourrait y avoir plusieurs catégories avec le même nom.

La façon dont je peux penser à faire ceci est de trouver la catégorie supérieure, puis trouver chaque catégorie suivante avec le nom donné dont l'ID parent est celui de la catégorie précédemment trouvée, mais cela ne me semble pas très efficace. Existe-t-il une meilleure façon de rechercher un ensemble imbriqué?

Répondre

1

Je ne crois pas qu'il y ait un moyen parfaitement propre et efficace pour faites ceci avec des ensembles imbriqués. Stocker une liste des ancêtres du nœud dans une colonne dénormalisée fournirait cela efficacement, mais je ne suggère pas de l'implémenter.

Il existe une méthode ok'ish mais, ce qui est une requête et sera facilement atteint l'indice que vous avez déjà. Vous regardez une jointure pour chaque niveau de profondeur du noeud cible.

Pour votre exemple foo-bar-baz

select c3.*
from categories c1
inner join categories c2 on c2.parent_id = c1.id AND c2.name = 'bar'
inner join categories c3 on c3.parent_id = c2.id AND c2.name = 'baz'
where c1.name = 'foo'

Ce n'est pas le plus grand, mais il est probablement votre meilleur pari à moins que vous voulez aller à l'effort de stocker un tas d'informations dénormalisé. Il est assez simple de générer le code SQL dans le code.

0

J'ai déjà vu ça dans un projet php qui m'a été remis, et bon, c'est juste mauvais .. si vous le pouvez, divisez-le en au moins 2 tables; avoir au moins 1 pour les catégories et 1 pour les articles, de sorte que vous pouvez rejoindre .. de toute façon vous aurez besoin de faire plusieurs requêtes j'ai peur

+0

Je ne pense pas que vous ayez bien compris la question; J'ai des tableaux séparés pour les catégories et les articles, mais je ne suis pas préoccupé par les articles ici. Je veux juste obtenir l'ID d'une catégorie spécifique, basée sur une hiérarchie donnée. –

1
TopVar = 'foo' 
MidVar = 'bar' 
BotVar = 'baz' 

SELECT D0.* 
FROM categories D0, categories D1, categories D2 
WHERE D0.name = :BotVar 
    AND D0.lft > D1.lft 
    AND D0.rgt < D1.rgt 
    AND D1.name = :MidVar 
    AND D1.lft > D2.lft 
    AND D1.rgt < D2.rgt 
    AND D2.name = :TopVar; 

-Al.

+0

Petit conseil qui m'a pris waaaaaaay trop long pour travailler quand je travaille avec des ensembles imbriqués. Pour rechercher les descendants d'un noeud, reportez-vous uniquement à la colonne 'lft' de votre requête. Si vous cliquez sur les colonnes de gauche et de droite, votre requête contiendra deux conditions de plage (c'est-à-dire une certaine résistance d'optimisation). Si vous ne faites référence qu'à gauche, ce sera une condition de plage unique, qui peut être entièrement couverte par un index sur lft. Disons que nous voulons que les descendants du noeud A qui a laissé 12 et à droite 155: select * from categories c where c.lft between 12 and 155 Michael

Questions connexes