web-dev-qa-db-ja.com

オラクルから各グループの最新の行を選択

ゲストブックにユーザーのコメントが記載されたテーブルがあります。列は、id、user_id、title、comment、timestampです。

ユーザーごとに最新の行を選択する必要があります。私はこれをgroup byで実行しようとしましたが、同じクエリでuser_idでグループ化する他のものを選択できないため、管理していません。

SELECT user_id, MAX(ts) FROM comments GROUP BY user_id

たとえば、このクエリでは、列id、tilte、およびコメントを選択することもできません。これはどのように行うことができますか?

12
user1985273

分析関数を使用できます

SELECT *
  FROM (SELECT c.*,
               rank() over (partition by user_id order by ts desc) rnk
          FROM comments c)
 WHERE rnk = 1

タイの処理方法に応じて(同じuser_idtsの2つの行が存在する場合)、row_numberまたはdense_rank関数を使用できます。 rankではなく。 rankを使用すると、同数の場合に複数の行を最初にできます。 row_numberは、同数の場合、任意に1行を返します。 dense_rankは、最初に関連付けられた行に対してはrankのように動作しますが、2つの行が最初に関連付けられていると仮定して、次の行を3番目ではなく2番目と見なします。

7
Justin Cave

JOINを使用してクエリを構築できます。

_select c.*
from comments c join
     (select user_id, max(ts) as maxts
      from comments c2
      group by user_id
     ) cc
     on c.user_id = cc.user_id and c.ts = cc.maxts;
_

他の方法があります。典型的なアドバイスはrow_number()を使用することです:

_select t.*
from (select c.*, row_number() over (partition by user_id order by ts desc) as seqnum
      from comments c
     ) c
where seqnum = 1;
_

これら2つのクエリは微妙に異なります。最初のコメントは、ユーザーの最新のコメントがまったく同じtsの場合に重複を返します。 2番目は、ユーザーごとに1行を返します。

8
Gordon Linoff

このタイプの問題には、dense rank first/last関数を使用した非常にシンプルで非常に効率的な解決策があります。

select id,
       max(user_id) keep (dense_rank last order by ts) over (partition by id) as user_id,
       max(title)   keep (dense_rank last order by ts) over (partition by id) as title,
       max(comment) keep (dense_rank last order by ts) over (partition by id) as comment,
       max(ts)                                                                as ts
from   comments;
3
mathguy