The maximum recursion 100 has been exhausted before statement completion. SQL Server

TAII EL MEHDI 21 Reputation points
2020-09-23T20:34:21.567+00:00

I have this function to count with day names, and when I am add a long date he give this error
'The maximum recursion 100 has been exhausted before statement completion.'
Can someone please help?
CREATE FUNCTION COUNT
(@d1 DATE,
@d2 DATE,
@nd VARCHAR(MAX))
RETURNS TABLE
AS
RETURN
(WITH AllDates AS
(
SELECT @d1 AS DateOf
UNION ALL
SELECT DATEADD (DAY, 1, DateOf)
FROM AllDates
WHERE DateOf < @d2
)
SELECT COUNT(*) SumOfDays
FROM AllDates
WHERE EXISTS (SELECT 1
FROM STRING_SPLIT (@nd,' ')
WHERE DATENAME(weekday, dateof) = value)
)

SQL Server
SQL Server
A family of Microsoft relational database management and analysis systems for e-commerce, line-of-business, and data warehousing solutions.
12,714 questions
Transact-SQL
Transact-SQL
A Microsoft extension to the ANSI SQL language that includes procedural programming, local variables, and various support functions.
4,552 questions
{count} votes

4 answers

Sort by: Most helpful
  1. Erland Sommarskog 101K Reputation points MVP
    2020-09-23T21:53:49.32+00:00

    You need to add the hint OPTION(MAXRECUSION 0) where you run the query. (You cannot put the hint inside the function.)

    However, there are better techniques to achieve what you want to do, and I have an article on my web site that gives you suggestions: http://www.sommarskog.se/Short%20Stories/table-of-numbers.html

    3 people found this answer helpful.

  2. EchoLiu-MSFT 14,571 Reputation points
    2020-09-24T01:36:40.537+00:00

    Hi @TAII EL MEHDI ,

    The default number of iterations for recursive cte is 100. As long as this number is exceeded, the query will be interrupted and an error will be generated. If you want to remove this restriction, you can specify maxrecursion 0.

    Please refer to:

       CREATE FUNCTION COUNT  
        (@d1 DATE,  
        @d2 DATE,  
        @nd VARCHAR(MAX))  
        RETURNS TABLE  
        AS  
        RETURN  
        (WITH AllDates AS  
        (  
        SELECT @d1 AS DateOf  
        UNION ALL  
        SELECT DATEADD (DAY, 1, DateOf)  
        FROM AllDates  
        WHERE DateOf < @d2  
        )  
        SELECT COUNT(*) SumOfDays  
        FROM AllDates  
        WHERE EXISTS (SELECT 1  
        FROM STRING_SPLIT (@nd,' ')  
        WHERE DATENAME(weekday, dateof) = value)  
        OPTION(MAXRECURSION 0)  
        )  
    

    Best Regards
    Echo


    If the answer is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

  3. Jeffrey Williams 1,886 Reputation points
    2020-09-26T16:22:40.52+00:00

    Instead of trying to figure out how to make a recursive CTE work - you can use an inline tally approach to generate the list of dates between your start/end dates. Here is some sample code for you:

        Set Nocount On;
    
    Declare @date1 date = '2020-01-01'
          , @date2 date = getdate()
          , @nd varchar(8000) = 'Monday Tuesday Saturday';
    
        Set STATISTICS io, time On;
    
     --==== Recursive Method
      Print 'Recursive Method';
    
       With AllDates As
            (
     Select @date1 As DateOf
      Union All
     Select dateadd(day, 1, DateOf)
       From AllDates
      Where DateOf < @date2
            )
     Select count(*) SumOfDays
       From AllDates
      Where Exists( Select 1
                      From STRING_SPLIT(@nd, ' ')
                     Where datename(weekday, dateof) = Value)
     Option (maxrecursion 0);
    
     --==== inline-tally version
      Print char(10) + 'Inline-Method';
    
       With t(n)
         As (
     Select t.n 
       From (
     Values (0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) As t(n)
            )
          , dates (DateOf)
         As (
     Select Top (datediff(day, @date1, @date2) + 1) 
            dateadd(day, checksum(row_number() over(Order By @@spid)) - 1, @date1) 
       From t t1, t t2, t t3, t t4                  -- 10000 rows = over 27 years of possible dates
            )
     Select SumOfDays = count(d.DateOf)
       From dates                                    d
      Where Exists (Select *
                      From string_split(@nd, ' ')   ss
                     Where ss.value = datename(weekday, d.DateOf));
    
        Set STATISTICS io, time Off;
    
    1 person found this answer helpful.
    0 comments No comments

  4. Jeffrey Williams 1,886 Reputation points
    2020-09-26T16:39:36.103+00:00

    It also might be a better idea to change your @nd parameter to accept the numeric day of week instead of the week day name, and delimit that list by something other than a space (comma, semi-colon, pipe).

    Using this calculation for the day of the week datediff(day, 0, DateOf) % 7 + 1 gives us Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6 and Sunday = 7 (ISO day of week number).

    Here is that code:

        Set Nocount On;
    
    Declare @date1 date = '2020-01-01'
          , @date2 date = getdate()
          , @nd varchar(8000) = '1;2;6';
    
        Set STATISTICS io, time On;
    
     --==== Recursive Method
      Print 'Recursive Method';
    
       With AllDates As
            (
     Select @date1 As DateOf
      Union All
     Select dateadd(day, 1, DateOf)
       From AllDates
      Where DateOf < @date2
            )
     Select count(*) SumOfDays
       From AllDates
      Where Exists( Select 1
                      From STRING_SPLIT(@nd, ';')
                     Where datediff(day, 0, dateof) % 7 + 1 = Value)
     Option (maxrecursion 0);
    
     --==== inline-tally version
      Print char(10) + 'Inline-Method';
    
       With t(n)
         As (
     Select t.n 
       From (
     Values (0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) As t(n)
            )
          , dates (DateOf)
         As (
     Select Top (datediff(day, @date1, @date2) + 1) 
            dateadd(day, checksum(row_number() over(Order By @@spid)) - 1, @date1) 
       From t t1, t t2, t t3, t t4                  -- 10000 rows = over 27 years of possible dates
            )
     Select SumOfDays = count(d.DateOf)
       From dates                                    d
      Where Exists (Select *
                      From string_split(@nd, ';')   ss
                     Where ss.value = datediff(day, 0, d.DateOf) % 7 + 1); -- datename(weekday, d.DateOf));
    
        Set STATISTICS io, time Off;
    
    0 comments No comments