views:

46

answers:

1

I have three tables, categories, tags, and taggings. The categories table is put together in the fashion of a nested set, with relevant columns id, lft, rgt, and parent_id. Tags has id and name. Taggings has tag_id and taggable_id, which refers to categories.id.

If possible, I'd like for one query which returns in a field, say tag_list, a string containing a category's and all of its ancestors' tags. So given the following schema:

Categories

id | parent_id | lft | rgt
 1 |      NULL |   1 |   6
 2 |         1 |   2 |   5
 3 |         2 |   3 |   4

Tags

id | name
 1 | cool
 2 |  rad
 3 | soup

Taggings

id | tag_id | taggable_id
 1 |      1 |           1
 2 |      2 |           2
 3 |      3 |           3

I'd like for the query SELECT ??? FROM categories to return:

id |      tag_list
 1 |          cool
 2 |      rad cool
 3 | rad cool soup

Background info: I'm running Rails, and I'm using Thinking Sphinx for search and awesome_nested_set for nesting. I have a table called categories, which has many tags in a has_many_through relationship.

+1  A: 
SELECT node_id, group_concat(name SEPARATOR ' ')
FROM taggings INNER JOIN tags ON taggings.tag_id=tags.id
INNER JOIN (SELECT node.id AS node_id, parent.id AS parent_id
FROM categories AS node,
categories AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt) subcategories
ON subcategories.parent_id=taggings.taggable_id
GROUP BY (node_id);

Well, I for one learned a thing or three doing that :)

EDIT: I did not check it against mysql, only SQLite, so the syntax might not be 100%.

MPelletier
Thanks! This works wonderfully, but I'm afraid your SQL-fu is sufficiently above my level that I'm not able to follow along with your logic once grouping rolls around. I understand how the parenthetical query in the middle constructs a mapping of categories and ancestors, and I can see how that is helpful in determining a category's inherited tags, but I'm at a loss as to exactly how the rest of it fits in. If you would be so kind as to help me out on the grouping steps, I would be grateful.
Steven Xu
A follow up on my previous comment... never mind! I understand it now. Brilliant work!
Steven Xu
The nested-sets was the new part to me. To make sense of this, I had to change the ID's a bit because it was just too confusing, so I took tag.id and tagggings.tag_id and multiplied them by 10.For grouping, what I did was use a group_concat, which essentially works like any group function (max, sum, etc), only it concatenates on identity. Try it on a simpler schema, a single table with repeated ID's.
MPelletier
I'm not sure what you mean by multiplying things by 10. Care to elaborate on that? Also, I feel it's semantically more appropriate to refer to what you call `parent_id` or `parent` as `ancestor_or_self_id` and `ancestor_or_self` or something on those lines.
Steven Xu
Yes, I think you're right on the dot with "ancestor_or_self_id". As for multiplying my 10, it's just that I kept seeing the same IDs from one table to another, try as I might to focus on the structure and not the data itself, so I removed that distraction by just changing the ID's a bit, but retained the same structure.
MPelletier