【问题标题】:Complicated SQL Server query复杂的 SQL Server 查询
【发布时间】:2012-03-12 20:05:05
【问题描述】:

我正在尝试编写一个 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 周的事件。当一个人在这一周后的一周查询该事件时,没有返回任何内容。

【问题讨论】:

  • 另一个问题中的答案为您指明了错误的方向......它与您的 SQL 无关 - 相反,您尝试传入groupID,这是一个DynamicRecord,到db.Query。您需要从前一个结果集中获取值,并将其转换为 int。或者,我建议重新编写这段代码,因为我看不出为什么你会使用 foreach 而不是在你的主查询中进行额外的连接......
  • @MichaelFredrickson,感谢您的回复。我正在尝试这样做,但正如您所说,我作为 SQL Server 和 C# 的初学者正在苦苦挣扎。您是否介意建议我可以使用您建议的连接编写一个替代查询,因为我几乎没有编写这样一段代码所需的技能?
  • @MichaelFredrickson,我真的不确定在这种情况下连接是如何工作的,因为在 Groups 表中,可以有同一个 UserID 的多个实例!
  • @MichaelFredrickson 我已经编辑了上面的代码,这样 C# 中的 groupID 就不会出现错误。查询返回 OK,但即使我已将它们添加到我的表中也没有结果。

标签: c# sql sql-server database


【解决方案1】:

最简单的解决方案是更改以下 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-Server 将缓存执行计划,它会导致(稍微)更好的性能。

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 子句。

我假设知道Common Table Expressions。他们不需要使查询工作,在我看来,它们只是让事情变得更清晰。如果需要,我可以在没有它们的情况下重写它。

【讨论】:

  • 非常感谢您的回答 - 作为初学者,这对我真的很有帮助。谢谢你。我正在尝试简单地编辑我的代码,以便它首先工作(您提供的第一个更简单的解决方案)。我仍然有一个问题,它目前没有返回任何事件。我已经编辑了上面的代码以显示我当前正在运行的内容。在我着手进行您建议的其他更改之前,有什么想法吗? :)
  • @GarethdD 另外,这应该是 INNER JOIN Groups m ON m.GroupID = e.group_id 吗?我认为?我完全在猜测,因为这就是我编译它的方式:)
  • 现在我正在编译它,但有一个小错误 - 每个日期都返回事件......添加日期除外!
  • @SimonKiely 是的,抱歉,它应该是 m.GroupID 而不是 e.GroupID。我已经更新了我的答案,我还更正了第一个重复事件未返回的问题。
  • 太棒了。我已经在这个代码上工作了几个星期,而你在几个小时内就完成了。极好的。非常非常感谢。
【解决方案2】:

我会根据一些琐碎的 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);

【讨论】:

  • 这是一个错误,但是我已经纠正了它,不幸的是我的代码仍然无法工作!
  • 是否可以调试打印 SQL 并在 SSMS 中运行?
  • @是否属于第二个查询的前面?我注意到它不在第一个前面。 @"SELECT
  • 是的,确实如此,这只是表明它是一个多行查询,我相信!
  • 非常感谢您的回复。我已经按照您的建议编辑了我的代码,现在我遇到了一个新错误。当查询存储在数据库中的事件日期时,将返回该事件和 52 周的事件。当一个人在该事件之后的一周查询该事件时,没有返回任何内容。有什么方法可以根据生成的日期时间检查参数“开始”和“结束”?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-03
相关资源
最近更新 更多