SQL查询改进 - 使用max和groupby选择

时间:2010-03-03 16:16:14

标签: sql-server nhibernate select group-by max

问题


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

查询结果应仅返回帖子ID 1,因为帖子ID 2的最新评论超出了时间范围过滤器的范围。

问题


我在下面创建了SELECT语句似乎是正确的,并处理所有抛出的测试用例。

但是,为了继续提高我的SQL技能,我要求社区是否有一个“更好”的方法用于此场景,任何改进现有语句的建议,和/或边缘案例不包括在内。

请注意,这是实际表格的松散翻译,旨在使问题更易于理解。对于它的价值,我正在使用SQL Server 2005。


发布

Id    Text     Visible
1     Post 1   1
2     Post 2   1
3     Post 3   0
.     ...
n     Post n   1

注释

Id    Post_Id    Text                  CommentNumber    Timestamp
1     1          Comment 1, Post 1     1                2/3/2010
2     1          Comment 2, Post 1     2                2/4/2010
3     2          Comment 1, Post 2     1                3/1/2010
.     .          .
n     m          Comment n, Post m     x                xx/xx/xxxx


SQL命令


SELECT [Id],[Text]
FROM [Post]  
WHERE [Id] IN (  
    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  
)
AND [Post].[Visible] = 1


奖金问题


是否可以使用NHiberate创建此查询(使用Criteria API或HQL)?

4 个答案:

答案 0 :(得分:4)

SELECT
    Post_Id
FROM
    Comment
GROUP BY
    Post_Id
HAVING
    MAX(Timestamp) >= '2/1/2010'

HAVING视为WHERE,发生在 GROUP BY之后的,对分组结果集进行操作。

虽然不知道NHibernate。

答案 1 :(得分:3)

已经发布了好的解决方案,但我想我会逐步解释你的查询是如何简化的:

最外面的子查询是多余的

子查询的最外层部分(SELECT [Id] FROM [Post] WHERE [Id] IN (位)是多余的,因为您已经返回了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来获取最新评论。这意味着我们可以选择最高的TimeStamp,而不是选择具有最高Id的评论的TimeStamp。

这样就无需再次加入评论,只留下:

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'

子查询现在是多余的

现在查询已经有所简化,应该很容易看到如何使用distincthaving语法将其进一步缩小到此处发布的其他解决方案之一。

使用<和> =而不是BETWEEN

只是一个小小的傻笑。而不是花费很长的时间来找到2月的最后日期,将BETWEEN分成<和> =使查询更清晰:

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

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

这是我在结合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));