본 글은 oracle 12g 및 실무 영역 기준으로 작성되었음을 알려드립니다.JOIN IS IIKE PUZZLE
※ 개발을 진행하면서 처음으로 대학교나 학원 및 자격증 공부블 하면서 배웠던 내용을 써먹겠구나! 했는데 사실 가장 헷갈렸던 부분이였다. 필자는 mysql 및 postgresql의 환경만 접했었고, 대규모 프로젝트 경험 또한 없었기 때문에 식은땀이 났던 경험이 생각났다.
1. INNER JOIN : 개발을 하다보면 실무에서는 INNER JOIN과 LEFT OUTER JOIN을 가장 많이 쓰는 것을 경험할 것이다. 그렇다면 INNER JOIN은 정확히 어떻게 사용할까?
SELECT A.COL1
FROM TABLE_1 A,
TABLE_2 B
WHERE A.PK = B.PK;
위 코드와 같이 테이블 2개를 조인시킬때, 각 테이블의 PK를 WHERE 절에서 JOIN 시켜주면 된다. 다만, 위 코드처럼 깔끔하게 2개의 테이블로 조인시켜 구성하는 경우는 ODS에서 DIMENSION으로 확장시킬 경우이다. FACT 테이블을 구성하게 되면 최소 3개 이상의 테이블들이 JOIN되어 구성된다.
2. LFET OUTER JOIN
: 위에서 설명했듯이, FACT 테이블을 구성할 땐, 최소 3개 이상의 테이블로 구성하는 경우가 대다수이다. AS-IS에서 1:1로 FACT를 적재하면 가장 좋겠지만, 그렇게 쉬이 진행되는 경우는 크지 않았었다. 아래 코드를 보자.
SELECT A.YMD,
B.STORE_NM,
A.SLEM,
C.RT
FROM FACT_1 A,
DIMENSION_1 B,
FACT_2 C
WHERE A.STORE_CD = B.STORE_CD
AND A.YMD = C.YMD(+)
AND A.STORE_CD = C.STORE_CD(+)
위 코드는 FACT 테이블 2개와 DIMENSION 테이블 1개를 조인시키는 경우이다. A테이블과 B테이블의 PK인 코드값과 A의 테이블과 C테이블의 일자와 코드값을 LEFT OUTER JOIN 하여 추출하는 경우이다.
보통 조인을 하기 전, 메인 테이블을 지정하고 해당 테이블에 각 테이블들을 조인시키는 게 가독성 면에서도 가장 좋고 개발자 본인에게도 헷갈리지 않고 가장 깔끔한 경우이다. 메인 테이블을 지정하지 않고 중구남방으로 조인시키면 해당 값에 대한 검증이 어려울 수 있다.
다만, 해당 코드에서 LEFT OUTER JOIN을 (+)로 표기하였는데, 필자도 초기 LEFT OUTER JOIN은 해당 방식으로 처리하였다. 하지만 개발 후반부 값검증을 진행할 때, 이상하게 값이 안맞는 경우가 있어 ANSI 표준 방식으로 수정해야 했었다. ANSI 표준 방식이 아닌 (+)로 코드를 구성하였을 때, 일부 값들이 누락되는 경우가 있는데 해당 부분은 아직도 원인불명이다. 그렇다면 ANSI 표준 방식은 어떻게 하면 되는지 확인해보자.
SELECT A.YMD,
B.STORE_NM,
A.SLEM,
C.RT
FROM FACT_1 A
INNER JOIN DIMENSION_2 B
ON A.STORE_CD = B.STORE_CD
LEFT OUTER JOIN FACT_2 C
ON A.YMD = C.YMD
AND A.STORE_CD = C.STORE_CD;
위 코드처럼 ON절로 표현되는 것이 ANSI 표준 방식이다. 필자도 개발 후반부에는 LEFT OUTER JOIN이 (+)로 처리 된 코드들을 모두 변경 및 수정하였었다. 여러분들도 oracle로 개발을 진행중이라면 ANSI 표준 방식으로 구성하기를 적극 추천드린다.
SELECT A.DATE,
A.ST_CD,
MAX(B.S1_CD) G_CD,
MAX(B.S2_CD) C_CD,
MAX(B.S3_CD) A_CD,
SUM(A.SLEM) SLEM,
SUM(A.SLEM - A.VAT) S_N_V,
MAX(C.CTS_N) CTS_N,
SUM(A.D_A_V) D_A_V,
NVL(MAX(D.S_A), 0) S_A,
NVL(MAX(E.C_N_A), 0) C_N_A,
MAX(F.R_S_Q) R_S_Q,
MAX(F.R_S_A) R_S_A,
MAX(G.BROQ) BROQ,
MAX(G.BROA) BROA,
MAX(F.BSQ) BSQ,
MAX(F.BSA) BSA,
...
FROM MRT.TABLE_DI A
LEFT JOIN COD.TABLE_ST B
ON A.ST_CD = B.ST_CD
LEFT JOIN MRT.TABLE_STRS C
ON A.DATE = C.DATE
AND A.ST_CD = C.STR_CD
LEFT JOIN (SELECT CR_DATE,
ST_CD,
SUM(CSA) S_A
FROM FCT.TABLE_CS
WHERE CR_DATE = /*PARAMETER*/
GROUP BY CR_DATE, ST_CD) D
ON A.DATE = D.CR_DATE
AND A.ST_CD = D.ST_CD
LEFT JOIN (SELECT DATE,
ST_CD,
SUM(SA) C_N_A
FROM FCT.TABLE_MAIN
WHERE DATE = /*PARAMETER*/
AND TRDC IN ('2', '3')
GROUP BY DATE, ST_CD) E
ON A.DATE = E.DATE
AND A.ST_CD = E.ST_CD
LEFT JOIN (SELECT BDATE,
ST_CD,
SUM( CASE WHEN LOC = 'A' THEN Q ELSE 0 END ) AS R_S_Q,
SUM( CASE WHEN LOC = 'C' THEN AV ELSE 0 END ) AS R_S_A,
SUM( CASE WHEN LOC = 'A' THEN Q ELSE 0 END ) AS BSQ,
SUM( CASE WHEN LOC = 'C' THEN AV ELSE 0 END ) AS BSA
FROM MRT.TABLE_LOC
WHERE BDATE = /*PARAMETER*/
AND LOC IN ('A', 'C')
GROUP BY BDATE, ST_CD) F
ON A.DATE = F.BDATE
AND A.ST_CD = F.ST_CD
(...이하 생략)
위 코드는 필자가 실제 개발했던 쿼리의 샘플이다. A라는 메인 테이블을 기준으로 FACT와 DIMENSION들을 PK에 해당하는 컬럼끼리 조인 처리하여 구성하는 것을 확인할 수 있을 것이다.
필자가 경험한 사이트들을 토대로 보면 INNER, LEFT OUTER JOIN 이외에 RIGHT OUTER JOIN이나 FULL OUTER JOIN이 쓰이는 경우는 거의 없었다. 다만, FULL OUTER JOIN은 카티션 곱으로 치환하여 사용하기 때문에 본 내용에 관련해서는 조금 더 뒤에서 다루도록 하겠다.