Showing posts with label cte. Show all posts
Showing posts with label cte. Show all posts

Monday, March 12, 2012

How can we control the recursion in CTE?

Hi,

I would like to have some sort of control to recursive CTE, i.e. the query result should be generated level by level.

By default, it looks like this:

levels parentid, categoryid category name

--

1 0 1 aaa

2 1 100 bbb

2 1 101 ccc

2 1 102 ddd

3 102 200 xxx

3 102 201 yyy

4 201 300 zzz

5 300 400 qqq

3 101 210 ppp

....

How can I control the JOIN so that sql server query engine will process join level by level? the CTE I used as following:

with cte(levels, parentid, categoryid, title) as (

select 1,

parentid,

categoryid,

title

from categories

where parentid = 0

union all

select p.levels+1,

c.parentid,

c.categoryid,

c.title

from cte p join categories c on p.categoryid = c.parentid

)

select *

from cte

Thanks

Bill:

If what you want is just to sort the data by level, a simple order by will do -- something like:

declare @.categories table
( levels int,
parentId int,
categoryId int,
title varchar(10)
)
insert into @.categories
select 1,0,1,'aaa' union all
select 2,1,100,'bbb' union all
select 2,1,101,'ccc' union all
select 2,1,102,'ddd' union all
select 3,102,200,'xxx' union all
select 3,102,201,'yyy' union all
select 4,201,300,'zzz' union all
select 5,300,400,'qqq' union all
select 3,101,210,'ppp'

;with cte(levels, parentid, categoryid, title) as (
select 1,
parentid,
categoryid,
title
from @.categories
where parentid = 0
union all
select p.levels+1,
c.parentid,
c.categoryid,
c.title
from cte p join @.categories c on p.categoryid = c.parentid
)
select *
from cte order by levels, categoryId

/*
levels parentid categoryid title
-- -- -- -
1 0 1 aaa
2 1 100 bbb
2 1 101 ccc
2 1 102 ddd
3 102 200 xxx
3 102 201 yyy
3 101 210 ppp
4 201 300 zzz
5 300 400 qqq
*/

However, now that I think about it, I think you are asking a different question; hold on and I will get a second example.

|||

What do you mean "the query result should be generated level by level." There is never any guarantee what the result will look like unless you order it. What order do you want to achieve? Based on your generated level by level question, I would guess:

...

select *

from cte

order by levels

|||

My knee-jerk reaction to the question was the same as Louis' reaction. What I often find is that this question is intended to ask how do you display the data in tree order. That is a very different proposition. Is this more like what you are looking for:


;with cte(levels, parentid, categoryid, tree, title) as (
select 1,
parentid,
categoryid,
cast(str(categoryId, 11) as varchar(120)),
title
from @.categories
where parentid = 0
union all
select p.levels+1,
c.parentid,
c.categoryid,
cast(tree + '/' + str(c.categoryId, 11) as varchar(120)),
c.title
from cte p join @.categories c on p.categoryid = c.parentid
)
select levels,
parentId,
categoryId,
title,
replace(tree, ' ', '') as tree
from cte order by tree

/*
levels parentId categoryId title tree
-- -- -- - -
1 0 1 aaa 1
2 1 100 bbb 1/100
2 1 101 ccc 1/101
3 101 210 ppp 1/101/210
2 1 102 ddd 1/102
3 102 200 xxx 1/102/200
3 102 201 yyy 1/102/201
4 201 300 zzz 1/102/201/300
5 300 400 qqq 1/102/201/300/400
*/

|||

Thanks Kent/Louis,

Please refer to this thread: http://forums.microsoft.com/TechNet/ShowPost.aspx?PostID=473895&SiteID=17.
Euan's codes work perfect for a small table, but mine is pretty large, I want the CTE returning me maximum 20 levels of result, I did use [OPTION (MAXRECURSION 20)] in my query, but because the statement will terminate first when it reaches maximum levels, in this case, ORDER BY won't work.

My result set looks like this:

Levels Pathid Paths

1 96 40885>96
1 98 40885>98
....
1 60532 40885>60532
2 193 40885>60532>193
2 194 40885>60532>194
....
2 60524 40885>60532>60524
3 698 40885>60532>60524>698
3 915 40885>60532>60524>915
...
3 60527 40885>60532>60524>60527
4 3068 40885>60532>60524>60527>3068
4 4022 40885>60532>60524>60527>4022
...
4 60530 40885>60532>60524>60527>60530
5 10760 40885>60532>60524>60527>60530>10760
5 20365 40885>60532>60524>60527>60530>20365
...

please note, above pattern is very interesting, what I really want here is: I'd like to have the next level results are all from previous level, NOT just last record.

Thanks

|||

Did Kent's query answer you with the path? It seems to be the same (other than the fact that you are not returning the root node). A query like that was going to be my next suggeston.

Instead of MAXRECURSION, you can also limit the level value in the where clause to make it stop too:

;with cte(levels, parentid, categoryid, tree, title) as (
select 1,
parentid,
categoryid,
cast(str(categoryId, 11) as varchar(120)),
title
from @.categories
where parentid = 0
union all
select p.levels+1,
c.parentid,
c.categoryid,
cast(tree + '/' + str(c.categoryId, 11) as varchar(120)),
c.title
from cte p join @.categories c on p.categoryid = c.parentid
where p.levels < 2 --this will get level one and two only (because p.levels + 1)
)
select levels,
parentId,
categoryId,
title,
replace(tree, ' ', '') as tree
from cte
order by tree

levels parentId categoryId title tree

-- -- -- - --

1 0 1 aaa 1

2 1 100 bbb 1/100

2 1 101 ccc 1/101

2 1 102 ddd 1/102

|||

Thanks Louis , that works

Bill

Friday, February 24, 2012

How can i use Recursive CTE here

Hello Everyone,

I have a purchase order table that holds say 2 columns.PO andOrgPO. That isPurchase Order # andOriginal Purchase Order # respectively. Assume i have the following rows in the table.

PO OrgPO

-- --

po1 NULL

co1 po1

co2 co1

co3 co2

po2 NULL

cpo1 po2

po3 NULL

Now what i would like to report in the output is the PO and along with the lastly generated change order for that po. For eg,

PO LastCO

-- --

po1 co3

po2 cpo1

po3 po3

Currently i 'm using function to achieve this effect and i believe this is not the efficient way. I would like to generate this in a much efficient way. Please help me to achieve this.

Assume your table named 'pot' and here's the code below:

WITH x (newPO, newLastCO, depth)AS(SELECTCASEWHEN OrgPOISNULLTHEN POELSE OrgPOEND AS newPO, POAS newLastCO, 0AS depthFROM potWHERE OrgPOISNULLUNIONALLSELECT x.newPO, POAS newLastCO, x.depth + 1FROM pot pJOIN xON x.newLastCO = p.OrgPO)SELECT x.newPO, x.newLastCOFROM xINNERJOIN (SELECT newPO,MAX(depth)AS deepestFROM XGROUP BY newPO)AS yON x.newPO = y.newPOAND x.depth = y. deepestORDER BY x.newPO
|||

Hi jackyang,

Thanx for the solution manYes