sql >> Databáze >  >> RDS >> Sqlserver

Zobrazit datum další události

Vaším prvním krokem je získat data zahájení události s každou událostí a interval opakování, k tomu můžete použít:

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

To dává:

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 se to opakovalo, budete potřebovat tabulku čísel, ke které se budete křížově připojit, pokud ji nemáte, existuje několik způsobů, jak ji vygenerovat za běhu, pro jednoduchost použiji:

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

Pro další čtení Aaron Bertrand provedl několik hloubkových srovnání způsobů generování sekvenčních seznamů čísel:

  • Vygenerujte sadu nebo sekvenci bez smyček – část 1
  • Vygenerujte sadu nebo sekvenci bez smyček – část 2
  • Vygenerujte sadu nebo sekvenci bez smyček – část 3

Pokud omezíme naši tabulku čísel pouze na 0 - 5 a podíváme se pouze na první událost, křížové spojení těchto dvou dá:

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

Poté můžete svůj výskyt získat přidáním RepeatInterval * Number do času začátku události:

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;

To dává očekávaný výstup:

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

Příklad na SQL Fiddle

Myslím, že schéma, které máte, je sporné, připojení:

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

je v nejlepším případě chabý. Myslím, že by bylo mnohem lepší uložit počáteční datum a interval opakování s ním spojený:

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

Tím by se vaše data zjednodušila na:

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

Umožňuje vám také přidat k opakování koncové datum, tj. pokud chcete, aby se opakovalo pouze jeden týden. To pak váš dotaz zjednoduší na:

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;

Příklad na SQL Fiddle

Neposkytnu vám své úplné schéma, jak jsem toho v minulosti dosáhl, ale uvedu velmi okleštěný příklad, ze kterého si snad můžete sestavit svůj vlastní. Přidám pouze příklad události, která se koná každý týden v pondělí až pátek:

Ve výše uvedeném ER RepeatEvent jsou uloženy základní informace pro opakující se událost, pak se v závislosti na typu opakování (Denně, týdně, měsíčně) vyplní jedna nebo více dalších tabulek. V příkladu týdenní události by se uložily všechny dny v týdnu, ve kterých se opakuje, do tabulky RepeatDay . Pokud to bylo potřeba omezit pouze na určité měsíce, můžete tyto měsíce uložit do RepeatMonth , a tak dále.

Potom pomocí kalendářní tabulky můžete získat všechna možná data po prvním datu a omezit je pouze na data, která odpovídají dni v týdnu/měsíci v roce atd.:

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

Příklad na SQL Fiddle

Toto je jen velmi základní znázornění toho, jak jsem to implementoval, například jsem ve skutečnosti použil úplně zobrazení jakéhokoli dotazu na opakující se data, takže jakákoli událost bez záznamů v RepeatDayOfWeek předpokládá se, že se bude opakovat každý den, spíše než nikdy. Spolu se všemi dalšími podrobnostmi v této a dalších odpovědích byste snad měli mít víc než dost, abyste mohli začít.



  1. vytvořte dynamický dotaz SQL pomocí knihovny psycopg2 python a pomocí dobrých nástrojů pro typ konverze

  2. chyba při vkládání android.database.sqlite.sqliteconstraintexception error code 19 constraint failed

  3. Jak najít záznamy s NULL ve sloupci

  4. Moje cesta OOW15 je dokončena