How CTE really works?

前端 未结 2 1443
渐次进展
渐次进展 2021-02-20 11:03

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

相关标签:
2条回答
  • 2021-02-20 11:34

    The page Recursive Queries Using Common Table Expressions describes the logic of CTEs:

    The semantics of the recursive execution is as follows:

    1. Split the CTE expression into anchor and recursive members.

    2. Run the anchor member(s) creating the first invocation or base result set (T0).

    3. Run the recursive member(s) with Ti as an input and Ti+1 as an output.

    4. Repeat step 3 until an empty set is returned.

    5. Return the result set. This is a UNION ALL of T0 to Tn.

    However, that's only the logical flow. As always, with SQL, the server is free to reorder operations as it sees fit, if the result will be "the same", and the reordering is perceived to provide the results more efficiently.

    The presence of your function with side effects (causing a delay, then returning GETDATE()) isn't something that would normally be considered when deciding whether to reorder operations.

    One obvious way in which the query may be reordered is that it may decide to start working on result set Ti+1 before it has fully created result set Ti - it may be more efficient to do this than to fully construct Ti first, since the new rows are definitely already in memory and have been accessed recently.

    0 讨论(0)
  • 2021-02-20 11:51

    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<Tuple<int, string, string>>();
    
                //Add anchor elements
                AddRowToSpool(spool, new Tuple<int, string, string>(1, "", ""));
                AddRowToSpool(spool, new Tuple<int, string, string>(2, "", ""));
                AddRowToSpool(spool, new Tuple<int, string, string>(4, "", ""));
    
                while (spool.Count > 0)
                {
                    Tuple<int, string, string> lastRowAdded = spool.Pop();
                    AddChildRows(lastRowAdded, spool);
                }
    
                Console.ReadLine();
            }
    
        private static void AddRowToSpool(Stack<Tuple<int, string, string>> spool,
                                          Tuple<int, string, string> row)
            {
                Console.WriteLine("CategoryId={0}, product_list = {1}",
                                  row.Item1,
                                  row.Item3);
                spool.Push(row);
            }
    
        private static void AddChildRows(Tuple<int, string, string> lastRowAdded,
                                         Stack<Tuple<int, string, string>> 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<int, string, string>
                                      (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
    
    0 讨论(0)
提交回复
热议问题