테이블 조인시 성능 저하 질문드려요 0 11 4,649

by 콩이 [DB 모델링/설계] 조인 테이블 성능 [2012.11.05 16:19:40]



안녕하세요. 조인시 테이블 성능 저하로 문의드립니다.

우선 그룹 테이블과 그룹상세 테이블 2개가 있습니다.

그룹(1) : 그룹상세(N) 구조가 되겠습니다.

그룹상세에는 현재 테스트로 120만건 정도 넣어놨습니다.
(예상은 저보다 많은 1200만건 정도 예상됩니다.)

그런데 페이지 로드시 꽤 오래 걸리네요..

갯수검색 쿼리
SELECT COUNT(B.NO) AS CNT FROM T_ADDR_GROUP A, T_ADDR_DETAIL B WHERE A.NO = B.PARENT_NO AND B.USER_ID = ?

페이징 리스팅 쿼리

SELECT * FROM (
SELECT /*+ INDEX_DESC(PK_T_ADDR_DETAIL) */ ROWNUM AS RNUM, X.* FROM (
SELECT A.NAME AS GROUP_NAME, B.* FROM T_ADDR_GROUP A, T_ADDR_DETAIL B WHERE A.NO = B.PARENT_NO AND B.USER_ID =? 
ORDER BY B.NO DESC
) X WHERE ROWNUM <= ?
) WHERE RNUM >= ?

쿼리는 위와 같습니다.

조인을 하는 이유는, 그룹명 검색을 위해서입니다. 그게 아니면 조인할 필요는 없는 상황입니다.

첫 페이지에서 저 페이징 쿼리를 타면 8 초정도 걸립니다.

*인덱스는 각 테이블에 기본키 NO 필드에 잡혀 있습니다. 그외는 없습니다.

어디가 문제일까요?
by 마농 [2012.11.05 16:57:13]
1. User_ID 로 검색하니 해당 항목에 인덱스가 있어야 합니다.
 - No Descending 을 위해서 결합인덱스(User_ID, No)를 이용하세요.
2. 페이징이나 카운트시에는 그룹명칭은 필요 없습니다.
 - 카운트에서는 아예 조인을 빼시고 디테일만 이용
 - 페이징시에는 디테일만 이용하여 페이징을 완료한 상태에서 조인하세요.

-- 갯수검색 쿼리
SELECT COUNT(*) AS cnt
  FROM t_addr_detail b
 WHERE b.user_id = ?
;

CREATE INDEX idx01_t_addr_detail ON t_addr_detail(user_id, no);

-- 페이징 리스팅 쿼리
SELECT a.name AS group_name
     , b.*
  FROM t_addr_group a
     , (SELECT /*+ INDEX_DESC(b idx01_t_addr_detail) */
               ROWNUM AS rnum
             , b.*
          FROM t_addr_detail b
         WHERE b.user_id = ?
           AND ROWNUM <= ?
        ) b
 WHERE a.no = b.parent_no
   AND rnum >= ?
 ORDER BY rnum
;

by 콩이 [2012.11.05 17:05:30]
답변 해주셔서 정말 감사해요^^

그런데 group_name을 검색해야 하는 경우에요..

그럼 카운트 시에도 group_name이 검색되어야 하구요

페이징 쿼리에서도 검색되어야 하는데 위처럼 하면 가능한가요?

카운트시 조인해야 되지않을까요?

by 마농 [2012.11.05 17:11:06]

처음 부터 가능한 요구사항을 모두 적어 주셔야 합니다.
요구사항 하나 추가 하고 안하고에 따라 최적의 쿼리는 변하기 마련입니다.
또한 조건이 동적으로 변한다면 참 곤란하지요.
1조건에 최적인게 2조건에 최악일 수 있습니다.
여러 조건을 모두 만족하는 최적의 쿼리 찾기는 힘들고 절충안을 찾아야 하겠지요.


by 콩이 [2012.11.05 17:18:31]
예 제가 자세히 못썼네요..

테이블 구조는 

'그룹' 테이블
번호 | 그룹명 | 등록자ID | 날짜
'그룹상세' 테이블
번호 | 부모번호 | 성명 | 핸드폰 | 이메일 | 팩스 | 등록자ID | 날짜

위처럼 이루어져 있습니다.

검색의 조건은
'그룹명' '성명' '핸드폰' '이메일' '팩스' '등록자ID' 입니다.

가장 문제가 되는 부분은 그룹 테이블의 '그룹명'을 검색하여 페이징하는 쿼리입니다.

물론 그룹상세 테이블의 모든 필드도 검색이 가능해야 하구요..

조언을 구해봅니다. 어찌해야 될까요? ^^

by 마농 [2012.11.05 17:31:57]
조건이 유동적인 경우인데요.
필수 조건과 선택 조건을 구분해서 알려주셔야 합니다.

by 콩이 [2012.11.05 17:50:05]
'그룹' 테이블
번호 | 그룹명 | 등록자ID | 날짜
'그룹상세' 테이블
번호 | 부모번호 | 성명 | 핸드폰 | 이메일 | 팩스 | 등록자ID | 날짜

우선 필수조건은
조인시 필요한 그룹테이블 NO(번호)와 그룹상세 테이블 PARENT_NO(부모번호)
그리고 등록자ID 즉 USER_ID 입니다.

그리고 유동적 조건은 그외 '그룹명', '성명', '핸드폰', '이메일', '팩스' 번호입니다.

정렬은 날짜 또는 번호로만 가능하면 될 것 같습니다.

참고로 '성명' 테이블은 초성 검색을 지원합니다. NCHR(유니코드)..

by 마농 [2012.11.05 18:07:13]
필수조건이 User_ID 이고
a의 그룹명 검색결과 no 를 user_id 조건과 함께 b에 제공하면
b에서는 (user_id, parent_no) 로 된 결합인덱스 필요합니다.
(user_id, parent_no, no) 로 된 결합인덱스도 고려해 보시구요.
가능하다면 (user_id, parent_no, no, 기타컬럼들) 하면 좋겠지만...
인덱스가 너무 커지는 것도 바람직하진 않습니다.
그룹명칭 검색을 위한 인덱스 생성 a(group_name, no) 도 고려해 보세요.
상황에 따라서는 그룹명 조건이 들어올때와 안들어올때
동적으로 쿼리를 두개로 분리하는 방법도 생각해 보세요.
SELECT *
  FROM (SELECT ROWNUM rnum
             , c.*
          FROM (SELECT a.group_name
                     , b.*
                  FROM t_addr_group a
                     , t_addr_detail b
                 WHERE b.user_id = :v_user_id
                   AND a.group_name LIKE :v_group_name||'%'
                   AND a.no = b.parent_no
                   AND (b.기타 조건들)
                 ORDER BY no DESC
                ) c
         WHERE ROWNUM <= :v_page * 10
        )
 WHERE rnum >= :v_page * 10 - 9
   AND ROWNUM <= 10
;

by 콩이 [2012.11.05 18:17:20]
예 감사합니다. 마농님.. 분리 하는 방법도 생각해봐야겠습니다. 정말 감사합니다.^^

by 마농 [2012.11.05 18:18:03]

여러가지 테스트 해보시고 실행계획도 꼭 확인해 보세요.
원하시는 실행계획이 안나오면 힌트로 조절하셔야 합니다.


by 콩이 [2012.11.05 18:41:44]
마농님 조언대로 실행하니 2초 정도로 매우 단축이 되었습니다.

그래서 힌트를 쓰라고 하셨는지 모르겠지만..


위에서 ORDER BY no DESC 부분을 a.no DESC로 하면 2초가 나오는데요 b.no DESC로 하면 13~14초가 나옵니다.. 만약 b.no으로 정렬하고자 할 경우에 힌트를 써야 할까요?


by 아발란체 [2012.11.06 10:12:33]

캐쉬 내용으로 속도 차이가 날 수 있기 때문에 풀스캔 해보세용~ ㅋㅅㅋ)/
풀스캔 했는데 속도 차이가 있으면 힌트 주시는게 좋을 것 같아용.

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