2


0

SQLクエリの改善-maxおよびgroupbyで選択

問題

'' '' '

次の2つのテーブルがある場合、最新の投稿(つまり、 最後)指定された期間内に行われたコメント(例: 2010年2月)。

ポストID 2の最新のコメントは期間フィルターの範囲外であるため、クエリの結果はポストID 1のみを返します。

質問

'' '' '

以下の「SELECT」ステートメントを作成しました。これは正しいと思われ、スローされたすべてのテストケースを処理します。

ただし、SQLスキルを向上させ続けるために、このシナリオで使用する「より良い」方法、既存のステートメントの改善に関する提案、および/またはカバーされないエッジケースがあるかどうかをコミュニティに尋ねています。 。

これは実際のテーブルの緩やかな翻訳であり、質問を理解しやすくするために変更されていることに注意してください。 価値があるものとして、SQL Server 2005を使用しています。

テーブル

'' '' '

Post

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 Answer


4


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

「GROUP BY」の後に行われる「WHERE」として「HAVING」を考え、_grouper結果セットを操作します。

ただし、NHibernateについては知りません。


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を使用する必要はありません。投稿は既にタイムスタンプによって順序付けされているためです。 これは、最も高い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`構文を使用してここに投稿された他のソリューションのいずれかにさらに削減できるかを簡単に確認できるはずです。

  • BETWEENではなく<および> =を使用します*

ほんの少しだけ。 2月の最後の日付を見つけるために長い時間をかけるのではなく、BETWEENを<と> =に分割することで、クエリがよりクリーンになります。

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


0


これは、HAVING句を使用するよりも少し速いはずです。

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


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()
    .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()
    .Add(Restrictions.Eq("Visible", true))
    .Add(Subqueries.PropertyIn("Id", subCriteria));