Как предотвратить сканирование кластерного индекса?

Я работаю в базе данных SAP B1, поэтому изменение структуры базы данных не допускается.

У меня есть таблица с 4 столбцами.

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

Обратите внимание, что в этой таблице нет первичного ключа.

У меня есть функция, которую я создал, чтобы определить количество дней между двумя датами, исключая праздничные дни (как указано в таблице HLD1 выше) и выходные. Хотя функция работает, как и ожидалось, для каждой используемой строки также требуется ~ 0,75 секунды, и мы пытаемся вернуть 50000 строк для последующего суммирования в Crystal Reports.

Часть функции, которая ссылается на таблицу HLD1 (и вызывает кластеризованный индекс в плане выполнения), выглядит следующим образом:

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:

Добавлена ​​информация о схеме таблиц из SQL Management

Схема таблицы

РЕДАКТИРОВАТЬ 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

3 ответа

Решение

Вы можете попробовать добавить 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 здесь

ОБНОВИТЬ

Насколько вам нужна хранимая процедура, вы можете попробовать;

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;
Ряды | Выполняет | StmtText                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | StmtId | NodeId | Родитель | PhysicalOp       | LogicalOp      | Аргумент | DefinedValues ​​| EstimateRows | EstimateIO | EstimateCPU | AvgRowSize | TotalSubtreeCost | OutputList                                                                                                                         | Предупреждения Тип | Параллельно | EstimateExecutions:--- |:------- |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -----: | -----: | -----: |:--------------- |:------------- |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |:----------- |:--------- |:---------- | ---------: |:--------------- |:--------------------------------------------------------------------------------------------------------------------------------- |:------- |:------- |:------- |:-----------------
1    | 1        | SELECT @AddDays = (SELECT   sum(datediff(dd, StrDate, EndDate) + 1) 
ОТ hld1 WITH (INDEX(HLD1_PRIMARY))
ГДЕ StrDate МЕЖДУ @START AND @END)
+
(ВЫБРАТЬ (DATEDIFF (wk, @Start, @End) * 2)
+ (СЛУЧАЙ, КОГДА ДАТА ИМЕНА (dw, @Start) = 'Воскресенье', ТОГДА 1, ЛИБО 0 КОНЕЦ)
+ (СЛУЧАЙ, КОГДА ДАТА ИМЕНА (dw, @End) = 'Суббота' ТОГДА 1 ИЛИ 0 КОНЕЦ)) | 1 | 1 | 0 | ноль | ноль | ноль | ноль | 1 | ноль | ноль | ноль | 0,00329658 | ноль | ноль | ВЫБРАТЬ | Ложь | ноль 0 | 0 | | --Расчитать скаляр (DEFINE:([Expr1006]=[Expr1003]+(датировка (неделя,CONVERT_IMPLICIT(datetimeoffset(7),[@START],0),CONVERT_IMPLICIT(datetimeoffset(7),[@END],0))*(2)+CASE WHEN, когда имя даты (день недели,[@START])= воскресенье ' THEN (1) ELSE (0) END+CASE, когда имя даты (день недели,[@END])=N'Saturday' THEN (1) ELSE (0) END))) | 1 | 2 | 1 | Вычислить скаляр | Вычислить скаляр | DEFINE:([Expr1006]=[Expr1003]+(DateDiff (неделя, CONVERT_IMPLICIT (DateTimeOffset (7), [@ START], 0), CONVERT_IMPLICIT (DateTimeOffset (7), [@ END], 0)) * (2) + СЛУЧАЙ, КОГДА ДАТА ИМЕНИ даты (день недели,[@START])= НЕС воскресенье, ТОГДА (1) ДАЛЕЕ (0) КОНЕЦ + СЛУЧАЙ, КОГДА ДАТА ДАТЫ (день недели, [@ ЭНД]) = НС субботы, ТОГДА (1) ДАЛЕЕ (0)) КОНЕЦ)) | [Expr1006]=[Expr1003]+(дата (неделя,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] | ноль | PLAN_ROW | Ложь | 1 0 | 0 | | --Рассчитать скаляр (DEFINE:([Expr1003]= СЛУЧАЙ, КОГДА [Expr1012]=(0) ПОСЛЕ НУЛЬ ЕЩЕ [Expr1013] END)) | 1 | 3 | 2 | Вычислить скаляр | Вычислить скаляр | ОПРЕДЕЛИТЬ: ([Expr1003] = СЛУЧАЙ, КОГДА [Expr1012]=(0) ТОГДА НЕДОГОДНО [Expr1013] END) | [Expr1003]= СЛУЧАЙ, КОГДА [Expr1012]=(0) ТОГДА НЕДЕЙСТВИТЕЛЕНО [Expr1013] END | 1 | 0 | 0 | 11 | 0,00329648 | [Expr1003] | ноль | PLAN_ROW | Ложь | 1 1 | 1 | | - Агрегат потока (DEFINE:([Expr1012]=COUNT_BIG([Expr1007]), [Expr1013]=SUM([Expr1007])))) | 1 | 4 | 3 | Поток Агрегат | Агрегат | ноль | [Expr1012]=COUNT_BIG([Expr1007]), [Expr1013]=SUM([Expr1007]) | 1 | 0 | 2.3E-06 | 11 | 0,00329648 | [Expr1012], [Expr1013] | ноль | PLAN_ROW | Ложь | 1 0 | 0 | |--Compute Scalar(DEFINE:([Expr1007]=datediff(день,[fiddle_c7abc2eb9b3f49599be6803069c6aa56].[Dbo].[HLD1].[StrDate],[fiddle_c7abc2eb9b3b496] h6306) (1))) | 1 | 5 | 4 | Вычислить скаляр | Вычислить скаляр | DEFINE:([Expr1007]=datediff(день,[fiddle_c7abc2eb9b3f49599be6803069c6aa56].[Dbo].[HLD1].[StrDate],[fiddle_c7abc2eb9b3f49599be6803069c6a1] [1].] []. [Expr1007] = datediff (день,[fiddle_c7abc2eb9b3f49599be6803069c6aa56].[Dbo].[HLD1].[StrDate],[fiddle_c7abc2eb9b3f49599be6803069c6aa56].[D]] [HD]. [D]. 3 | 0 | 3E-07 | 11 | 0,00329418 | [Expr1007] | ноль | PLAN_ROW | Ложь | 1 3 | 1 | | - Индексное сканирование (OBJECT:([fiddle_c7abc2eb9b3f49599be6803069c6aa56].[Dbo].[HLD1].[HLD1_PRIMARY]), WHERE:([fiddle_c7abc2eb9b3f49599be6803069.DD.DB.DB) AND [fiddle_c7abc2eb9b3f49599be6803069c6aa56].[Dbo].[HLD1].[StrDate]<=[@END])) | 1 | 6 | 5 | Индекс сканирования | Индекс сканирования | OBJECT:([fiddle_c7abc2eb9b3f49599be6803069c6aa56].[Dbo]. [HLD1]. [HLD1_PRIMARY]), где: 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] | ноль | PLAN_ROW | Ложь | 1 | (Без названия столбца) | | ---------------: | | 108 |

dbfiddle здесь

Вы должны использовать функцию? как насчет создания календаря даты в виде таблицы?

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;

Так как HLD1_PRIMARY Индекс включает в себя следующие столбцы: HldCode, StrDate, EndDate попробуйте добавить условие, которое фильтрует по HldCode к вашему совокупному выбору.

Например, если вы хотите включить только строки с 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

ОБНОВИТЬ

Вы не получите ничего лучше, чем поиск по индексу для этого конкретного запроса.

Затем, посмотрев полный код, я подозреваю, что проблема не в этом запросе, а в циклах while, используемых для вычисления @firstWeekDayInRange а также @lastWeekDayInRange:

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

В лучшем случае эти запросы приводят к сканированию кластеризованного индекса, а в худшем - к сканированию таблицы в таблице HLD1 для каждой итерации цикла, и мы мало что знаем о cteTally, так что это может быть хуже.

Другие вопросы по тегам