Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Wyświetl datę następnego wydarzenia

Pierwszym krokiem jest ustalenie dat rozpoczęcia wydarzeń dla każdego wydarzenia i interwału powtarzania, w tym celu możesz użyć:

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));

Daje to:

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

Aby to powtórzyć, będziesz potrzebować tabeli liczb do łączenia krzyżowego, jeśli nie masz takiej, istnieje kilka sposobów na jej wygenerowanie w locie, dla uproszczenia użyję:

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

Aby dowiedzieć się więcej, Aaron Bertrand dokonał kilku dogłębnych porównań sposobów generowania sekwencyjnych list liczb:

  • Generuj zestaw lub sekwencję bez pętli – część 1
  • Generuj zestaw lub sekwencję bez pętli – część 2
  • Generuj zestaw lub sekwencję bez pętli – część 3

Jeśli ograniczymy naszą tabelę liczb tylko do 0 - 5 i spojrzymy tylko na pierwsze zdarzenie, skrzyżowanie tych dwóch da:

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

Następnie możesz uzyskać swoje wystąpienie, dodając RepeatInterval * Number do czasu rozpoczęcia wydarzenia:

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;

Daje to oczekiwany wynik:

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

Przykład w SQL Fiddle

Myślę, że schemat, który masz, jest wątpliwy, dołączanie:

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

jest w najlepszym razie wątły. Myślę, że znacznie lepiej byłoby przechowywać razem datę rozpoczęcia i powiązany z nią interwał powtórzeń:

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)
);

To uprościłoby Twoje dane do:

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

Pozwala również dodać datę zakończenia do powtórki, tj. jeśli chcesz, aby powtarzała się tylko przez tydzień. To wtedy Twoje zapytanie upraszcza się do:

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;

Przykład w SQL Fiddle

Nie podam pełnego schematu tego, jak udało mi się to osiągnąć w przeszłości, ale podam bardzo okrojony przykład, z którego, miejmy nadzieję, można zbudować własny. Dodam tylko przykład dla wydarzenia, które odbywa się co tydzień od poniedziałku do piątku:

W powyższym ER RepeatEvent przechowuje podstawowe informacje o powtarzającym się zdarzeniu, a następnie w zależności od typu powtórzenia (codziennie, co tydzień, co miesiąc) wypełniana jest jedna lub więcej innych tabel. Na przykład cotygodniowe wydarzenie, przechowuje wszystkie dni tygodnia, w których się powtarza, w tabeli RepeatDay . Jeśli musiałoby to być ograniczone tylko do określonych miesięcy, możesz przechowywać te miesiące w RepeatMonth i tak dalej.

Następnie korzystając z tabeli kalendarza, możesz uzyskać wszystkie możliwe daty po pierwszej dacie i ograniczyć je tylko do tych dat, które pasują do dnia tygodnia/miesiąca roku itp.

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);

Przykład w SQL Fiddle

Jest to tylko bardzo podstawowa reprezentacja tego, jak to zaimplementowałem, na przykład faktycznie użyłem całkowicie widoków dowolnego zapytania dla powtarzających się danych, tak aby każde zdarzenie bez wpisów w RepeatDayOfWeek zakłada się, że będzie się powtarzać codziennie, a nie nigdy. Wraz ze wszystkimi innymi szczegółami w tej i innych odpowiedziach, miejmy nadzieję, że powinieneś mieć wystarczająco dużo, aby zacząć.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL Server:Szybkie zapytania, ale wolne od procedury

  2. Różne sposoby wykorzystania funkcji daty SQL CONVERT

  3. Funkcje rankingowe w SQL Server

  4. Błąd serwera SQL:ciąg lub dane binarne zostaną obcięte

  5. Przywracanie głównej bazy danych programu SQL Server