I came across this CTE solution for concatenating row elements and I thought it\'s brilliant and I realized how powerful CTEs can be.
However, in order to use such a tool
This is an interesting question that helped me better understand recursive CTEs too.
If you look at the execution plan you will see that a spool is used and that it has the WITH STACK
property set. Which means that rows are read in a stack-like manner (Last In First Out)
So first the anchor part runs
EXEC_TIME CategoryID product_list
----------------------- ----------- --------------
2011-10-18 12:46:14.930 1
2011-10-18 12:46:14.990 2
2011-10-18 12:46:15.050 4
Then 4
is processed as that is the last row added. The JOIN
returns 1 row that is added to the spool then this newly added row is processed. In this case the Join returns nothing so there is nothing additional added to the spool and it moves on to processing the CategoryID = 2
row.
This returns 3 rows which are added to the spool
Aniseed Syrup
Chef Anton's Cajun Seasoning
Chef Anton's Gumbo Mix
then each of these rows are processed in turn in a similar LIFO fashion with any child rows added being dealt with first before processing can move on to the sibling rows. Hopefully you can see how this recursive logic explains your observed results but just in case you can't a C#
simulation
using System;
using System.Collections.Generic;
using System.Linq;
namespace Foo
{
internal class Bar
{
private static void Main(string[] args)
{
var spool = new Stack>();
//Add anchor elements
AddRowToSpool(spool, new Tuple(1, "", ""));
AddRowToSpool(spool, new Tuple(2, "", ""));
AddRowToSpool(spool, new Tuple(4, "", ""));
while (spool.Count > 0)
{
Tuple lastRowAdded = spool.Pop();
AddChildRows(lastRowAdded, spool);
}
Console.ReadLine();
}
private static void AddRowToSpool(Stack> spool,
Tuple row)
{
Console.WriteLine("CategoryId={0}, product_list = {1}",
row.Item1,
row.Item3);
spool.Push(row);
}
private static void AddChildRows(Tuple lastRowAdded,
Stack> spool)
{
int categoryId = lastRowAdded.Item1;
string productName = lastRowAdded.Item2;
string productList = lastRowAdded.Item3;
string[] products;
switch (categoryId)
{
case 1:
products = new[] {"Changassad"};
break;
case 2:
products = new[]
{
"Aniseed Syrup",
"Chef Anton's Cajun Seasoning",
"Chef Anton's Gumbo Mix "
};
break;
case 4:
products = new[] {"vcbcbvcbvc"};
break;
default:
products = new string[] {};
break;
}
foreach (string product in products.Where(
product => string.Compare(productName, product) < 0))
{
string product_list = string.Format("{0}{1}{2}",
productList,
productList == "" ? "" : ",",
product);
AddRowToSpool(spool,
new Tuple
(categoryId, product, product_list));
}
}
}
}
Returns
CategoryId=1, product_list =
CategoryId=2, product_list =
CategoryId=4, product_list =
CategoryId=4, product_list = vcbcbvcbvc
CategoryId=2, product_list = Aniseed Syrup
CategoryId=2, product_list = Chef Anton's Cajun Seasoning
CategoryId=2, product_list = Chef Anton's Gumbo Mix
CategoryId=2, product_list = Chef Anton's Cajun Seasoning,Chef Anton's Gumbo Mix
CategoryId=2, product_list = Aniseed Syrup,Chef Anton's Cajun Seasoning
CategoryId=2, product_list = Aniseed Syrup,Chef Anton's Gumbo Mix
CategoryId=2, product_list = Aniseed Syrup,Chef Anton's Cajun Seasoning,Chef Anton's Gumbo Mix
CategoryId=1, product_list = Changassad