views:

49

answers:

1

Hey guys,

I'm trying to create a efficient query which will retrieve 'projects' from a database and all the relevant 'tags' which have been assigned to that particular project.

My table setup mimics that of wordpress, and is a tad complicated:

term
- Where I define the tags name, slug etc

term_relationships
- Links the project to a term in the above table.

term_taxonomy
- Defines the taxonomy of each of terms in the term table e.g. 'category', 'tag'.

So, what I need to do is first query the term_taxonomy table for any entries with the taxonomy 'tag', then filter these results so that only the terms specified in term_relationships are returned. Finally I then need to query the term table to return the relevant tags.

My question is which is fastest way of doing this? I imagine it would be best to split it into multiple queries eg. Query 1: retrieve project, Query 2: retrieve project tags but please consider I may have up to 20-30 projects per page.

PS: I know it'd be far easier to just create a new table specifically for tagging, but I'd like to confine it to my current setup for now.

Any help would be much appreciated, as this is driving me mad!

+2  A: 
select     p.id
,          p.name 
,          group_concat(t.text order by t.text) as tags
from       project              p
left join  term_relationships   tr
on         p.id               = tr.project_id
left join  term                 t
on         tr.term_id         = t.id 
left join  term_taxonomy        tt
on         t.term_taxonomy_id = tt.id
and        'tag'              = tt.type
group by   p.id

note that the group by clause does not include all selected columns from project - other databases will bitch about this, but Msql won't. Also note that it is perfectly valid to do so in this case, as p.id is the primary key of project, so there can be only one distinct name value per p.id.

One word of caution RE GROUP_CONCAT():

The result is truncated to the maximum length that is given by the group_concat_max_len system variable, which has a default value of 1024. The value can be set higher, although the effective maximum length of the return value is constrained by the value of max_allowed_packet. The syntax to change the value of group_concat_max_len at runtime is as follows, where val is an unsigned integer:

SET [GLOBAL | SESSION] group_concat_max_len = val;

See http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat

So decide in advance how large the list of tags may be, and set @@group_concat_max_len to that value.

Roland Bouman
Thanks, I never knew about the group_concat function, it's great! The only problem here is that I also need to get the slug for each tag, as well as the name. Is there anyway to retrieve two columns from the terms table rather than just the name?
Hanpan
Hanpan, yes. You can either write a second `GROUP_CONCAT` expression, and use your language (php?) to explode them into arrays, or you can write some expression to combine tag and slug directly. You could even write: `GROUP_CONCAT('<a href="', slug, '">', text, '</a>' ORDER BY text)` but personally I would not do that - I feel its nicer to solve this in php, and use the database only to retrieve data. One word of caution. Since you're new to `GROUP_CONCAT` I should point that it truncates the result in case the result is longer than `group_concat_max_len`. See my edited answer for more details
Roland Bouman