Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Hiển thị ngày sự kiện tiếp theo

Bước đầu tiên của bạn là lấy ngày bắt đầu sự kiện của bạn với mỗi sự kiện và khoảng thời gian lặp lại, để làm điều này, bạn có thể sử dụng:

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

Điều này mang lại:

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

Để lặp lại điều này, bạn sẽ cần một bảng số để kết hợp chéo với nhau, nếu bạn không có bảng đó, có một số cách để tạo một bảng ngay lập tức, vì lý do đơn giản, tôi sẽ sử dụng:

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

Để đọc thêm, Aaron Bertrand đã thực hiện một số so sánh chuyên sâu về cách tạo danh sách số liên tiếp:

  • Tạo một tập hợp hoặc chuỗi không có vòng lặp - part1
  • Tạo một tập hợp hoặc chuỗi không có vòng lặp - part2
  • Tạo một tập hợp hoặc trình tự không có vòng lặp - phần 3

Nếu chúng tôi giới hạn bảng số của mình chỉ từ 0 - 5 và chỉ xem xét sự kiện đầu tiên, kết hợp chéo cả hai sẽ cho:

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

Sau đó, bạn có thể nhận được sự xuất hiện của mình bằng cách thêm RepeatInterval * Number đến thời gian bắt đầu sự kiện:

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;

Điều này mang lại kết quả mong đợi của bạn:

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

Ví dụ trên SQL Fiddle

Tôi nghĩ rằng lược đồ bạn có vẫn còn nghi vấn, hãy tham gia vào:

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

tốt nhất là mỏng manh. Tôi nghĩ rằng bạn sẽ tốt hơn nhiều nếu lưu trữ ngày bắt đầu và khoảng thời gian lặp lại được kết hợp với nó cùng nhau:

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

Điều này sẽ đơn giản hóa dữ liệu của bạn thành:

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

Nó cũng cho phép bạn thêm ngày kết thúc để lặp lại, tức là nếu bạn chỉ muốn nó lặp lại trong một tuần. Sau đó, truy vấn của bạn sẽ mô phỏng thành:

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;

Ví dụ trên SQL Fiddle

Tôi sẽ không cung cấp cho bạn lược đồ đầy đủ của tôi về cách tôi đã đạt được điều này trong quá khứ, nhưng tôi sẽ đưa ra một ví dụ rất chi tiết, từ đó bạn có thể hy vọng xây dựng cho riêng mình. Tôi sẽ chỉ thêm một ví dụ cho một sự kiện diễn ra hàng tuần vào Thứ Hai-Thứ Sáu:

Trong ER RepeatEvent ở trên lưu trữ thông tin cơ bản cho sự kiện lặp lại, sau đó tùy thuộc vào loại lặp lại (Hàng ngày, hàng tuần, hàng tháng) mà một hoặc nhiều bảng khác được điền. Ví dụ về một sự kiện hàng tuần, nó sẽ lưu trữ tất cả các ngày trong tuần mà nó lặp lại trong bảng RepeatDay . Nếu điều này chỉ cần được giới hạn trong những tháng nhất định, bạn có thể lưu trữ những tháng này trong RepeatMonth , và như vậy.

Sau đó, sử dụng bảng lịch, bạn có thể nhận được tất cả các ngày có thể có sau ngày đầu tiên và chỉ giới hạn những ngày này phù hợp với ngày trong tuần / tháng trong năm, v.v.:

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

Ví dụ trên SQL Fiddle

Đây chỉ là phần trình bày rất cơ bản về cách tôi triển khai nó, chẳng hạn như tôi thực sự đã sử dụng hoàn toàn xem mọi truy vấn cho dữ liệu lặp lại để mọi sự kiện không có mục nhập nào trong RepeatDayOfWeek sẽ được cho là lặp lại hàng ngày, thay vì không bao giờ. Cùng với tất cả các chi tiết khác trong câu trả lời này và các câu trả lời khác, hy vọng bạn sẽ có nhiều hơn đủ để bắt đầu.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Chèn tất cả các giá trị của một bảng vào một bảng khác trong SQL

  2. Ký hiệu dấu chấm trong SQL

  3. Thu thập số lượng từ một truy vấn SQL

  4. Sử dụng @@ IDENTITY để trả lại giá trị nhận dạng được chèn lần cuối trong SQL Server

  5. ListAGG trong SQLSERVER