And while I am at it, here was the original suggestion I had before finding that nice approach using find_in_set() and group_concat(). It uses a technique discussed here:
http://jimlife.wordpress.c
Showing as it is actually less code if you are stuck with not being able to create a view. Therefore, if you cannot create a view this may be an alternative.
Main Topics
Browse All Topics





by: mwvisa1Posted on 2009-09-11 at 10:52:59ID: 25311853
killer455,
sql/2007/0 3/29/emula ting-analy tic- aka-ra nking-func tions-with -mysql.htm l?page=1
========== ====
========== ====
Although IMHO this is not a 125 point question, I found the challenge quite interesting as my first thought was use UNION to give you the flexibility of getting all children on first level but X number of children on second level with LIMIT to control the number of records; however, LIMIT will not care if you get X number per category, it will just return X number.
With that in mind, I figured you needed a row_number() over() style syntax for MySQL like there is for Oracle and MS SQL and as far as I knew there was not one; hence, I took on the challenge.
My recommendation would be to create a view; however, I will show how to do this without one as well.
===== solution 1 : if can create view(s) ======
1. create view of child categories by parent.
create view vw_childcategories
as
select c.id, c.title, c.left_, c.right_,
c.`level`, p.id as parent
from category c
inner join category p
on c.left_ between p.left_ and p.right_
and c.right_ between p.left_ and p.right_
and c.`level` = coalesce(p.`level`,0)+1
;
2. Query for children of parent = 12 and union that with sub children.
select id, title,
left_, right_
level, parent
from vw_childcategories
where parent = 12
union
select id, title,
left_, right_
level, parent
from (
select cc.*,
/* ranking trick to simulate row_number() over() */
find_in_set(cc.id, p.children) as rank
from vw_childcategories as cc
inner join (
/* get listing of ids by parent */
select parent as id,
group_concat(id) as children
from vw_childcategories
group by parent
) as p on p.id = cc.parent
where exists ( /* children of parent 12 now become parents */
select 1
from vw_childcategories
where parent = 12
and id = cc.parent
)
) derived
where rank <= 1 /* change 1 to appropriate X number of sub children */
order by parent, id
;
Here is a good reference on what I am doing with group_concat and find_in_set:
http://onlamp.com/pub/a/my
==========================
Now if you can't create the view, then go with this:
===== solution 2 : if cannot create view(s) ======
/* get children */
select c.id, c.title, c.left_, c.right_,
c.`level`, p.id as parent
from category c
inner join category p
on c.left_ between p.left_ and p.right_
and c.right_ between p.left_ and p.right_
and c.`level` = coalesce(p.`level`,0)+1
where p.id = 12
union
select id, title, left_, right_, `level`, parent
from (
/* get sub children */
select c.id, c.title, c.left_, c.right_, c.`level`, p.id as parent,
/* ranking trick to simulate row_number() over() */
find_in_set(c.id, p_.children) as rank
from category c
inner join category p
on c.left_ between p.left_ and p.right_
and c.right_ between p.left_ and p.right_
and c.`level` = coalesce(p.`level`,0)+1
inner join (
/* get listing of ids by parent */
select p.id,
group_concat(c.id) as children
from category c
inner join category p
on c.left_ between p.left_ and p.right_
and c.right_ between p.left_ and p.right_
and c.`level` = coalesce(p.`level`,0)+1
group by p.id
) as p_ on p_.id = p.id
where exists (
select 1
from category c1
inner join category p1
on c1.left_ between p1.left_ and p1.right_
and c1.right_ between p1.left_ and p1.right_
and c1.`level` = coalesce(p1.`level`,0)+1
where p1.id = 12 and c1.id = p.id
)
) derivedb
where rank <= 1 /* change 1 to appropriate X number of sub children */
order by parent, id
==========================
Hope that helps.
Regards,
Kevin