sql >> Databasteknik >  >> RDS >> Sqlserver

Visa nästa evenemangsdatum

Ditt första steg är att få ditt evenemangs startdatum med varje evenemang, och upprepningsintervallet, för att göra detta kan du använda:

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

Detta ger:

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

För att få detta att upprepas behöver du en siffertabell att gå med i, om du inte har en finns det flera sätt att generera en direkt, av enkelhetsskäl kommer jag att använda:

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

För ytterligare läsning har Aaron Bertrand gjort några djupgående jämförelser för att skapa sekventiella listor med nummer:

  • Generera en uppsättning eller sekvens utan loopar – del1
  • Generera en uppsättning eller sekvens utan loopar – del2
  • Generera en uppsättning eller sekvens utan loopar – del3

Om vi ​​begränsar vår siffertabell till endast 0 - 5, och bara tittar på den första händelsen, kommer korskoppling av de två att ge:

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

Sedan kan du få din förekomst genom att lägga till RepeatInterval * Number till evenemangets starttid:

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;

Detta ger din förväntade produktion:

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

Exempel på SQL Fiddle

Jag tror dock att schemat du har är tveksamt, gå med på:

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

är i bästa fall tunn. Jag tror att du skulle vara mycket bättre av att lagra startdatumet och upprepningsintervallet för det tillsammans:

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

Detta skulle förenkla dina uppgifter till:

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

Det låter dig också lägga till ett slutdatum till din repetition, d.v.s. om du bara vill att den ska upprepas i en vecka. Då förenklas din fråga till:

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;

Exempel på SQL Fiddle

Jag kommer inte att ge dig mitt fullständiga schema för hur jag har uppnått detta tidigare, men jag kommer att ge ett mycket nedskäret exempel, från vilket du förhoppningsvis kan bygga ditt eget. Jag kommer bara att lägga till ett exempel för en händelse som inträffar varje vecka på mån-fre:

I ovanstående ER lagrar RepeatEvent den grundläggande informationen för den återkommande händelsen, och beroende på upprepningstypen (dagligen, veckovis, månadsvis) fylls en eller flera av de andra tabellerna i. I ett exempel på en veckohändelse skulle den lagra alla veckodagar som den upprepas i tabellen RepeatDay . Om detta behövde begränsas till endast vissa månader kan du lagra dessa månader i RepeatMonth , och så vidare.

Med hjälp av en kalendertabell kan du sedan få alla möjliga datum efter det första datumet, och begränsa dessa till endast de datum som matchar veckodagen/månaden på året etc:

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

Exempel på SQL Fiddle

Det här är bara en mycket grundläggande representation av hur jag implementerade det, till exempel använde jag faktiskt helt visningar av alla frågor för upprepad data så att alla händelser utan poster i RepeatDayOfWeek skulle antas upprepas varje dag, snarare än aldrig. Tillsammans med alla andra detaljer i detta och andra svar borde du förhoppningsvis ha mer än tillräckligt för att komma igång.



  1. 4 funktioner som extraherar mikrosekunder från ett tidsvärde i MariaDB

  2. Varför denna viloläge mall bulkUpdate inte fungerar

  3. proxysql-admin Alternativ - ClusterControl ProxySQL GUI

  4. Hur hittar man alla tabeller som har främmande nycklar som refererar till en viss table.column och har värden för dessa främmande nycklar?