랭킹 구하기 ... 자신이 순위에서 아래 위로 10명씩 구하기 0 5 2,173

by pranludi [SQL Query] [2013.03.18 22:01:20]


안녕하세요 -

회원이 대략 2~300백만명 정도 됩니다.
회원 마다, 점수가 있고, 점수별로 순위가 주어 집니다.
순위는 매번 갱신이 되어 별도의 필드에 update되지 않습니다.

테이블 구조는 대략 이렇습니다.
create table user_info (
user_no number pk,
user_id varchar2(32) not null,
user_score number not null,
.....
);

회원 a에 대한 순위를 구하고, 아래/위로 10명씩 between 하면 될것 같지만 ...
이건 무리가 있을것 같고 ...

좋은 방법이 없을까요?
by 비니부장 [2013.03.19 08:41:35]
user_score 로 인덱스를 생성해 주시고, 회원 a에 대한 스코어를 구하신 후에...
다음과 같이 하면 실행계획 추적해 보시면 index range scan으로 처리됩니다.
더 좋은 방법이 있을지 모르니 참고만 하세요.

 select /*+ index(user_info 인덱스명) */ 
  *
from user_info 
where user_score <= 회원a의 스코어 
and user_no <> 'a' 
and rownum <= 10

union all

select /*+ index(user_info 인덱스명) */ 
  *
from user_info 
where user_score >= 회원a의 스코어 
and user_no <> 'a' 
and rownum <= 10 

by 마농 [2013.03.19 08:56:30]
-- 순위까지 나타내야 한다면? 전체범위 처리를 할수밖에 없습니다.
-- 점수가 동일하여 순위가 동일 할 수 있으므로
-- rownum_number 정렬항목에 유니크한 user_no 를 포함시켰습니다.
WITH tmp AS
(
SELECT user_no, user_id, user_score
     , RANK() OVER(ORDER BY user_score DESC) rk
     , ROW_NUMBER() OVER(ORDER BY user_score DESC, user_no) rn
  FROM user_info
)

SELECT a.*
  FROM tmp a
     , (SELECT * FROM tmp WHERE user_no = 'a') b
 WHERE a.rn BETWEEN b.rn - 10 AND b.rn + 10
 ORDER BY rn
;

by 마농 [2013.03.19 09:59:23]
-- 부분범위 처리를 위해서는 비니부장님 방법으로 하시면 되는데...
-- 점수만으로는 유니크 하지 않기 때문에 이부분에 대한 추가 처리가 필요합니다.
-- 유니크하게 비교하기 위해 ROWID 를 이용하겠습니다.
-- 점수에 대한 인덱스명을 idx_score 라고 가정하겠습니다.
WITH tmp AS
(
-- 유저 a 의 정보를 pk 인덱스로 조회합니다.
SELECT user_no, user_score, ROWID rid
 FROM user_info
 WHERE user_no = 'a'
)
-- 인덱스 역순으로 a 를 포함하여 11건 가져오기.
SELECT /*+ ORDERED USE_NL(b) INDEX_DESC(b idx_score) */
       b.*
  FROM tmp a
     , user_info b
 WHERE b.user_score <= a.user_score
   AND b.ROWID <= DECODE(a.user_score, b.user_score, a.rid, b.ROWID)
   AND ROWNUM <= 11
 UNION ALL
-- 인덱스 순으로 a 를 제외하여 10건 가져오기.
SELECT /*+ ORDERED USE_NL(b) INDEX_ASC(b idx_score) */
       b.*
  FROM tmp a
     , user_info b
 WHERE b.user_score >= a.user_score
   AND b.ROWID >= DECODE(a.user_score, b.user_score, a.rid, b.ROWID)
   AND b.user_no != a.user_no
   AND ROWNUM <= 10
;

by 채용근 [2013.03.19 13:17:43]
SELECT
 FROM DUAL

by pranludi [2013.03.19 18:15:42]
답변 고맙습니다 ㅜㅜ
댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입