sql >> Database teknologi >  >> RDS >> Sqlserver

Vis næste begivenhedsdato

Dit første skridt er at få din begivenheds startdatoer med hver begivenhed, og gentagelsesintervallet, for at gøre dette kan du bruge:

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

Dette giver:

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

For at få dette til at gentage skal du bruge en taltabel til at krydse join til, hvis du ikke har en, er der en række måder at generere en på i farten, af overskuelighedsgrunde vil jeg bruge:

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

For yderligere læsning har Aaron Bertrand lavet nogle dybdegående sammenligningsmetoder til at generere sekventielle lister over tal:

  • Generer et sæt eller en sekvens uden loops – del1
  • Generer et sæt eller en sekvens uden loops – del2
  • Generer et sæt eller en sekvens uden loops – del3

Hvis vi begrænser vores taltabel til kun 0 - 5 og kun ser på den første begivenhed, vil krydsforbindelse af de to give:

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

Så kan du få din forekomst ved at tilføje RepeatInterval * Number til begivenhedens starttidspunkt:

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;

Dette giver dit forventede output:

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

Eksempel på SQL Fiddle

Jeg tror dog, at skemaet du har er tvivlsomt, joinforbindelsen på:

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

er i bedste fald spinkel. Jeg tror, ​​du ville være meget bedre stillet med at gemme startdatoen og gentagelsesintervallet, der er forbundet med det, sammen:

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

Dette ville forenkle dine data til:

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

Det giver dig også mulighed for at tilføje en slutdato til din gentagelse, dvs. hvis du kun ønsker, at den skal gentages i en uge. Dette forenkler din forespørgsel til:

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;

Eksempel på SQL Fiddle

Jeg vil ikke give dig mit fulde skema for, hvordan jeg har opnået dette tidligere, men jeg vil give et meget nedskåret eksempel, som du forhåbentlig kan bygge dit eget ud fra. Jeg vil kun tilføje et eksempel for en begivenhed, der finder sted ugentligt mandag-fredag:

I ovenstående ER gemmer RepeatEvent de grundlæggende oplysninger for den tilbagevendende begivenhed, og afhængigt af gentagelsestypen (Daglig, ugentlig, månedlig) er en eller flere af de andre tabeller udfyldt. I et eksempel på en ugentlig begivenhed vil den gemme alle ugens dage, som den gentages i tabellen RepeatDay . Hvis dette kun skulle begrænses til bestemte måneder, kunne du gemme disse måneder i RepeatMonth , og så videre.

Så ved hjælp af en kalendertabel kan du få alle mulige datoer efter den første dato, og begrænse disse til kun de datoer, der matcher ugedagen/måneden i året osv.:

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

Eksempel på SQL Fiddle

Dette er kun en meget grundlæggende repræsentation af, hvordan jeg implementerede det, for eksempel brugte jeg faktisk fuldstændigt visninger af enhver forespørgsel for de gentagne data, så enhver begivenhed uden indtastninger i RepeatDayOfWeek ville blive antaget at gentage hver dag, snarere end aldrig. Sammen med alle de andre detaljer i dette og andre svar, skulle du forhåbentlig have mere end nok til at komme i gang.



  1. Hvorfor enhver lille virksomhed har brug for en database

  2. Udvikling af fejltolerance i PostgreSQL

  3. Sådan viser du alle datoer mellem to datoer

  4. Er der et alvorligt præstationshit for at bruge udenlandske nøgler i SQL Server?