如何防止聚簇索引扫描?

How to prevent clustered index scan?

我操作的是SAP B1数据库,不允许修改数据库的结构。

我有一个有 4 列的 table。

Table name: HLD1
Column  Name     Type
1       HldCode  nvarchar
2       StrDate  datetime
3       EndDate  datetime
4       Rmrks    nvarchar

部分数据如下所示:

HldCode         StrDate                  EndDate                    Rmrks
2016 Holidays   2016-09-05 00:00:00.000  2016-09-05 00:00:00.000    Labor Day
2016 Holidays   2016-11-24 00:00:00.000  2016-11-25 00:00:00.000    Thankgiving
2016 Holidays   2016-12-26 00:00:00.000  2016-12-26 00:00:00.000    Christmas
2017 Holidays   2017-01-02 00:00:00.000  2017-01-02 00:00:00.000    New Years Day
2017 Holidays   2017-05-29 00:00:00.000  2017-05-29 00:00:00.000    Memorial Day
2017 Holidays   2017-07-04 00:00:00.000  2017-07-04 00:00:00.000    Indepenance Day

注意这个table中没有主键。

我创建了一个函数来查找两个日期之间的天数,不包括节假日(由上面的 HLD1 table 提供)和周末。虽然该函数按预期工作,但它使用的每行也需要 ~.75 秒,我们正在尝试 return 50000 行稍后在 Crystal 报告中进行总结。

引用 HLD1 table(并导致执行计划中的聚簇索引)的函数片段如下所示:

CREATE FUNCTION [dbo].[dateDiffHolidays] (
declare @START DATE
declare @END DATE
)

RETURNS INT
AS
BEGIN

SELECT @AddDays =
      (select sum(datediff(dd,strdate,enddate) + 1) from hld1
        where strdate between @START and @END) 
    + 
      (SELECT
        (DATEDIFF(wk, @Start, @End) * 2)
        +(CASE WHEN DATENAME(dw, @Start) = 'Sunday'   THEN 1 ELSE 0 END)
        +(CASE WHEN DATENAME(dw, @End)   = 'Saturday' THEN 1 ELSE 0 END))

RETURN @AddDays

END 
GO

具体来说,第一部分。 @START@END 是传递给函数的参数。

当我检查函数的执行计划时,一切看起来都很快,除了这一段。它给了我以下信息:

我在网上找到的关于如何防止或修复这种减速的所有资源都建议添加索引,而不是引用某些列等,但由于我无法修改数据库,我'我一直找不到任何方法来帮助解决我的情况。

有什么建议吗?

编辑 1:

添加了 Table 来自 SQL 管理的架构信息

编辑 2:

添加了函数的全文,以防万一:

CREATE FUNCTION [dbo].[dateDiffHolidays] (
@startdaytime DATETIME,
@enddaytime DATETIME
)
RETURNS INT
AS
BEGIN

DECLARE @answer INT, @START Date, @END Date, @AddDays int

SET @answer = 0



-- Strip Times
SELECT @START = dateadd(dd,0, datediff(dd,0,@StartDayTime)), @END = 
dateadd(dd,0, datediff(dd,0,@EndDayTime))
SELECT @AddDays = (select sum(datediff(dd,strdate,enddatE) + 1) from hld1
    where strdate between @START and @END
    order by HldCode, StrDate, EndDate) + (
SELECT
  (DATEDIFF(wk, @Start, @End) * 2)
   +(CASE WHEN DATENAME(dw, @Start) = 'Sunday'   THEN 1 ELSE 0 END)
   +(CASE WHEN DATENAME(dw, @End)   = 'Saturday' THEN 1 ELSE 0 END))

-- handle end conditions
DECLARE @firstWeekDayInRange datetime, @lastWeekDayInRange datetime;
SELECT @firstWeekDayInRange = @START, @lastWeekDayInRange = @END


WHILE @firstWeekDayInRange in (select cast( DATEADD(day, t.N - 1, StrDate) 
as date) as ResultDate 
from HLD1 s join cteTally t on t.N <= DATEDIFF(day, StrDate, EndDate) + 1)
or datepart(dw,@firstWeekDayInRange) in (1,7) 

BEGIN

SELECT @firstWeekDayInRange =
CASE
WHEN @firstWeekDayInRange in (select cast( DATEADD(day, t.N - 1, StrDate) as 
date) from HLD1 s join cteTally t on t.N <= DATEDIFF(day, StrDate, EndDate) 
+ 1)
or datepart(dw,@firstWeekDayInRange) in (1,7) 
THEN dateadd(DAY,1,@firstWeekDayInRange)
ELSE @firstWeekDayInRange
END

END 



WHILE @lastWeekDayInRange in (select cast( DATEADD(day, t.N - 1, StrDate) as 
date) as ResultDate 
from HLD1 s join cteTally t on t.N <= DATEDIFF(day, StrDate, EndDate) + 1)
or datepart(dw,@lastWeekDayInRange) in (1,7) 

BEGIN

SELECT @lastWeekDayInRange =
CASE
WHEN @lastWeekDayInRange in (select cast( DATEADD(day, t.N - 1, StrDate) as 
date) from HLD1 s join cteTally t on t.N <= DATEDIFF(day, StrDate, EndDate) 
+ 1)
or datepart(dw,@lastWeekDayInRange) in (1,7) 
THEN dateadd(DAY,-1,@lastWeekDayInRange)
ELSE @lastWeekDayInRange
END
END 


-- add one day to answer (to count Friday) if enddate was on a weekend

SELECT @answer = @answer +
CASE
-- triggered if start and end date are on same weekend
WHEN dateDiff(DAY,@firstWeekDayInRange,@lastWeekDayInRange) < 0 THEN 
 (@answer * -1)
-- otherwise count the days and substract 2 days per weekend in between dates
ELSE (DateDiff(DAY, @firstWeekDayInRange, @lastWeekDayInRange) - @AddDays)
END


RETURN @answer
END 




GO

您可以尝试添加一个 ORDER BY 子句。

 CREATE TABLE HLD1
 (
     HldCode nvarchar(20),
     StrDate datetime,
     EndDate datetime,
     Rmrks nvarchar(50)
 )

 create unique index id_hld1 on HLD1 (HldCode, StrDate, EndDate);
 GO

INSERT INTO HLD1 
VALUES ('2016 Holidays', '2016-09-05 00:00:00.000', '2016-09-05 00:00:00.000', 'Labor Day'),
       ('2016 Holidays', '2016-11-24 00:00:00.000', '2016-11-25 00:00:00.000', 'Thanksgiving'),
       ('2016 Holidays', '2016-12-26 00:00:00.000', '2016-12-26 00:00:00.000', 'Christmas'),
       ('2017 Holidays', '2017-01-02 00:00:00.000', '2017-01-02 00:00:00.000', 'New Years Day'),
       ('2017 Holidays', '2017-05-29 00:00:00.000', '2017-05-29 00:00:00.000', 'Memorial Day'),
        ('2017 Holidays',  '2017-07-04 00:00:00.000', '2017-07-04 00:00:00.000',  'Independence Day');
GO
6 rows affected

DECLARE @StrDate datetime = '2017-01-01';
DECLARE @EndDate datetime = '2018-01-01'

set statistics profile on;

SELECT HldCode, StrDate, EndDate, Rmrks
FROM HLD1
WHERE StrDate >= @StrDate
  AND EndDate < @EndDate;

set statistics profile off;
GO

输出:

HldCode       | StrDate             | EndDate             | Rmrks          
:------------ | :------------------ | :------------------ | :--------------
2017 Holidays | 02/01/2017 00:00:00 | 02/01/2017 00:00:00 | New Years Day  
2017 Holidays | 29/05/2017 00:00:00 | 29/05/2017 00:00:00 | Memorial Day   
2017 Holidays | 04/07/2017 00:00:00 | 04/07/2017 00:00:00 | Indepenance Day


Rows | Executes | StmtText                                                                                                                                                                                                                                               | StmtId | NodeId | Parent | PhysicalOp | LogicalOp  | Argument                                                                                                                                                                                                                              | DefinedValues                                                                                                                                                                                                                                                        | EstimateRows | EstimateIO | EstimateCPU | AvgRowSize | TotalSubtreeCost | OutputList                                                                                                                                                                                                                                                           | Warnings | Type     | Parallel | EstimateExecutions
> :--- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -----: | -----: | -----: | :--------- | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | :--------- | :---------- | ---------: | :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :------- | :------- | :-----------------
> 3    | 1        | SELECT HldCode, StrDate, EndDate, Rmrks&lt;br&gt;from   HLD1&lt;br&gt;WHERE  StrDate &gt;= @StrDate&lt;br&gt;AND    EndDate &lt; @EndDate                                                                                                                                      |      1 |      1 |      0 | <em>null</em>       | <em>null</em>       | <em>null</em>                                                                                                                                                                                                                                  | <em>null</em>                                                                                                                                                                                                                                                                 | 1            | <em>null</em>       | <em>null</em>        |       <em>null</em> | 0.0032886        | <em>null</em>                                                                                                                                                                                                                                                                 | <em>null</em>     | SELECT   | False    | <em>null</em>              
> 3    | 1        |   |--Table Scan(OBJECT:([fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1]), WHERE:([fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[StrDate]&gt;=[@StrDate] AND [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[EndDate]&lt;[@EndDate])) |      1 |      2 |      1 | Table Scan | Table Scan | OBJECT:([fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1]), WHERE:([fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[StrDate]&gt;=[@StrDate] AND [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[EndDate]&lt;[@EndDate]) | [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[HldCode], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[StrDate], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[EndDate], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[Rmrks] | 1            | 0.003125   | 0.0001636   |         99 | 0.0032886        | [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[HldCode], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[StrDate], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[EndDate], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[Rmrks] | <em>null</em>     | PLAN_ROW | False    | 1                 


DECLARE @StrDate datetime = '2017-01-01';
DECLARE @EndDate datetime = '2018-01-01'

set statistics profile on;

SELECT HldCode, StrDate, EndDate, Rmrks
FROM HLD1
WHERE StrDate >= @StrDate
  AND EndDate < @EndDate
ORDER BY HldCode, StrDate, EndDate;

set statistics profile off;
GO

输出:

HldCode       | StrDate             | EndDate             | Rmrks          
:------------ | :------------------ | :------------------ | :--------------
2017 Holidays | 02/01/2017 00:00:00 | 02/01/2017 00:00:00 | New Years Day  
2017 Holidays | 29/05/2017 00:00:00 | 29/05/2017 00:00:00 | Memorial Day   
2017 Holidays | 04/07/2017 00:00:00 | 04/07/2017 00:00:00 | Indepenance Day

Rows | Executes | StmtText                                                                                                                                                                                                                                                                               | StmtId | NodeId | Parent | PhysicalOp   | LogicalOp  | Argument                                                                                                                                                                                                                                                         | DefinedValues                                                                                                                                                                                                   | EstimateRows | EstimateIO | EstimateCPU | AvgRowSize | TotalSubtreeCost | OutputList                                                                                                                                                                                                                                                           | Warnings | Type     | Parallel | EstimateExecutions
 :--- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -----: | -----: | -----: | :----------- | :--------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | :--------- | :---------- | ---------: | :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :------- | :------- | :-----------------
 3    | 1        | SELECT HldCode, StrDate, EndDate, Rmrks&lt;br&gt;from   HLD1&lt;br&gt;WHERE  StrDate &gt;= @StrDate&lt;br&gt;AND    EndDate &lt; @EndDate&lt;br&gt;ORDER BY HldCode, StrDate, EndDate                                                                                                                                |      1 |      1 |      0 | <em>null</em>         | <em>null</em>       | <em>null</em>                                                                                                                                                                                                                                                             | <em>null</em>                                                                                                                                                                                                            | 1            | <em>null</em>       | <em>null</em>        |       <em>null</em> | 0.00658116       | <em>null</em>                                                                                                                                                                                                                                                                 | <em>null</em>     | SELECT   | False    | <em>null</em>              
 3    | 1        |   |--Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1000]))                                                                                                                                                                                                                            |      1 |      2 |      1 | Nested Loops | Inner Join | OUTER REFERENCES:([Bmk1000])                                                                                                                                                                                                                                     | <em>null</em>                                                                                                                                                                                                            | 1            | 0          | 4.18E-06    |         99 | 0.00658116       | [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[HldCode], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[StrDate], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[EndDate], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[Rmrks] | <em>null</em>     | PLAN_ROW | False    | 1                 
 3    | 1        |        |--Index Scan(OBJECT:([fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[id_hld1]),  WHERE:([fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[StrDate]&gt;=[@StrDate] AND [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[EndDate]&lt;[@EndDate]) ORDERED FORWARD) |      1 |      3 |      2 | Index Scan   | Index Scan | OBJECT:([fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[id_hld1]),  WHERE:([fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[StrDate]&gt;=[@StrDate] AND [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[EndDate]&lt;[@EndDate]) ORDERED FORWARD | [Bmk1000], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[HldCode], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[StrDate], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[EndDate] | 1            | 0.003125   | 0.0001636   |         55 | 0.0032886        | [Bmk1000], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[HldCode], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[StrDate], [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[EndDate]                                                      | <em>null</em>     | PLAN_ROW | False    | 1                 
 3    | 3        |        |--RID Lookup(OBJECT:([fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1]), SEEK:([Bmk1000]=[Bmk1000]) LOOKUP ORDERED FORWARD)                                                                                                                                               |      1 |      5 |      2 | RID Lookup   | RID Lookup | OBJECT:([fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1]), SEEK:([Bmk1000]=[Bmk1000]) LOOKUP ORDERED FORWARD                                                                                                                                               | [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[Rmrks]                                                                                                                                                  | 1            | 0.003125   | 0.0001581   |         61 | 0.0032831        | [fiddle_9f66021924d842d39e112d909afc0794].[dbo].[HLD1].[Rmrks]                                                                                                                                                                                                       | <em>null</em>     | PLAN_ROW | False    | 1                 

dbfiddle here

更新

只要你需要存储过程,你都可以试试;

WITH (INDEX(IndexName))
CREATE FUNCTION [dbo].[dateDiffHolidays] (@START DATE, @END DATE)
RETURNS INT
AS
BEGIN
    DECLARE @AddDays int;

    SELECT @AddDays = (SELECT   sum(datediff(dd, StrDate, EndDate) + 1) 
                       FROM     hld1 WITH (INDEX(HLD1_PRIMARY))
                       WHERE    StrDate BETWEEN @START AND @END) 
                       + 
                       (SELECT (DATEDIFF(wk, @Start, @End) * 2)
                               + (CASE WHEN DATENAME(dw, @Start) = 'Sunday' THEN 1 ELSE 0 END)
                               + (CASE WHEN DATENAME(dw, @End) = 'Saturday' THEN 1 ELSE 0 END))

    RETURN @AddDays
END 
DECLARE @StrDate datetime = '2017-01-01';
DECLARE @EndDate datetime = '2018-01-01';
DECLARE @NumDays int = 0;

set statistics profile on;
EXEC @NumDays = [dbo].[dateDiffHolidays] @StrDate, @EndDate;
set statistics profile off;

SELECT @NumDays;
Rows | Executes | StmtText                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | StmtId | NodeId | Parent | PhysicalOp       | LogicalOp      | Argument                                                                                                                                                                                                                                                                                  | DefinedValues                                                                                                                                                                                                                                                                    | EstimateRows | EstimateIO | EstimateCPU | AvgRowSize | TotalSubtreeCost | OutputList                                                                                                                         | Warnings | Type     | Parallel | EstimateExecutions
:--- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -----: | -----: | -----: | :--------------- | :------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | :--------- | :---------- | ---------: | :--------------- | :--------------------------------------------------------------------------------------------------------------------------------- | :------- | :------- | :------- | :-----------------
1    | 1        | SELECT @AddDays = (SELECT   sum(datediff(dd, StrDate, EndDate) + 1) <br>                       FROM     hld1 WITH (INDEX(HLD1_PRIMARY))<br>                       WHERE    StrDate BETWEEN @START AND @END) <br>                       + <br>                       (SELECT (DATEDIFF(wk, @Start, @End) * 2)<br>                               + (CASE WHEN DATENAME(dw, @Start) = 'Sunday' THEN 1 ELSE 0 END)<br>                               + (CASE WHEN DATENAME(dw, @End) = 'Saturday' THEN 1 ELSE 0 END)) |      1 |      1 |      0 | null             | null           | null                                                                                                                                                                                                                                                                                      | null                                                                                                                                                                                                                                                                             | 1            | null       | null        |       null | 0.00329658       | null                                                                                                                               | null     | SELECT   | False    | null              
0    | 0        |   |--Compute Scalar(DEFINE:([Expr1006]=[Expr1003]+(datediff(week,CONVERT_IMPLICIT(datetimeoffset(7),[@START],0),CONVERT_IMPLICIT(datetimeoffset(7),[@END],0))*(2)+CASE WHEN datename(weekday,[@START])=N'Sunday' THEN (1) ELSE (0) END+CASE WHEN datename(weekday,[@END])=N'Saturday' THEN (1) ELSE (0) END)))                                                                                                                                                                                                    |      1 |      2 |      1 | Compute Scalar   | Compute Scalar | DEFINE:([Expr1006]=[Expr1003]+(datediff(week,CONVERT_IMPLICIT(datetimeoffset(7),[@START],0),CONVERT_IMPLICIT(datetimeoffset(7),[@END],0))*(2)+CASE WHEN datename(weekday,[@START])=N'Sunday' THEN (1) ELSE (0) END+CASE WHEN datename(weekday,[@END])=N'Saturday' THEN (1) ELSE (0) END)) | [Expr1006]=[Expr1003]+(datediff(week,CONVERT_IMPLICIT(datetimeoffset(7),[@START],0),CONVERT_IMPLICIT(datetimeoffset(7),[@END],0))*(2)+CASE WHEN datename(weekday,[@START])=N'Sunday' THEN (1) ELSE (0) END+CASE WHEN datename(weekday,[@END])=N'Saturday' THEN (1) ELSE (0) END) | 1            | 0          | 1E-07       |         11 | 0.00329658       | [Expr1006]                                                                                                                         | null     | PLAN_ROW | False    | 1                 
0    | 0        |        |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1012]=(0) THEN NULL ELSE [Expr1013] END))                                                                                                                                                                                                                                                                                                                                                                                                              |      1 |      3 |      2 | Compute Scalar   | Compute Scalar | DEFINE:([Expr1003]=CASE WHEN [Expr1012]=(0) THEN NULL ELSE [Expr1013] END)                                                                                                                                                                                                                | [Expr1003]=CASE WHEN [Expr1012]=(0) THEN NULL ELSE [Expr1013] END                                                                                                                                                                                                                | 1            | 0          | 0           |         11 | 0.00329648       | [Expr1003]                                                                                                                         | null     | PLAN_ROW | False    | 1                 
1    | 1        |             |--Stream Aggregate(DEFINE:([Expr1012]=COUNT_BIG([Expr1007]), [Expr1013]=SUM([Expr1007])))                                                                                                                                                                                                                                                                                                                                                                                                            |      1 |      4 |      3 | Stream Aggregate | Aggregate      | null                                                                                                                                                                                                                                                                                      | [Expr1012]=COUNT_BIG([Expr1007]), [Expr1013]=SUM([Expr1007])                                                                                                                                                                                                                     | 1            | 0          | 2.3E-06     |         11 | 0.00329648       | [Expr1012], [Expr1013]                                                                                                             | null     | PLAN_ROW | False    | 1                 
0    | 0        |                  |--Compute Scalar(DEFINE:([Expr1007]=datediff(day,[fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[StrDate],[fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[EndDate])+(1)))                                                                                                                                                                                                                                                                                                       |      1 |      5 |      4 | Compute Scalar   | Compute Scalar | DEFINE:([Expr1007]=datediff(day,[fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[StrDate],[fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[EndDate])+(1))                                                                                                                   | [Expr1007]=datediff(day,[fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[StrDate],[fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[EndDate])+(1)                                                                                                                   | 3            | 0          | 3E-07       |         11 | 0.00329418       | [Expr1007]                                                                                                                         | null     | PLAN_ROW | False    | 1                 
3    | 1        |                       |--Index Scan(OBJECT:([fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[HLD1_PRIMARY]),  WHERE:([fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[StrDate]>=[@START] AND [fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[StrDate]<=[@END]))                                                                                                                                                                                                                             |      1 |      6 |      5 | Index Scan       | Index Scan     | OBJECT:([fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[HLD1_PRIMARY]),  WHERE:([fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[StrDate]>=[@START] AND [fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[StrDate]<=[@END]), FORCEDINDEX                             | [fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[StrDate], [fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[EndDate]                                                                                                                                               | 3            | 0.003125   | 0.0001636   |         23 | 0.0032886        | [fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[StrDate], [fiddle_c7abc2eb9b3f49599be6803069c6aa56].[dbo].[HLD1].[EndDate] | null     | PLAN_ROW | False    | 1                 

| (No column name) |
| ---------------: |
|              108 |

dbfiddle here

由于 HLD1_PRIMARY 索引包含以下列:HldCode, StrDate, EndDate 尝试将按 HldCode 过滤的条件添加到聚合 select.

例如,如果您只想包含 HldCode = '2016 Holidays'HldCode = '2017 Holidays' 的行:

CREATE FUNCTION [dbo].[dateDiffHolidays] (
    declare @START DATE
    declare @END DATE
)
RETURNS INT
AS
BEGIN
    SELECT @AddDays = (
        select
            sum(datediff(dd,strdate,enddate) + 1)
        from
            hld1
        where
            HldCode in ('2016 Holidays', '2017 Holidays')
            and strdate between @START and @END
    ) 
    + 
    (
        SELECT
            (DATEDIFF(wk, @Start, @End) * 2)
            +(CASE WHEN DATENAME(dw, @Start) = 'Sunday'   THEN 1 ELSE 0 END)
            +(CASE WHEN DATENAME(dw, @End)   = 'Saturday' THEN 1 ELSE 0 END)
    )
    RETURN @AddDays
END

更新

对于该特定查询,没有比索引查找更好的了。

然后,在查看完整代码后,我怀疑问题不在于该查询,而在于用于计算 @firstWeekDayInRange@lastWeekDayInRange:

的 while 循环
WHILE @firstWeekDayInRange in (
    select
        cast(DATEADD(day, t.N - 1, StrDate) as date) as ResultDate 
    from
        HLD1 s
        join cteTally t
            on t.N <= DATEDIFF(day, StrDate, EndDate) + 1
    )
    or datepart(dw, @firstWeekDayInRange) in (1,7) 
BEGIN
    SELECT @firstWeekDayInRange =
        CASE
        WHEN @firstWeekDayInRange in (
            select
                cast(DATEADD(day, t.N - 1, StrDate) as date)
            from
                HLD1 s
                join cteTally t on
                    t.N <= DATEDIFF(day, StrDate, EndDate) + 1
        )
        or datepart(dw,@firstWeekDayInRange) in (1,7) THEN
            dateadd(DAY,1,@firstWeekDayInRange)
        ELSE
            @firstWeekDayInRange
        END
END

最好的情况是这些查询会导致聚集索引扫描,最坏的情况是 Table 扫描 HLD1 table for each 循环迭代,而且我们对 cteTally 了解不多,所以情况可能更糟。

一定要用函数吗?如何创建一个 table 类型的日期日历?

CREATE TABLE #Datecalendar
(DateId DATE PRIMARY KEY
, IsWeekend BIT DEFAULT 0
, IsHoliday BIT DEFAULT 0
);

CREATE TABLE #HLD1
(HldCode nvarchar(20)
, StrDate datetime
, EndDate datetime
, Rmrks nvarchar(50)
);

INSERT INTO #HLD1 
VALUES ('2016 Holidays', '2016-09-05 00:00:00.000', '2016-09-05 00:00:00.000', 'Labor Day'),
   ('2016 Holidays', '2016-11-24 00:00:00.000', '2016-11-25 00:00:00.000', 'Thanksgiving'),
   ('2016 Holidays', '2016-12-26 00:00:00.000', '2016-12-26 00:00:00.000', 'Christmas'),
   ('2017 Holidays', '2017-01-02 00:00:00.000', '2017-01-02 00:00:00.000', 'New Years Day'),
   ('2017 Holidays', '2017-05-29 00:00:00.000', '2017-05-29 00:00:00.000', 'Memorial Day'),
    ('2017 Holidays',  '2017-07-04 00:00:00.000', '2017-07-04 00:00:00.000',  'Independence Day');


WITH cte AS (SELECT CAST('2016-01-01' AS DATE) AS DateId
                      UNION ALL
                      SELECT   DATEADD(dd, 1, DateId)
                      FROM cte
                      WHERE DATEADD(dd, 1, DateId) <= '2019-01-01'
)
INSERT INTO #Datecalendar 
(DateId
, IsWeekend
, IsHoliday
)
SELECT DateId
,CASE WHEN DATEPART(WEEKDAY,DateId) =1 OR DATEPART(WEEKDAY,DateId) = 7 THEN 1 ELSE 0 END  
,CASE WHEN h.HldCode IS NOT NULL THEN 1 ELSE 0 END
FROM cte cte
LEFT JOIN #HLD1 h
ON cte.DateId BETWEEN h.StrDate AND h.EndDate
OPTION (MAXRECURSION 0);

SELECT COUNT(*)
FROM #Datecalendar
WHERE DateId BETWEEN '2016-05-06' AND '2017-02-24'
AND IsWeekend = 0 
AND IsHoliday = 0;

DROP TABLE #HLD1;
DROP TABLE #Datecalendar;