Because the function doesn't reference any physical tables it's good to specify 'with schemabinding' when defining the function. This allows the optimizer to skip late binding to objects. Here's the function
CREATE FUNCTION TEST
(@d1 DATE,
@d2 DATE,
@nd VARCHAR(MAX))
RETURNS TABLE with schemabinding
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)
)
Query to execute
SELECT *
FROM [dbo].[TEST]('2022-06-06','2029-08-08','Monday Tuesday')
OPTION (MAXRECURSION 0);
Output
SumOfDays
750
Below is a different approach using a tally function
Tally based approach
One way to go would be to drop the recursive cte altogether and go with a "tally" table approach. Please see this link for more details.
Something like this
Inline table valued function
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[fnTally]
/**********************************************************************************************************************
Jeff Moden Script on SSC: https://www.sqlservercentral.com/scripts/create-a-tally-function-fntally
**********************************************************************************************************************/
(@ZeroOrOne BIT, @MaxN BIGINT)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN WITH
H2(N) AS ( SELECT 1
FROM (VALUES
(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
)V(N)) --16^2 or 256 rows
, H4(N) AS (SELECT 1 FROM H2 a, H2 b) --16^4 or 65,536 rows
, H8(N) AS (SELECT 1 FROM H4 a, H4 b) --16^8 or 4,294,967,296 rows
SELECT N = 0 WHERE @ZeroOrOne = 0 UNION ALL
SELECT TOP(@MaxN)
N = ROW_NUMBER() OVER (ORDER BY N)
FROM H8
;
Query
CREATE FUNCTION COUNT_Dt(@d1 DATE, @d2 DATE, @nd VARCHAR(MAX))
RETURNS TABLE with schemabinding
AS
RETURN
select COUNT(*) SumOfDays
FROM dbo.fnTally(0, datediff(d, @d1, @d2)) fn
cross apply
(select dateadd(d, fn.n, @d1) dt) da
WHERE EXISTS(SELECT 1
FROM STRING_SPLIT(@nd,' ')
WHERE DATENAME(weekday,da.dt)=[value]);
go