Подсчитать, сколько рабочих дней между двумя датами - T-SQL? - PullRequest
1 голос
/ 13 февраля 2010

Я понимаю, что разные решения будут иметь разные вариации того, что означает «рабочие дни», но в моем случае я имею в виду с понедельника по пятницу включительно.

По сути, я создал функцию для расчета за меня, и мое текущее решение работает. Моя проблема (и причина для того, чтобы задать этот вопрос) заключается в том, что я обеспокоен тем, что это плохой способ достижения этого, потому что функция вызывается с очень высокой частотой. За последние 3 месяца он был вызван 12 миллионов раз в производственной системе, со средним рабочим временем 44 мс.

Это заставляет меня задаться вопросом, является ли это правильным способом достижения решения.

Во-первых, вот функция, которую я создал:

 CREATE FUNCTION [dbo].[fn_WorkDays]
    (
     @StartDate DATETIME,
     @EndDate   DATETIME = NULL --@EndDate replaced by @StartDate when DEFAULTed
    )
    RETURNS INT
 AS

 BEGIN
    --===== Declare local variables
    --Temporarily holds @EndDate during date reversal
    DECLARE @Swap DATETIME

    --===== If the Start Date is null, return a NULL and exit
         IF @StartDate IS NULL
            RETURN NULL

    --===== If the End Date is null, populate with Start Date value
         -- so will have two dates (required by DATEDIFF below)
         IF @EndDate IS NULL
            SELECT @EndDate = @StartDate

    --===== Strip the time element from both dates (just to be safe) by converting
         -- to whole days and back to a date.  Usually faster than CONVERT.
         -- 0 is a date (01/01/1900 00:00:00.000)
     SELECT @StartDate = DATEADD(dd,DATEDIFF(dd,0,@StartDate),0),
            @EndDate   = DATEADD(dd,DATEDIFF(dd,0,@EndDate)  ,0)

    --===== If the inputs are in the wrong order, reverse them
         IF @StartDate > @EndDate
            SELECT @Swap      = @EndDate,
                   @EndDate   = @StartDate,
                   @StartDate = @Swap

    --===== Calculate and return the number of workdays using the
         -- input parameters.  This is the meat of the function.
         -- This is really just one formula with a couple of parts
         -- that are listed on separate lines for documentation
         -- purposes.
     RETURN (
            SELECT
          --Start with total number of days including weekends
            (DATEDIFF(dd,@StartDate,@EndDate)+1)

          --Subtact 2 days for each full weekend
           -(DATEDIFF(wk,@StartDate,@EndDate)*2)

          --If StartDate is a Sunday, Subtract 1
           -(CASE WHEN DATENAME(dw,@StartDate) = 'Sunday'
                  THEN 1
                  ELSE 0
              END)

          --If EndDate is a Saturday, Subtract 1
           -(CASE WHEN DATENAME(dw,@EndDate) = 'Saturday'
                  THEN 1
                  ELSE 0
              END)
            )
END

В качестве простого примера его использования я бы запустил запрос такого типа:

SELECT MYTABLE.EntryDate
     ,dbo.fn_WorkDays(MYTABLE.EntryDate, getutcdate()) as WorkingDays
    FROM MYTABLE                                    

MyTable может содержать 5000 строк с разными датами в столбце EntryDate (5000 вызовов функции)

У меня вопрос: я что-то здесь упускаю из-за того, как я это делаю, было бы полезно создать для этого справочную таблицу (но это много комбинаций дат)

Любые мысли, улучшения или рекомендации будут оценены ...

Ответы [ 2 ]

2 голосов
/ 13 февраля 2010

Я не думаю, что вы можете многое сделать с UDF tbh - его вычисление во время выполнения, как это в SQL, всегда в некоторой степени повлечет за собой попадание.

Итак, в идеале (а это может быть невозможно, так как я не знаю полной картины), я думаю, что я бы сделал, чтобы сохранить число рабочих дней в вашей таблице и рассчитать его ОДИН РАЗ, когда будет создана запись. Если это невозможно (т. Е. Когда создается запись, у вас нет «даты окончания», поэтому ее нужно обрабатывать, используя «сейчас»), то я бы подумал о том, чтобы запланировать работу на ночь и пересчитать все эти данные. отдельные записи, чтобы они обновлялись каждый день, а затем, когда действительно указана «дата окончания», эта запись не включается в это пакетное обновление.

Преимущества этого в том, что вы переносите вычисления в более спокойный период и выполняете вычисления только один раз в день. Запрос становится намного проще и производительнее, так как он может просто прочитать число рабочих дней из таблицы.

Если это не вариант, то я бы предложил выполнить вычисления во внешнем интерфейсе, удалив попадание из БД.

0 голосов
/ 13 февраля 2010

Здесь есть две проблемы:

  1. Расчет количества дней между двумя датами,
  2. Определение того, является ли дата подачи "рабочим днем".

Второй включает в себя такие простые, как «будний день» или «выходные», праздничные дни (светские, религиозные и правовые) и т. Д.

Вам нужно решить оба вопроса.

Первое проще, потому что реляционные базы данных будут иметь функции, которые помогут вам. Это второе, которое сложнее и изменчивее, потому что оно меняется в зависимости от региона и бизнеса.

...