오라클 쿼리작성중인데 너무 느린 쿼리가 있습니다.
그런데 좀 이해가 안되는 상황이 발생..
WITH eqipInfo AS (
SELECT a.equip_id,
a.wkor_id,
b.item_no,
a.comp_no,
b.sub_mng_id,
b.eqctg_id,
b.old_eq_no,
b.description ,
b.eqctg_type
FROM TAWOEQUIP a, TAEQUIPMENT b
WHERE a.comp_no = b.comp_no
AND a.equip_id = b.equip_id )
SELECT
(SELECT ''
FROM eqipInfo a
WHERE a.wkor_id = x.wkor_id
AND a.comp_no = x.comp_no ) AS equipDesc,
(SELECT TO_CHAR(WM_CONCAT(c.description))
FROM eqipInfo b, TAEQCTG c
WHERE b.comp_no = c.comp_no
AND b.equip_id = b.equip_id
AND b.eqctg_id = c.eqctg_id
AND b.wkor_id = x.wkor_id
AND b.comp_no = x.comp_no
GROUP BY b.comp_no, b.wkor_id ) AS eqCtgDesc
FROM TAWORKORDER x
WHERE 1=1
AND x.wkor_date = '20170223'
AND x.comp_no = '160'
AND x.wo_status = 'P'
위의 쿼리는 실행시 1초만에 완료..
WITH eqipInfo AS (
SELECT a.equip_id,
a.wkor_id,
b.item_no,
a.comp_no,
b.sub_mng_id,
b.eqctg_id,
b.old_eq_no,
b.description ,
b.eqctg_type
FROM TAWOEQUIP a, TAEQUIPMENT b
WHERE a.comp_no = b.comp_no
AND a.equip_id = b.equip_id )
SELECT
(SELECT TO_CHAR(WM_CONCAT(c.description))
FROM eqipInfo b, TAEQCTG c
WHERE b.comp_no = c.comp_no
AND b.equip_id = b.equip_id
AND b.eqctg_id = c.eqctg_id
AND b.wkor_id = x.wkor_id
AND b.comp_no = x.comp_no
GROUP BY b.comp_no, b.wkor_id ) AS eqCtgDesc
FROM TAWORKORDER x
WHERE 1=1
AND x.wkor_date = '20170223'
AND x.comp_no = '160'
AND x.wo_status = 'P'
아래의 쿼리는 17초 걸립니다.
차이점은 컬럼에
(SELECT ''
FROM eqipInfo a
WHERE a.wkor_id = x.wkor_id
AND a.comp_no = x.comp_no ) AS equipDesc,
이걸 조회 해주면 빠르고 안해주면 17초나 걸리는데요...
도대체 왜 이런 현상이 발생하는것이며.. 저 필드는 사실 조회할필요가 없는데 어떻게 제외하고 최적화 (2번쨰쿼리) 로 빠르게 실행시킬수 있을까요.
WITH문은 INLINE, MATERIALIZE 두 가지 형태로 운용될 수 있습니다. INLINE의 경우 단순히 반복되는 쿼리를 WITH문으로 빼내어 가독성과 관리성을 높여주는 목적이 크구요.
MATERIALIZE 의 경우에는 가상의 공간에 데이터를 미리 테이블처럼 만들어놓아 여러번 쿼리가 반복되었을 때 결과를 재사용하기 위한 목적이 큽니다.
WITH문 SELECT 절에 /*+INLILNE*/ 이나 /*+MATERIALIZE */ 힌트를 통해 지정이 가능하구요. 지정을 안 하게 되면 옵티마이저 판단에 따라 선택하게 됩니다.
저도 아직 공부 중이라 확실하지 않지만 위의 경우에 INLINE의 형태로 풀린 것으로 보이구요. 굵은 글씨 아래 서브쿼리에 EQIPINFO와 TAEQCTG를 조인하고 GROUP BY 하게 되는데
이것이 INLINE으로 풀리면서 효율적인 조인이 일어나지 못한 것으로 보입니다. 제 추측으로는 X의 COMP_NO, WKOR_ID를 받기 전에 GROUP BY 로 결과부터 만들고
나중에 COMP_NO로 WKOR_ID로 필터링하는 게 아닌가 하는 생각이 드네요. 굵은 글씨 서브쿼리에서 COMP_NO, WKOR_ID를 먼저 받아서 결과셋을 만들어 놓게 함으로써
뒷쪽 조인이 풀릴 때 해당 결과를 반영해서 실행계획이 바뀌는 것이 아닌가.. 하는 생각이 듭니다. 물론 저도 공부중이기에 추측일 뿐 정확하지는 않습니다.
만약 WITH문의 결과셋이 크지 않다면(결과 건수가 많다면 오히려 비효율적이겠죠.)
WITH eqipInfo AS (
SELECT /*+MATERIALIZE */ a.equip_id,
~~
의 형태로 변경해보셔도 비슷한 성능이 나올 것 같네요.
네 INLINE으로 풀린 것 같구요. 굵은 글씨 아래 서브쿼리에 EQIPINFO, TAEQCTG 두개를 조인하게 되는데
해당 서브쿼리가 INLINE 성격으로 풀리면 EQIPINFO자리에 WITH문 쿼리가 들어가게 되는데 그렇게 되면
EQIPINFO와 TAEQCTG 가 조인되게 되는 것이 아니라 TAWOEQUIP, TAEQUIPMENT, TAEQCTG 세 가지 테이블이
조인을 하게 됩니다. 옵티마이저가 이때 조인을 좀 비효율적으로 한 게 아닌가 싶구요.
제가 말씀 드린 MATERIALIZE 힌트는 결과셋이 작고 재사용이 많이 될때 사용하면 좋고
굳이 INLINE의 효율이 높을 때는 사용할 필요가 없습니다. 기존 쿼리와 MATERIALIZE 힌트를 사용한
쿼리의 실행계획을 비교하여 더 좋은 쪽으로 적용하시는 것이 좋겠네요.
혹시 MATERIALIZE 힌트 자리에 NO_MERGE 힌트 주고 테스트 한번 부탁드립니다.
저도 안 해봐서 될지는 모르겠네요.