Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Показване на датата на следващото събитие

Първата ви стъпка е да получите началните дати на вашето събитие с всяко събитие и интервала на повторение, за да направите това, можете да използвате:

SELECT  EventID = e.ID, 
        e.Name, 
        StartDateTime = DATEADD(SECOND, rs.Meta_Value, '19700101'),
        RepeatInterval = ri.Meta_Value
FROM    dbo.Events e
        INNER JOIN dbo.Events_Meta rs
            ON rs.Event_ID = e.ID
            AND rs.Meta_Key = 'repeat_start'
        INNER JOIN dbo.Events_Meta ri
            ON ri.Event_ID = e.ID
            AND ri.Meta_Key = 'repeat_interval_' + CAST(e.ID AS VARCHAR(10));

Това дава:

EventID | Name         | StartDateTime       | RepeatInterval
--------+--------------+---------------------+-----------------
   1    | Billa Vist   | 2014-01-03 10:00:00 |     604800
   1    | Billa Vist   | 2014-01-04 18:00:00 |     604800

За да накарате това да се повтори, ще ви е необходима таблица с числа, към която да се присъедините, ако нямате такава, има няколко начина да генерирате такава в движение, от съображения за простота ще използвам:

WITH Numbers AS
(   SELECT  Number = ROW_NUMBER() OVER(ORDER BY a.object_id) - 1
    FROM    sys.all_objects a
)
SELECT  Number
FROM    Numbers;

За по-нататъшно четене, Aaron Bertrand е направил някои задълбочени сравнения начини за генериране на последователни списъци с числа:

  • Генерирайте набор или последователност без цикли – част1
  • Генерирайте набор или последователност без цикли – част2
  • Генерирайте набор или последователност без цикли – част3

Ако ограничим нашата таблица с числа само до 0 - 5 и погледнем само първото събитие, кръстосаното свързване на двете ще даде:

EventID | Name         | StartDateTime       | RepeatInterval | Number
--------+--------------+---------------------+----------------+---------
   1    | Billa Vist   | 2014-01-03 10:00:00 |     604800     |    0
   1    | Billa Vist   | 2014-01-03 10:00:00 |     604800     |    1
   1    | Billa Vist   | 2014-01-03 10:00:00 |     604800     |    2
   1    | Billa Vist   | 2014-01-03 10:00:00 |     604800     |    3
   1    | Billa Vist   | 2014-01-03 10:00:00 |     604800     |    4
   1    | Billa Vist   | 2014-01-03 10:00:00 |     604800     |    5

След това можете да получите своето появяване, като добавите RepeatInterval * Number до началния час на събитието:

DECLARE @EndDate DATETIME = '20140130';

WITH EventData AS
(   SELECT  EventID = e.ID, 
            e.Name, 
            StartDateTime = DATEADD(SECOND, rs.Meta_Value, '19700101'),
            RepeatInterval = ri.Meta_Value
    FROM    dbo.Events e
            INNER JOIN dbo.Events_Meta rs
                ON rs.Event_ID = e.ID
                AND rs.Meta_Key = 'repeat_start'
            INNER JOIN dbo.Events_Meta ri
                ON ri.Event_ID = e.ID
                AND ri.Meta_Key = 'repeat_interval_' + CAST(rs.ID AS VARCHAR(10))
), Numbers AS
(   SELECT  Number = ROW_NUMBER() OVER(ORDER BY a.object_id) - 1
    FROM    sys.all_objects a
)
SELECT  e.EventID,
        e.Name,
        EventDate = DATEADD(SECOND, n.Number * e.RepeatInterval, e.StartDateTime)
FROM    EventData e
        CROSS JOIN Numbers n
WHERE   DATEADD(SECOND, n.Number * e.RepeatInterval, e.StartDateTime) < @EndDate
ORDER BY e.EventID, EventDate;

Това дава очаквания резултат:

EVENTID | NAME          | EVENTDATE
--------+---------------+--------------------------------
   1    | Billa Vist    | January, 03 2014 10:00:00+0000
   1    | Billa Vist    | January, 04 2014 18:00:00+0000
   1    | Billa Vist    | January, 10 2014 10:00:00+0000
   1    | Billa Vist    | January, 11 2014 18:00:00+0000
   1    | Billa Vist    | January, 17 2014 10:00:00+0000
   1    | Billa Vist    | January, 18 2014 18:00:00+0000
   1    | Billa Vist    | January, 24 2014 10:00:00+0000
   1    | Billa Vist    | January, 25 2014 18:00:00+0000

Пример за SQL Fiddle

Мисля обаче, че схемата, която имате, е съмнителна, присъединяването на:

Meta_Key = 'repeat_interval_' + CAST(rs.ID AS VARCHAR(10))

е крехък в най-добрия случай. Мисля, че би било много по-добре да съхраните началната дата и интервала за повтаряне, свързан с нея, заедно:

CREATE TABLE dbo.Events_Meta
(       ID INT IDENTITY(1, 1) NOT NULL,
        Event_ID INT NOT NULL,
        StartDateTime DATETIME2 NOT NULL,
        IntervalRepeat INT NULL, -- NULLABLE FOR SINGLE EVENTS
        RepeatEndDate DATETIME2 NULL, -- NULLABLE FOR EVENTS THAT NEVER END
    CONSTRAINT PK_Events_Meta__ID PRIMARY KEY (ID),
    CONSTRAINT FK_Events_Meta__Event_ID FOREIGN KEY (Event_ID) REFERENCES dbo.Events (ID)
);

Това ще опрости данните ви до:

EventID | StartDateTime       | RepeatInterval | RepeatEndDate
--------+---------------------+----------------+---------------
   1    | 2014-01-03 10:00:00 |    604800      |     NULL
   1    | 2014-01-04 18:00:00 |    604800      |     NULL

Освен това ви позволява да добавите крайна дата към повторението си, т.е. ако искате да се повтаря само за една седмица. След това вашата заявка се опростява до:

DECLARE @EndDate DATETIME = '20140130';
WITH Numbers AS
(   SELECT  Number = ROW_NUMBER() OVER(ORDER BY a.object_id) - 1
    FROM    sys.all_objects a
)
SELECT  e.ID,
        e.Name,
        EventDate = DATEADD(SECOND, n.Number * em.IntervalRepeat, em.StartDateTime) 
FROM    Events e
        INNER JOIN Events_Meta em
            ON em.Event_ID = e.ID
        CROSS JOIN Numbers n
WHERE   DATEADD(SECOND, n.Number * em.IntervalRepeat, em.StartDateTime) <= @EndDate
AND (   DATEADD(SECOND, n.Number * em.IntervalRepeat, em.StartDateTime) <= em.RepeatEndDate 
    OR  em.RepeatEndDate IS NULL
    )
ORDER BY EventDate;

Пример за SQL Fiddle

Няма да ви давам пълната си схема за това как съм постигнал това в миналото, но ще дам един много съкратен пример, от който да се надяваме, че можете да изградите свой собствен. Ще добавя само пример за събитие, което се случва всяка седмица в понеделник-пет:

В горния ER RepeatEvent съхранява основната информация за повтарящото се събитие, след което в зависимост от типа на повторение (дневно, седмично, месечно) се попълва една или повече от другите таблици. В пример за седмично събитие, той ще съхранява всички дни от седмицата, в които се повтаря в таблицата RepeatDay . Ако това трябва да бъде ограничено само до определени месеци, можете да съхранявате тези месеци в RepeatMonth , и така нататък.

След това с помощта на календарна таблица можете да получите всички възможни дати след първата дата и да ги ограничите само до онези дати, които съответстват на деня от седмицата/месеца от годината и т.н.:

WITH RepeatingEvents AS
(   SELECT  e.Name,
            re.StartDateTime,
            re.EndDateTime,
            re.TimesToRepeat,
            RepeatEventDate = CAST(c.DateKey AS DATETIME) + CAST(re.StartTime AS DATETIME),
            RepeatNumber = ROW_NUMBER() OVER(PARTITION BY re.RepeatEventID ORDER BY c.Datekey)
    FROM    dbo.Event e
            INNER JOIN dbo.RepeatEvent re
                ON e.EventID = re.EventID
            INNER JOIN dbo.RepeatType rt
                ON rt.RepeatTypeID = re.RepeatTypeID
            INNER JOIN dbo.Calendar c
                ON c.DateKey >= re.StartDate
            INNER JOIN dbo.RepeatDayOfWeek rdw
                ON rdw.RepeatEventID = re.RepeatEventID
                AND rdw.DayNumberOfWeek = c.DayNumberOfWeek
    WHERE   rt.Name = 'Weekly'
)
SELECT  Name, StartDateTime, RepeatEventDate, RepeatNumber
FROM    RepeatingEvents
WHERE   (TimesToRepeat IS NULL OR RepeatNumber <= TimesToRepeat)
AND     (EndDateTime IS NULL OR RepeatEventDate <= EndDateTime);

Пример за SQL Fiddle

Това е само много основно представяне на начина, по който го внедрих, например всъщност използвах изцяло прегледи на всяка заявка за повтарящи се данни, така че всяко събитие без записи в RepeatDayOfWeek ще се приеме, че се повтаря всеки ден, а не никога. Заедно с всички други подробности в този и други отговори, да се надяваме, че имате повече от достатъчно, за да започнете.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. sp_add_schedule срещу sp_add_jobschedule в SQL Server:Каква е разликата?

  2. Клауза WHERE срещу ON при използване на JOIN

  3. Средно несъответствие на float

  4. Възможни начини за коригиране на проблема с повреда на метаданните на SQL Server

  5. SQL Server Express Backup база данни | Как да планирате автоматизиране и почистване на SQL Express Backup