Сложный запрос SQL Server - PullRequest
0 голосов
/ 13 марта 2012

Я пытаюсь написать запрос SQL (Server), который будет возвращать все события за текущий день, и для всех событий, для которых столбец recurring = 1, я хочу, чтобы он возвращал это событие в день его обработки. проводится и в течение последующих 52 недель после события.

Мои таблицы структурированы следующим образом:

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

Код, который я имею до сих пор, выглядит следующим образом:

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

Это приводит к тому, что при запросе даты события, сохраненного в базе данных, возвращается это событие и 52 недели событий. Когда кто-то запрашивает событие через неделю после этого, ничего не возвращается.

Ответы [ 2 ]

1 голос
/ 13 марта 2012

Самое простое решение было бы изменить следующие 2 строки

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

на

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

Однако я бы посоветовал поместить весь этот SQL в хранимую процедуру, SQL-Сервер будет кэшировать планы выполнения, и это приведет (немного) к повышению производительности.

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

Затем вы можете вызвать это с помощью

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

Это указывает на необходимость перебора групп в вашем коде.Если это действительно требование, вы можете изменить хранимую процедуру, чтобы в качестве параметра использовать @GroupID, и при необходимости изменить операторы select / where.

Я принял знание Общих выражений таблиц .Они не обязаны делать запрос, они просто делают вещи немного более разборчивыми, на мой взгляд.Я могу переписать это без них, если потребуется.

1 голос
/ 13 марта 2012

Я бы сравнивал свои параметры по одному с каким-то тривиальным SQL, просто чтобы исключить их в качестве возможных виновников.Как то так:

var result = db.Query("select r=cast(@0 as varchar(80))",username);
var result = db.Query("select r=cast(@0 as int)",newGroupID);
var result = db.Query("select r=cast(@0 as datetime)",start);
var result = db.Query("select r=cast(@0 as datetime)",end);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...