Sql server SQL查询改进-使用max和groupby进行选择 问题

Sql server SQL查询改进-使用max和groupby进行选择 问题,sql-server,nhibernate,select,group-by,max,Sql Server,Nhibernate,Select,Group By,Max,鉴于以下两个表格,我想为在给定时间跨度内(例如2010年2月)发表最新评论的帖子选择所有ID 查询结果应该只返回Post ID 1,因为Post ID 2的最新注释不在时间跨度筛选器的范围内 问题 我已经创建了下面的SELECT语句,它似乎是正确的,并处理所有抛出的测试用例 然而,为了继续提高我的SQL技能,我询问社区是否有更好的方法用于此场景,是否有改进现有语句的建议,以及/或未涵盖的边缘案例 请注意,这是对实际表格的松散翻译,旨在使问题更容易理解。无论如何,我使用的是SQLServer200

鉴于以下两个表格,我想为在给定时间跨度内(例如2010年2月)发表最新评论的帖子选择所有ID

查询结果应该只返回Post ID 1,因为Post ID 2的最新注释不在时间跨度筛选器的范围内

问题 我已经创建了下面的SELECT语句,它似乎是正确的,并处理所有抛出的测试用例

然而,为了继续提高我的SQL技能,我询问社区是否有更好的方法用于此场景,是否有改进现有语句的建议,以及/或未涵盖的边缘案例

请注意,这是对实际表格的松散翻译,旨在使问题更容易理解。无论如何,我使用的是SQLServer2005

桌子 邮递 议论 SQL命令 奖金问题 是否可以使用标准API或HQL使用NHiberate创建此查询

SELECT
    Post_Id
FROM
    Comment
GROUP BY
    Post_Id
HAVING
    MAX(Timestamp) >= '2/1/2010'
考虑在GROUP BY之后,在分组结果集上进行操作


不过我不知道NHibernate。

这应该比使用HAVING子句快一点

select distinct Post_id from Comment
where Timestamp >= '2/1/2010';

好的解决方案已经发布,但我想我应该发布一个解释,说明如何逐步简化您的查询:

最外层的子查询是冗余的

子查询的最外层是从[Post]中选择[Id],其中位中的[Id]是冗余的,因为您已经返回了一个Id列表

这就给我们留下了

SELECT comment1.[Post_Id]
FROM (  
    SELECT max([CommentNumber]) as maxComment,  
        [Post_id]  
    FROM [Comment]  
    GROUP BY [Post_id]  
) as comment2  
INNER JOIN [Comment] as comment1 on comment1.[Post_id] = comment2.[Post_id]  
WHERE comment1.[Timestamp] BETWEEN '2/1/2010 00:00:00.000' AND '2/28/2010 23:59:59.999'  
AND comment1.[CommentNumber] = comment2.maxComment  
CommentNumber的使用是多余的

不需要使用CommentNumber来获取最新的评论,因为帖子已经按时间戳排序。这意味着,我们可以选择最高的时间戳,而不是选择Id最高的注释的时间戳

这消除了再次加入评论的需要,给我们留下了:

SELECT [Post_Id], SomeColumn, SomeOtherColumn
FROM (
    SELECT max([TimeStamp]) as maxTimeStamp,
        [Post_id],
        SomeColumn,
        SomeOtherColumn
    FROM [Comment]
    GROUP BY [Post_id]
) as GroupedComments
WHERE GroupedComments.maxTimeStamp BETWEEN '2/1/2010 00:00:00.000' AND '2/28/2010 23:59:59.999'
子查询现在是多余的

现在查询已经简化了一些,应该很容易看到如何使用distinct或having语法将其进一步简化为本文发布的其他解决方案之一

使用<和>=而不是介于两者之间

只是一点小事。与其千篇一律地在2月份查找最后一个日期,不如将中间的日期拆分为a<和a>=使查询更加清晰:

WHERE GroupedComments.maxTimeStamp >= '2/1/2010'
AND GroupedComments.maxTimeStamp < '3/01/2010'

这是我结合了AakashM和Kragen的回答后,目前针对的问题:

SELECT [Id],[Text]
From [Post]
WHERE [Id] IN (
    SELECT Post_Id
    FROM Comment
    GROUP BY Post_Id
    HAVING MAX(Timestamp) >= '3/1/2010' AND MAX(Timestamp) < '4/1/2010'
)
AND [Post].[Visible] = 1
以下是如何使用Criteria API在NHibernate中表示此查询:

var subCriteria = DetachedCriteria.For<Comment>()
    .SetProjection(Projections.ProjectionList()
        .Add(Projections.GroupProperty("Post.Id")))
    .Add(Restrictions.Ge("Timestamp", new DateTime(2010, 3, 1)))
    .Add(Restrictions.Lt("Timestamp", new DateTime(2010, 4, 1)));

var criteria = session.CreateCriteria<Post>()
    .Add(Restrictions.Eq("Visible", true))
    .Add(Subqueries.PropertyIn("Id", subCriteria));

太好了,这更干净了。这很接近,但它没有满足只包括在时间范围内发表了他们最新评论的帖子的要求。谢谢你的精彩解释!旁注-最外层的查询实际上并不是多余的,由于我从Post表中选择的不仅仅是Id,这是我的错,因此我更新了Post以帮助澄清查询的全部意图。@Kevin即使没有子查询,您仍然可以让查询返回不仅仅是Post表中的Id。@Kragen-这需要正确的联接吗?以及在GROUPBY子句中包含所有额外的列?我有一个工作示例,两个工作示例的查询计划有细微的不同;我不知道其中一个表现是否比另一个好。实际的数据集大小太小了,在这种情况下根本不重要,所以我将使用我认为更可读的数据集。@Kevin不,您只需要将列添加到子查询中-我编辑了最后一个代码段,以便它获得两个额外的列作为示例。您应该发现查询计划与SQL server优化查询非常相似。例如,SELECT*FROM T的执行计划应与SELECT*FROM SELECT*FROM的计划相同T@Kragen-此查询的目的是使用Comments表中的ID从Post表中选择数据,这就是我所指的连接,例如获取[Post].[Text]。在更新的示例中,附加列也必须包含在GROUPBY子句中?
WHERE GroupedComments.maxTimeStamp >= '2/1/2010'
AND GroupedComments.maxTimeStamp < '3/01/2010'
SELECT [Id],[Text]
From [Post]
WHERE [Id] IN (
    SELECT Post_Id
    FROM Comment
    GROUP BY Post_Id
    HAVING MAX(Timestamp) >= '3/1/2010' AND MAX(Timestamp) < '4/1/2010'
)
AND [Post].[Visible] = 1
var subCriteria = DetachedCriteria.For<Comment>()
    .SetProjection(Projections.ProjectionList()
        .Add(Projections.GroupProperty("Post.Id")))
    .Add(Restrictions.Ge("Timestamp", new DateTime(2010, 3, 1)))
    .Add(Restrictions.Lt("Timestamp", new DateTime(2010, 4, 1)));

var criteria = session.CreateCriteria<Post>()
    .Add(Restrictions.Eq("Visible", true))
    .Add(Subqueries.PropertyIn("Id", subCriteria));