게임 랭킹을 얻어오는 쿼리 질문 입니다. 0 6 6,021

by 플란더스의개매너 [SQL Query] query 랭킹 [2013.04.10 20:54:54]


안녕하세요,

게임 결과 랭킹을 구하려고 하는데요,

컬럼은
id(pk),
user_id,
score,
gamedate

이런 식으로 구성이 되어있습니다.

게임을 할 때 마다
유저 정보와 스코어 플레이 시간이 DB에 전송이 됩니다.

기본키인 id만 중복이 되지 않구요,
유저의 아이디나, 게임 스코어의 경우는 컬럼 수만큼 중복이 됩니다.


랭킹은 10위 정도만 보여줄 예정입니다.
문제는 랭킹을 보여줄 때인데요..

테이블의 값을 예를 들자면...

id(pk) | user_id | score | gamedate
=======================================
1    id_001     100 2013-04-10
2    id_002     90  2013-04-10
3    id_003     90   2013-04-11
4    id_001     100 2013-04-11
5    id_004     89       2013-04-12
=======================================


이 경우 1위는 id_001인 유저 입니다...
하지만 같은 점수가 두개라서 하나만 보여주려고 합니다.
2위는 먼저 90점을 낸 id_002인 유저이구요...
3위는 점수는 같지만 늦게 플레이한 id_003인 유저입니다.
4위는 가장 낮은 점수인 id_004유저 이구요...


랭킹 선정 기준은
1. 스코어가 높은 순서 입니다.
2. 동일한 점수시에는 먼저 플레이한 유저가 더 상위 입니다.
3. 같은 유저가 동일한 점수를 냈을 때는 가장 일찍 플레이한 레코드만을 가져옵니다...

예들들어 한 유저가 동일한 점수를 여러번 냈을 경우,
위와 같이 id_001 유저가 100점을 두번 낸 경우입니다...

select 시 결과 값으로 4번 행을 제외하고
id(pk) 값이 1,2,3,5 컬럼만을 가져오고 싶습니다...


제가 쿼리 쪽을 많이 몰라서 구글링해서
서브쿼리, 그룹, 조인, 뭐 이것 저것 찾아봤는데요
도무지 감이 안잡히네요.....ㅠㅠ


글이 너무 길고 복잡한 것 같은데요...
읽어주셔서 감사드립니다...


조언 해주시면 정말 감사하겠습니다..

by 우리집아찌 [2013.04.11 08:50:09]

틀렸네요 ㅡㅡ;
WITH A (id, usr , score , dt ) AS(
SELECT 1  ,  'id_001'  ,   100 ,'2013-04-10'  FROM DUAL UNION ALL
SELECT 2  ,  'id_002'  ,   90  ,'2013-04-10'  FROM DUAL UNION ALL
SELECT 3  ,  'id_003'  ,   90  ,'2013-04-11'  FROM DUAL UNION ALL
SELECT 4  ,  'id_001'  ,   100 ,'2013-04-11'  FROM DUAL UNION ALL
SELECT 5  ,  'id_004'  ,   89  ,'2013-04-12'  FROM DUAL UNION ALL
SELECT 6  ,  'id_001'  ,   90 ,'2013-04-11'  FROM DUAL
)

SELECT rownum , usr , score , dt FROM
( SELECT  usr , SCORE , dt , rank() over(partition by usr order by score desc , dt desc ) rnk FROM A  )
WHERE RNK = 1
ORDER BY score desc , DT desc


by 마농 [2013.04.11 08:53:44]
-- User 별로 Group By 한 뒤 순위 부여
SELECT *
 FROM (SELECT user_id
       , MAX(score) score
       , MIN(id) KEEP(DENSE_RANK FIRST ORDER BY score DESC, gamedate) id
       , MIN(gamedate) KEEP(DENSE_RANK FIRST ORDER BY score DESC) gamedate
       , RANK() OVER(ORDER BY MAX(score) DESC
        , MIN(gamedate) KEEP(DENSE_RANK FIRST ORDER BY score DESC)
        , id -- 3차 조건 추가(2차 조건까지도 동일할 경우 동일 순위 발생)
        ) rk
     FROM t
     GROUP BY user_id
    )
 WHERE rk <= 10
;

by 디케이 [2013.04.11 09:09:30]
 
--부끄럽지만 일단 올려봅니다.
--우와 마농님은 SELECT 두개로 끝냈네요.. 전 줄여볼려고 했는데 아무리 짱구 굴려도 안되던데요.. ㅎㅎ
SELECT *
 FROM (
    SELECT ID, USER_ID, SCORE, GAMEDATE
     FROM (SELECT t.*
           , row_number() OVER (PARTITION BY user_id ORDER BY score DESC, gamedate) rn
         FROM t
        )
     WHERE rn = 1
      ORDER BY row_number() OVER (ORDER BY score DESC, gamedate, user_id)
    )
 WHERE ROWNUM <= 10 

by 손님 [2013.04.11 09:38:01]

우리집아찌님, 마농님
답글 달아주셔서 너무나 감사합니다.

덕분에 문제를 잘 해결하였습니다.
복받으실 거에요...


랭킹 테이블 구성을 오라클로 하게될줄 알았는데
ms-sql 로 변경되었네요;;
조금만 수정하니 ms-sql에서도 잘 돌아갑니다 ^^
좋은 쿼리 알려주셔서 정말 감사합니다.


혹시나 필요하신분이 계실 까 맨 밑에
ms-sql에 맞게 수정한 쿼리 남기고 갑니다.
(ms-sql 2005버전에서 사용했습니다.)



좋은 하루 되세요 ^^


============================================================

/* MS-SQL 2005 */
/*
* table name : game_score_table
* 1차로 유저별 자체 랭킹을 얻어온다 -> rank_by_user
* (score가 높고, gamedate 가 빠른 기준 정렬)
* 2차로 유저별 자체 랭킹에서 하나씩만 얻어 온 후
* score가 높은 순으로 정렬
*
* TOP (10) 으로 최상위 10개만 가져온다.
*/

SELECT TOP (10) * FROM
 (SELECT RANK() OVER(PARTITION BY user_id ORDER BY score DESC, gamedate ASC) AS 'self_ranking', * AS game_score_table) rank_by_user
WHERE
 rank_by_user.self_ranking = 1 ORDER BY score DESC;

============================================================


마지막 줄에 rank_by_user.self_ranking의 값 1은
유저의 가장 높은 점수 1개만을 보여주기 위함 입니다.
값을 2로 바꾸면 해당유저의 가장 높은점수, 그다음 .. 총 두개이구요...
상황에 맞게 값을 주시면 되겠습니다.


by 플란더스의개매너 [2013.04.11 09:38:46]

우리집아찌님, 마농님
답글 달아주셔서 너무나 감사합니다.

덕분에 문제를 잘 해결하였습니다.
복받으실 거에요...


랭킹 테이블 구성을 오라클로 하게될줄 알았는데
ms-sql 로 변경되었네요;;
조금만 수정하니 ms-sql에서도 잘 돌아갑니다 ^^
좋은 쿼리 알려주셔서 정말 감사합니다.


혹시나 필요하신분이 계실 까 맨 밑에
ms-sql에 맞게 수정한 쿼리 남기고 갑니다.
(ms-sql 2005버전에서 사용했습니다.)



좋은 하루 되세요 ^^


============================================================

/* MS-SQL 2005 */
/*
* table name : game_score_table
* 1차로 유저별 자체 랭킹을 얻어온다 -> rank_by_user
* (score가 높고, gamedate 가 빠른 기준 정렬)
* 2차로 유저별 자체 랭킹에서 하나씩만 얻어 온 후
* score가 높은 순으로 정렬
*
* TOP (10) 으로 최상위 10개만 가져온다.
*/

SELECT TOP (10) * FROM
 (SELECT RANK() OVER(PARTITION BY user_id ORDER BY score DESC, gamedate ASC) AS 'self_ranking', * AS game_score_table) rank_by_user
WHERE
 rank_by_user.self_ranking = 1 ORDER BY score DESC;

============================================================


마지막 줄에 rank_by_user.self_ranking의 값 1은
유저의 가장 높은 점수 1개만을 보여주기 위함 입니다.
값을 2로 바꾸면 해당유저의 가장 높은점수, 그다음 .. 총 두개이구요...
상황에 맞게 값을 주시면 되겠습니다.


by 플란더스의개매너 [2013.04.11 09:39:30]

손님으로 달고 또 다네요 ^^;
디케이님 쿼리도 한번 돌려보겠습니다.
답변 주셔서 감사합니다

점심 맛있게 드셔요 ㅎㅎ
댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입