2016-11-29 8 views
3

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

СТЕ должен Вычислить все случаи, основанные на следующих параметрах:

  • Рецидив Count - сколько раз это будет происходить
  • дни недели - в какой день недели он произойдет
  • Дата начала - , когда начнется расчет рисунков
  • Периодичность - Как часто в терминах недель, т.е. 1 раз в неделю, 2 каждые 2 недели
  • Начало недели - Неделя номер первого вхождения, который отражает столбец Start Date

Это, как я хранить образцы:

/* 
    Pattern Table 
*/ 
CREATE TABLE Pattern (
    [Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY 
, [Subject] [nvarchar](100) NULL 
, [RecurrenceCount] [int] NULL 
, [WeekDays] [varchar](max) NULL 
, [StartDate] [datetime] NULL 
, [EndDate] [datetime] NULL 
, [Periodicity] [int] NULL 
, [StartingWeek] [int] NULL 

); 

Ниже несколько моделей я использую, чтобы проверить мой КТР:

/* 
    Pattern samples for test 
*/ 
Insert into Pattern Values (N'Every 5 Weeks Fri, Sat, Sun', 72, 'Friday, Saturday, Sunday', N'2016-12-02', N'2016-12-02', 5, datepart(wk, N'2016-12-02')); 
Insert into Pattern Values (N'Every 3 Weeks Tue, Wed, Thu', 20, 'Tuesday, Wednesday, Thursday', N'2016-11-01', N'2016-11-01', 3, datepart(wk, N'2016-11-01')); 

Я начинаю отсчет считая первый день недели понедельник

SET DATEFIRST 1 

И это КТР я использую для запуска этого вычисления:

/* 
    Display Patterns 
*/ 
select * from Pattern 

DECLARE @mindate DATE  = (SELECT MIN(StartDate) FROM Pattern) 
DECLARE @maxmindate DATE = (SELECT MAX(StartDate) FROM Pattern) 
DECLARE @maxcount INT  = (SELECT MAX(RecurrenceCount) FROM Pattern) 
DECLARE @maxdate DATE  = DATEADD(WK, @maxcount + 10, @maxmindate) 

/* 
    CTE to generate required occurrences 
*/ 
;With cteKeyDate As (
    Select 
     KeyStartDate = @MinDate, 
     KeyDOW = DateName(WEEKDAY, @MinDate), 
     KeyWeek = datepart(WK,@MinDate) 
    Union All 
    Select 
     KeyStartDate = DateAdd(DD, 1, df.KeyStartDate) , 
     KeyDOW = DateName(WEEKDAY,DateAdd(DD, 1, df.KeyStartDate)), 
     KeyWeek= DatePart(WK,DateAdd(DD, 1, df.KeyStartDate)) 
    From cteKeyDate DF 
    Where DF.KeyStartDate <= @MaxDate 
) 

SELECT 
    Id, KeyStartDate, KeyDow, KeyWeek, RowNr, OccNr = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY StartDate) 
FROM 
    (Select 
     A.Id 
     ,A.StartDate 
     ,A.EndDate 
     ,Count = A.RecurrenceCount 
     ,Days = A.WeekDays 
     ,Every = A.Periodicity    
     ,KeyStartDate = CASE 
     /* 
      if no periodicity (1) then it is sequential 
      if periodicity, first week doesn't apply (MIN KeyWeek) 
     */ 
     WHEN A.Periodicity = 1 
      OR (Periodicity <> 1 AND (SELECT MIN(C.StartingWeek) FROM Pattern AS C WHERE C.Id = A.Id) = KeyWeek) 
      THEN KeyStartDate 
     /* Otherwise formula ADD WEEKS => Current Week Min Week */ 
     ELSE 
      DATEADD(WK, ((A.Periodicity - 1) * (KeyWeek - (SELECT MIN(C.StartingWeek) FROM Pattern AS C WHERE C.Id = A.Id))) , KeyStartDate) 
     END 
     ,KeyDow 
     ,KeyWeek 
     ,RowNr = Row_Number() over (Partition By A.Id Order By B.KeyStartDate) 
     ,Periodicity = A.Periodicity 
    from 
     Pattern A 
     Join cteKeyDate B on B.KeyStartDate >= DATEADD(DAY, -1, A.StartDate) and Charindex(KeyDOW, A.WeekDays) > 0 
    ) Final     
Where 
    RowNr <= Count AND Id = 1 
Option (maxrecursion 32767) 

Теперь, если я проверяю снова мой шаблоны, например, первый, я получаю этот результат, который имеет ошибку, когда события происходят в следующем году. RowNr 15 ошибочен, потому что это должно произойти 23 апреля (воскресенье), а не на следующей неделе.

Id KeyStartDate KeyDow  KeyWeek RowNr OccNr 
1 02.12.2016  Friday  49  1  1 
2 03.12.2016  Saturday 49  2  2 
3 04.12.2016  Sunday  49  3  3 
4 06.01.2017  Friday  50  4  4 
5 07.01.2017  Saturday 50  5  5 
6 08.01.2017  Sunday  50  6  6 
7 10.02.2017  Friday  51  7  7 
8 11.02.2017  Saturday 51  8  8 
9 12.02.2017  Sunday  51  9  9 
10 17.03.2017  Friday  52  10  10 
11 18.03.2017  Saturday 52  11  11 
12 19.03.2017  Sunday  52  12  12 
13 21.04.2017  Friday  53  13  13 
14 22.04.2017  Saturday 53  14  14 
15 28.04.2013  Sunday  1  15  15 
16 31.05.2013  Friday  2  16  16 
17 01.06.2013  Saturday 2  17  17 

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

Вы можете выполнить код с образцами here.

+0

В качестве точки отсчета, вам не нужно, что 'Разбиение BY' в вашем' ROW_NUMBER' и * пожалуйста * не срабатывают код с 'Ā',' b', 'C 'табличные псевдонимы. – iamdave

+0

@iamdave о псевдонимах, я не использую A, B, C, но A означает «действия», поэтому есть известный псевдоним . О PARTITION BY необходимо иначе, если запрос выполняется по нескольким шаблонам на определенном SQL-сервере, таком как 2008, строки не упорядочены правильно – Raffaeu

+1

Можете ли вы объяснить, что пытается сделать CTE, то, что вы пытаетесь сделать здесь, это поможет нам лучше понять ваш код, в настоящее время дата начала и дата окончания одинаковы в вашем шаблоне стол, не могли бы вы попытаться рассказать нам, что вы пытаетесь выполнить здесь. – Surendra

ответ

1

Потратьте некоторое время на это. Используемый вами расчет неверен.Если в вашем вопросе не указаны специальные правила, я не понимаю, почему некоторые даты являются особенными. Я предпочитаю использовать переменные таблицы.

/* 
     Pattern Table 
    */ 
    DECLARE @Pattern TABLE(
     [Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY 
     ,[Subject] [nvarchar](100) NULL 
     ,[RecurrenceCount] [int] NULL 
     ,[WeekDays] [varchar](max) NULL 
     ,[StartDate] [datetime] NULL 
     ,[EndDate] [datetime] NULL 
     ,[Periodicity] [int] NULL 
     ,[StartingWeek] [int] NULL 
    ); 

    /* 
     Populate with values based on Recurreance and Startdate. The startdate will give the start week, which make the start week obsolete. 
    */ 
    DECLARE @PreferredDate TABLE(
     [Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY 
     ,[PreferredDate] [datetime] NULL 
     ,[PreferredWeek] [int] NULL 
     ,[PreferredYear] [int] NULL 
    ) 

Очень важно всегда получать текущие настройки для даты первой. Вы разбейте чужой расчет, если они используют другую настройку. Я также добавил идентификатор шаблона по очевидным причинам.

DECLARE @DateFirst int = @@dateFirst --DATEFIRST is a global setting 

    DECLARE @mindate DATE  = (SELECT MIN(StartDate) FROM @Pattern WHERE [email protected]) 
    DECLARE @maxmindate DATE = (SELECT MAX(StartDate) FROM @Pattern WHERE [email protected]) 
    DECLARE @maxcount INT  = (SELECT MAX(RecurrenceCount) FROM @Pattern WHERE [email protected]) 
    DECLARE @maxdate DATE  = DATEADD(WK, @maxcount + 50, @maxmindate) 

    SET DATEFIRST 1 

    DECLARE @PreferredSubjectID int = 1 

@preferreddate таблица заполняется с использованием следующего:

/* 
     CTE to generate required preferred dates 
    */ 
    ;With ctePreferredDate AS (
     Select PreferredDate = @MinDate, PreferredWeek = DATEPART(WK, @MinDate), PreferredYear = DATEPART(YYYY, @MinDate) 
     Union All 
     SELECT PreferredDate = DATEADD(WK,(SELECT Periodicity FROM @Pattern WHERE [email protected]), PreferredDate) 
       ,PreferredWeek = DATEPART(WK,DATEADD(WK,(SELECT Periodicity FROM @Pattern WHERE [email protected]), PreferredDate)) 
       ,PreferredYear = DATEPART(yyyy,DATEADD(WK,(SELECT Periodicity FROM @Pattern WHERE [email protected]), PreferredDate)) 
     From ctePreferredDate pFD 
     Where pFD.PreferredDate <= @MaxDate 

    ) 
    INSERT INTO @PreferredDate (PreferredDate, PreferredWeek, PreferredYear) 
    SELECT PreferredDate, PreferredWeek, PreferredYear 
    FROM ctePreferredDate 

Финальный стол КТР заполняется с использованием следующих:

/* 
     CTE to generate required occurrences 
    */ 
    ;With cteKeyDate As (
     Select KeyStartDate = @MinDate 
       ,KeyDOW = DateName(WEEKDAY, @MinDate) 
       ,KeyWeek = datepart(WK,@MinDate) 
       ,id = @PreferredSubjectID 
       ,KeyOccurrance = @maxcount 
     Union All 
     Select KeyStartDate = DateAdd(DD, 1, df.KeyStartDate) 
       ,KeyDOW = DateName(WEEKDAY,DateAdd(DD, 1, df.KeyStartDate)) 
       ,KeyWeek= DatePart(WK,DateAdd(DD, 1, df.KeyStartDate)) 
       ,[email protected] 
       ,KeyOccurrance = @maxcount 
     From cteKeyDate DF 
     Where DF.KeyStartDate <= @MaxDate 
    ) 
    SELECT StartDate 
      ,[DayOfWeek] 
      ,[Week] 
      ,OccNr = ROW_NUMBER() OVER   (PARTITION BY Id ORDER BY StartDate) 
    FROM 
     (
     SELECT cte.KeyStartDate AS StartDate 
       ,cte.KeyDOW AS [DayOfWeek] 
       ,cte.KeyWeek AS [Week] 
       ,cte.id 
       ,cte.KeyOccurrance AS Occurrance 
       ,RowNr = ROW_NUMBER() OVER   (PARTITION BY KeyOccurrance ORDER BY KeyStartDate) 
     FROM cteKeyDate cte 
       INNER JOIN 
       @PreferredDate pfd 
        ON cte.KeyWeek = pfd.PreferredWeek 
         AND YEAR(cte.KeyStartDate) = pfd.PreferredYear 
     WHERE cte.KeyDOW IN (SELECT LTRIM(RTRIM(Item)) FROM fn_SplitString((SELECT weekdays from @Pattern WHERE Id=1),',')) 
     )cte 
    WHERE cte.RowNr <= cte.Occurrance 
    ORDER BY cte.StartDate 
    Option (maxrecursion 32767) 

    SET DATEFIRST @DateFirst --Quite important 

Результаты

2016/12/02 Friday 49 1 
2016/12/03 Saturday 49 2 
2016/12/04 Sunday 49 3 
2017/01/06 Friday 2 4 
2017/01/07 Saturday 2 5 
2017/01/08 Sunday 2 6 
2017/02/10 Friday 7 7 
2017/02/11 Saturday 7 8 
2017/02/12 Sunday 7 9 
2017/03/17 Friday 12 10 
2017/03/18 Saturday 12 11 
2017/03/19 Sunday 12 12 
2017/04/21 Friday 17 13 
2017/04/22 Saturday 17 14 
2017/04/23 Sunday 17 15 
2017/05/26 Friday 22 16 
2017/05/27 Saturday 22 17 
2017/05/28 Sunday 22 18 
2017/06/30 Friday 27 19 
2017/07/01 Saturday 27 20 
2017/07/02 Sunday 27 21 
2017/08/04 Friday 32 22 
2017/08/05 Saturday 32 23 
2017/08/06 Sunday 32 24 
2017/09/08 Friday 37 25 
2017/09/09 Saturday 37 26 
2017/09/10 Sunday 37 27 
2017/10/13 Friday 42 28 
2017/10/14 Saturday 42 29 
2017/10/15 Sunday 42 30 
2017/11/17 Friday 47 31 
2017/11/18 Saturday 47 32 
2017/11/19 Sunday 47 33 
2017/12/22 Friday 52 34 
2017/12/23 Saturday 52 35 
2017/12/24 Sunday 52 36 
2018/01/26 Friday 4 37 
2018/01/27 Saturday 4 38 
2018/01/28 Sunday 4 39 
2018/03/02 Friday 9 40 
2018/03/03 Saturday 9 41 
2018/03/04 Sunday 9 42 
2018/04/06 Friday 14 43 
2018/04/07 Saturday 14 44 
2018/04/08 Sunday 14 45 
2018/05/11 Friday 19 46 
2018/05/12 Saturday 19 47 
2018/05/13 Sunday 19 48 
2018/06/15 Friday 24 49 
2018/06/16 Saturday 24 50 
2018/06/17 Sunday 24 51 
2018/07/20 Friday 29 52 
2018/07/21 Saturday 29 53 
2018/07/22 Sunday 29 54 
2018/08/24 Friday 34 55 
2018/08/25 Saturday 34 56 
2018/08/26 Sunday 34 57 
2018/09/28 Friday 39 58 
2018/09/29 Saturday 39 59 
2018/09/30 Sunday 39 60 
2018/11/02 Friday 44 61 
2018/11/03 Saturday 44 62 
2018/11/04 Sunday 44 63 
2018/12/07 Friday 49 64 
2018/12/08 Saturday 49 65 
2018/12/09 Sunday 49 66 
2019/01/11 Friday 2 67 
2019/01/12 Saturday 2 68 
2019/01/13 Sunday 2 69 
2019/02/15 Friday 7 70 
2019/02/16 Saturday 7 71 
2019/02/17 Sunday 7 72 

Трещина Строка Функция:

ALTER FUNCTION [dbo].[fn_SplitString](
    @InputStr varchar(Max), 
    @Seperator varchar(10)) 
RETURNS @OutStrings TABLE (ItemNo int identity(1,1), Item varchar(256)) 

AS 
BEGIN 

    DECLARE @Str varchar(2000), 
      @Poz int, @cnt int 

    --DECLARE @OutStrings TABLE (Item varchar(2000)) 

    SELECT @Poz = CHARINDEX (@Seperator, @InputStr), @cnt = 0 
    WHILE @Poz > 0 AND @cnt <= 10000 
    BEGIN 
     SELECT @Str = SubString(@InputStr, 1, @Poz - 1) 
     INSERT INTO @OutStrings(Item) VALUES(@Str) 

     SELECT @InputStr = Right(@Inputstr, Len(@InputStr) - (len(@Str) + len(@Seperator))) 
     SELECT @Poz = CHARINDEX (@Seperator, @InputStr), @cnt = @cnt + 1 
    END 
    IF @InputStr <> '' 
    BEGIN 
     INSERT INTO @OutStrings(Item) VALUES(@InputStr) 
    END 

    RETURN 
END 
+0

, это очень хорошо, дай мне сегодня вечером поиграть с ним, и я вернусь с ответ, спасибо за усилия ;-) – Raffaeu

+0

Вы сделали это @ danie-schoeman, мне потребовалось некоторое время, чтобы проверить его и реорганизовать мой текущий вид, поэтому с вашим решением я теперь использую функцию таблицы, а не вид больше, но Я должен сказать, что он работает, это быстро и охватывает все шаблоны. Благодаря!! – Raffaeu

+1

Рад, что я смог помочь. –

-1

Причина, по которой вы получаете эту проблему, заключается в том, что вы используете датуpart для получения чисел в неделю, поэтому после изменения года datepart возвращается к 1. Это должно быть изменено ... Я сделал это изменение и даты теперь переходите в порядок последовательности, проверьте это. Это, кстати, первый CTE.

/* 
    Display Patterns 
*/ 
select * from Pattern 

DECLARE @mindate DATE  = (SELECT MIN(StartDate) FROM Pattern) 
DECLARE @maxmindate DATE = (SELECT MAX(StartDate) FROM Pattern) 
DECLARE @maxcount INT  = (SELECT MAX(RecurrenceCount) FROM Pattern) 
DECLARE @maxdate DATE  = DATEADD(WK, @maxcount + 10, @maxmindate) 

declare @minWeekPart INT = DATEPART(WK,@MinDate) 
/* 
    CTE to generate required occurrences 
*/ 
;With cteKeyDate As (
    Select 
     KeyStartDate = @MinDate, 
     KeyDOW = DateName(WEEKDAY, @MinDate), 
     KeyWeek = @minWeekPart 
    Union All 
    Select 
     KeyStartDate = DateAdd(DD, 1, df.KeyStartDate) , 
     KeyDOW = DateName(WEEKDAY,DateAdd(DD, 1, df.KeyStartDate)), 
     KeyWeek= @minWeekPart + datediff(WK,@MinDate,DateAdd(DD, 1, df.KeyStartDate)) 
    From cteKeyDate DF 
    Where DF.KeyStartDate <= @MaxDate 
) 
--select * from cteKeyDate 
-- order by 1 
--Option (maxrecursion 32767) 


SELECT 
    Id, KeyStartDate, KeyDow, KeyWeek, RowNr, OccNr = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY StartDate) 
FROM 
    (Select 
     A.Id 
     ,A.StartDate 
     ,A.EndDate 
     ,Count = A.RecurrenceCount 
     ,Days = A.WeekDays 
     ,Every = A.Periodicity    
     ,KeyStartDate = CASE 
     /* 
      if no periodicity (1) then it is sequential 
      if periodicity, first week doesn't apply (MIN KeyWeek) 
     */ 
     WHEN A.Periodicity = 1 
      OR (Periodicity <> 1 AND (SELECT MIN(C.StartingWeek) FROM Pattern AS C WHERE C.Id = A.Id) = KeyWeek) 
      THEN KeyStartDate 
     /* Otherwise formula ADD WEEKS => Current Week Min Week */ 
     ELSE 
      DATEADD(WK, ((A.Periodicity - 1) * (KeyWeek - (SELECT MIN(C.StartingWeek) FROM Pattern AS C WHERE C.Id = A.Id))) , KeyStartDate) 
     END 
     ,KeyDow 
     ,KeyWeek 
     ,RowNr = Row_Number() over (Partition By A.Id Order By B.KeyStartDate) 
     ,Periodicity = A.Periodicity 
    from 
     Pattern A 
     Join cteKeyDate B on B.KeyStartDate >= DATEADD(DAY, -1, A.StartDate) and Charindex(KeyDOW, A.WeekDays) > 0 
    ) Final     
Where 
    RowNr <= Count AND Id = 1 
    order by 2 
Option (maxrecursion 32767) 

я запустить его в rextester и вы можете найти его здесь ->http://rextester.com/GWEY37271

+0

Привет, на самом деле я протестировал и воспроизвел ту же ошибку, я сделал снимок экрана. После недели 53 ваш скрипт работает, увеличивая число недель, но даты ошибочно вычисляются: https://snag.gy/MoNBxw.jpg – Raffaeu

+1

Ваш скриншот для меня не так понятен, какая ошибка сейчас 16-я дата 26-го может 2017 это не так, и это в следующую пятницу после воскресенья ... он отлично вписывается. Какая строка ошибочна. Можете ли вы, пожалуйста, позвольте мне ... о, кстати, ссылка, которую я даю, неправильно это указывает на .yours вместо моего ... – Surendra

+0

Строка 15 говорит 21 мая-2017, но это должно быть 23 апреля 2017 года – Raffaeu