안녕하세요!
이번에 게시판으로 프로젝트를 만들어보고 있는데 막히는 쿼리가 있어서 질문드립니다.
먼저 게시판은 아래와 같이 통합테이블(카테고리로 구분)을 하나 만들었구요. 아래와 같습니다.
bno | category | title | content | hits | writer | created_dt | updated_dt |
1 | 공지사항 | 공지A | 공지내용A | 25 | 관리자 | 2019-01-05 | 2019-01-05 |
2 | 공지사항 | 공지B | 공지내용B | 9 | 관리자 | 2019-01-04 | 2019-01-04 |
3 | 자료실 | 자료A | 자료내용A | 10 | 관리자 | 2019-01-03 | 2019-01-03 |
4 | 문의 | 문의A | 문의내용A | 2 | 관리자 | 2019-01-03 | 2019-01-03 |
5 | 문의 | 문의B | 문의내용B | 4 | 관리자 | 2019-01-03 | 2019-01-03 |
6 | 자료실 | 자료B | 자료내용B | 30 | 관리자 | 2019-01-02 | 2019-01-02 |
7 | 공지사항 | 공지C | 공지내용C | 17 | 관리자 | 2019-01-02 | 2019-01-02 |
8 | 자료실 | 자료C | 자료내용C | 6 | 관리자 | 2019-01-01 | 2019-01-01 |
9 | 문의 | 문의C | 문의내용C | 3 | 관리자 | 2019-01-01 | 2019-01-01 |
이때 카테고리별로 상위(최근날짜순) n개의 데이터를 조회할려고 합니다.
만약, 카테고리별로 상위 2개의 데이터만 조회한다고 하면 아래와 같이 조회하고 싶습니다.
bno | category | title | content | hits | writer | created_dt | updated_dt |
1 | 공지사항 | 공지A | 공지내용A | 25 | 관리자 | 2019-01-05 | 2019-01-05 |
2 | 공지사항 | 공지B | 공지내용B | 9 | 관리자 | 2019-01-04 | 2019-01-04 |
3 | 자료실 | 자료A | 자료내용A | 10 | 관리자 | 2019-01-03 | 2019-01-03 |
6 | 자료실 | 자료B | 자료내용B | 30 | 관리자 | 2019-01-02 | 2019-01-02 |
4 | 문의 | 문의A | 문의내용A | 2 | 관리자 | 2019-01-03 | 2019-01-03 |
5 | 문의 | 문의B | 문의내용B | 4 | 관리자 | 2019-01-03 | 2019-01-03 |
구글링을 해보니까 mysql말고 다른 db는 partition? 뭐 그런걸 이용해서 할 수 있는데 mysql은 지원이 안되서
사용자정의변수를 사용하더라구요.. @rank := 이런 변수들이 들어가던데 사용자정의 변수를 사용하지 않고
카테고리별로 상위 n개의 게시물을 가져오고 싶습니다.
조언부탁드립니다.
정렬 기준으로 created_dt 를 사용해야 하는데 중복이 가능하므로 유니크한 bno 를 정렬기준으로 추가해야 합니다.
예시 자료의 bno 순서가 거꾸로 된것이 아닌가 생각되네요?
최신 등록 글의 bno 가 더 크다고 가정하고 작성했습니다.
WITH t AS ( SELECT 9 bno, '공지사항' category, '공지A' title, '공지내용A' content, 25 hits, '관리자' writer, '2019-01-05' created_dt, '2019-01-05' updated_dt UNION ALL SELECT 8, '공지사항', '공지B', '공지내용B', 9, '관리자', '2019-01-04', '2019-01-04' UNION ALL SELECT 7, '자료실' , '자료A', '자료내용A', 10, '관리자', '2019-01-03', '2019-01-03' UNION ALL SELECT 6, '문의' , '문의A', '문의내용A', 2, '관리자', '2019-01-03', '2019-01-03' UNION ALL SELECT 5, '문의' , '문의B', '문의내용B', 4, '관리자', '2019-01-03', '2019-01-03' UNION ALL SELECT 4, '자료실' , '자료B', '자료내용B', 30, '관리자', '2019-01-02', '2019-01-02' UNION ALL SELECT 3, '공지사항', '공지C', '공지내용C', 17, '관리자', '2019-01-02', '2019-01-02' UNION ALL SELECT 2, '자료실' , '자료C', '자료내용C', 6, '관리자', '2019-01-01', '2019-01-01' UNION ALL SELECT 1, '문의' , '문의C', '문의내용C', 3, '관리자', '2019-01-01', '2019-01-01' ) -- 1. mySQL 도 8.0 부터는 분석함수 지원됩니다. SELECT * FROM (SELECT a.* , ROW_NUMBER() OVER(PARTITION BY category ORDER BY created_dt DESC, bno DESC) rn FROM t a ) a WHERE rn <= 2 ; -- 2. @변수 사용 SELECT * FROM (SELECT a.* , @rn := CASE WHEN category != @category THEN 1 ELSE @rn + 1 END rn , @category := category x FROM t a , (SELECT @category := '', @rn := 0) b ORDER BY category, created_dt DESC, bno DESC ) a WHERE rn <= 2 ; -- 3. Self Join SELECT a.* , COUNT(b.bno) + 1 rn FROM t a LEFT OUTER JOIN t b ON a.category = b.category AND ( (a.created_dt < b.created_dt) OR (a.created_dt = b.created_dt AND a.bno < b.bno) ) GROUP BY a.bno HAVING COUNT(b.bno) < 2 ; -- 4. Exists SELECT * FROM t a WHERE NOT EXISTS (SELECT 1 FROM t b WHERE a.category = b.category AND ( (a.created_dt < b.created_dt) OR (a.created_dt = b.created_dt AND a.bno < b.bno) ) AND EXISTS (SELECT 1 FROM t c WHERE b.category = c.category AND ( (b.created_dt < c.created_dt) OR (b.created_dt = c.created_dt AND b.bno < c.bno) ) ) ) ; -- 5. 카테고리별 Limit, Union (SELECT * FROM t WHERE category = '공지사항' ORDER BY created_dt DESC, bno DESC LIMIT 2) UNION ALL (SELECT * FROM t WHERE category = '자료실' ORDER BY created_dt DESC, bno DESC LIMIT 2) UNION ALL (SELECT * FROM t WHERE category = '문의' ORDER BY created_dt DESC, bno DESC LIMIT 2) ;