Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I am trying to write an SQL (Server) query which will return all events on a current day, and for all events where the column recurring= 1, I want it to return this event on the day it is being held and for the subsequent 52 weeks following the event.

My tables are structured as followed :

Event
{
    event_id (PK)
    title,
    description,
    event_start DATETIME,
    event_end DATETIME,
    group_id,
    recurring
}

Users
{
    UserID (PK)
    Username
}

Groups
{
    GroupID (PK)
    GroupName
}

Membership
{
    UserID (FK)
    GroupID (FK)
}

The code I have thus far is as follows :

     var db = Database.Open("mPlan");
    string username = HttpContext.Current.Request.Cookies.Get("mpUsername").Value;
    var listOfGroups = db.Query("SELECT GroupID FROM Membership WHERE UserID = (SELECT UserID from Users WHERE Username = @0 )",  username);
    foreach(var groupID in listOfGroups)
        {
            int newGroupID = groupID.GroupID;
            var result = db.Query(
                @"SELECT e.event_id, e.title, e.description, e.event_start, e.event_end, e.group_id, e.recurring
                FROM   event e
                JOIN   Membership m ON m.GroupID = e.group_id
                WHERE  e.recurring = 0
                AND    m.GroupID = @0
                AND    e.event_start >= @1
                AND    e.event_end <= @2
                UNION ALL
                SELECT e.event_id, e.title, e.description, DATEADD(week, w.weeks, e.event_start), DATEADD(week, w.weeks, e.event_end), e.group_id, e.recurring
                FROM   event e
            JOIN   Membership m ON m.GroupID = e.group_id
            CROSS JOIN 
                ( SELECT  row_number() OVER (ORDER BY Object_ID) AS weeks
                FROM SYS.OBJECTS
                ) AS w
                WHERE  e.recurring = 1
                AND    m.GroupID = @3
                AND DATEADD(WEEK, w.Weeks, e.event_start) >= @4 
                AND DATEADD(WEEK, w.Weeks, e.event_end) <= @5", newGroupID, start, end, newGroupID, start, end
            );

This results in when one queries for the date of the event stored in the database, this event and 52 weeks of events are returned. When one queries for the event the week after this one, nothing is returned.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
157 views
Welcome To Ask or Share your Answers For Others

1 Answer

The simplest solution would be to alter the following 2 lines

AND    e.event_start >= @4
AND    e.event_end <= @5"

to

AND    DATEADD(WEEK, w.Weeks, e.event_start) >= @4
AND    DATEADD(WEEK, w.Weeks, e.event_end) <= @5"

However, I'd advise putting all this SQL into a stored procedure, SQL-Server will cache the execution plans and it will result in (slightly) better performance.

CREATE PROCEDURE dbo.GetEvents @UserName VARCHAR(50), @StartDate DATETIME, @EndDate DATETIME
AS
BEGIN
-- DEFINE A CTE TO GET ALL GROUPS ASSOCIATED WITH THE CURRENT USER
;WITH Groups AS 
(   SELECT  GroupID 
    FROM    Membership  m
            INNER JOIN Users u
                ON m.UserID = u.UserID
    WHERE   Username = @UserName
    GROUP BY GroupID
),
-- DEFINE A CTE TO GET ALL EVENTS FOR THE GROUPS DEFINED ABOVE
AllEvents AS
(   SELECT  e.*
    FROM    event e
            INNER JOIN Groups m 
                ON m.GroupID = e.group_id
    UNION ALL
    SELECT  e.event_id, e.title, e.description, DATEADD(WEEK, w.weeks, e.event_start), DATEADD(WEEK, w.weeks, e.event_end), e.group_id, e.recurring
    FROM    event e
            INNER JOIN Groups m 
                ON m.GroupID = e.group_id
            CROSS JOIN 
            (   SELECT  ROW_NUMBER() OVER (ORDER BY Object_ID) AS weeks
                FROM    SYS.OBJECTS
            ) AS w
    WHERE  e.recurring = 1
)   
-- GET ALL EVENTS WHERE THE EVENTS FALL IN THE PERIOD DEFINED
SELECT  *
FROM    AllEvents
WHERE   Event_Start >= @StartDate
AND     Event_End <= @EndDate

END

Then you can call this with

var result = db.Query("EXEC dbo.GetEvents @0, @1, @2", username, start, end);

This elimates the need to iterate over groups in your code behind. If this is actually a requirement then you could modify the stored procedure to take @GroupID as a parameter, and change the select statements/where clauses as necessary.

I have assumed knowledge of Common Table Expressions. They are not required to make the query work, they just make things slightly more legible in my opinion. I can rewrite this without them if required.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...