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のみに制限し、最初のイベントのみを見ると、2つをクロス結合すると次のようになります。

    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フィドルの例

    あなたが持っているスキーマは疑わしいと思いますが、参加してください:

    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
    

    また、リピートに終了日を追加することもできます。つまり、1週間だけリピートしたい場合です。これにより、クエリは次のように簡略化されます。

    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フィドルの例

    私が過去にこれをどのように達成したかについての私の完全なスキーマをあなたに与えることはしませんが、私はあなたがうまくいけばあなた自身を構築することができる非常に切り詰められた例を与えます。月曜から金曜に毎週発生するイベントの例のみを追加します:

    上記のERRepeatEventには、定期的なイベントの基本情報が格納され、繰り返しの種類(日次、週次、月次)に応じて、他の1つ以上のテーブルにデータが入力されます。毎週のイベントの例では、繰り返されるすべての曜日がテーブル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フィドルの例

    これは、実装方法の非常に基本的な表現にすぎません。たとえば、実際に使用したのは、繰り返しデータのクエリを完全に表示して、RepeatDayOfWeekにエントリがないイベントを表示することです。 決して繰り返さないのではなく、毎日繰り返すと想定されます。この回答や他の回答の他のすべての詳細に加えて、開始するのに十分なものがあることを願っています。



    1. pgAdmin IIIクエリ結果が短縮されるのはなぜですか?

    2. PostgreSQLは構成/confファイルをどこに保存しますか?

    3. jQueryリモートメソッドの使用を検証して、ユーザー名がすでに存在するかどうかを確認します

    4. EntityFrameworkでのMySQLの使用