鉴于以下两个表,我想选择所有在给定时间跨度内(例如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
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)?
答案 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'
子查询现在是多余的
现在查询已经有所简化,应该很容易看到如何使用distinct
或having
语法将其进一步缩小到此处发布的其他解决方案之一。
使用<和> =而不是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));