메뉴 건너뛰기

tnt_db

Oracle 1:1 JOIN / 1:M JOIN

운영자 2002.09.18 13:07 조회 수 : 3000 추천:19

조건.
    T01 :                               T02 :
    Name      Type                      Name      Type
    --------- ----                      --------- ----
    YMD       VARCHAR2(8)  -년월        YMD       VARCHAR2(8)  
    SITE_CD   VARCHAR2(10) -사업장      SITE_CD   VARCHAR2(10)
    SALE_TY   VARCHAR2(4)  -판매구분    PROC_AM   NUMBER      -구매금액
    SALE_AM   NUMBER       -판매금액

    위의 TABLE에 다음과 같은 DATA가 존재한다.

    T01 : KEY : YMD

    YMD      SITE_CD    SALE   SALE_AM
    -------- ---------- ---- ---------
    19990501 서울       직판      1000
    19990501 서울       대리      1300
    19990501 부산       직판       900
    19990501 부산       대리      1100
    19990501 인천       직판      1200
    19990501 인천       대리       900
    19990502 서울       직판      1200
    19990502 서울       대리      1400
    19990502 부산       직판      1000
    19990502 부산       대리      1200
    19990502 인천       직판      1300
    19990502 인천       대리      1000
    ...
    생략

    T02

    YMD      SITE_CD      PROC_AM
    -------- ---------- ---------
    19990501 서울            2000
    19990501 부산            2100
    19990501 인천            2100
    19990502 서울            2100
    19990502 부산            1900
    19990502 인천            1800
    19990503 서울            2400
    ...
    생략
    
    금액을 제외한 나머지 COLUMN은 PRIMARY KEY 이다.
    T02와 T01은 MASTER/DETAIL 관계이다.
    문제를 단순화하기 위하여 다음과 같은 가정을 한다.
    가정1:구매물량은 매일 100으로 동일하다.
    가정2:당일 구매된 물량은 전량 당일 판매된다.
    가정3:직판물량과 대리판매를 통한 물량은 항상 50:50이다.
    가정4:금액이 변하는 이유는 매일 구매/판매 단가가 다르기 때문이다.
    여러일자 중 1999년 5월1일과 5월2일 만을 생각해보자.

문제1. 1:1 JOIN
    년월 사업장별로 구매금액과 판매금액의 차이금액을 이용
    구매금액에 대한 비율, 즉 마진율을 백분율로 구해보자.
    결과가 다음과 같으면된다.
    
    YMD      SITE_CD      PERCENT
    -------- ---------- ---------
    19990501 부산              -5
    19990501 서울              15
    19990501 인천               0
    19990502 부산              16
    19990502 서울              24
    19990502 인천              28

생각1.
    1:1 조인이란 말그대로 JOIN으로 연결되는 KEY가 1:1 로 정확하게
    맞는다는 얘기다.
    하지만 현재 두 TABLE의 관계는 MASTER/DETAIL 관계로
    1:1이 될 수가 없다.
    그럼 어떻게 할 것인가?.
    1:1 JOIN 의 전형은 아니지만 전체구매금액과 판매금액을 1:1로 연결
    하기 위해서는 1:1 JOIN으로 연결되어야 한다.
    다른말로 하면 DETAIL의 판매구분을 무시한상태로 연결이 이루어져야
    한다는 뜻인데,
    무시된다는 뜻은 있어도 없는걸로 여기라는 말이 아니다.
    판매구분이 없어야 년월일/사업장 만이 KEY가 되고 그래야만 MASTER
    TABLE인 T02와 1:1로 연결을 해 줄 수 있다.
    실제로 연결을 해보자.
    5월1일자 DATA만을 이용하여 년월/사업장을 JOIN 조건으로 연결하고
    구매금액/판매금액의 합을 보자.

    미리 답을 보면 다음과 같다.
    판매금액

    SITE_CD    SUM(SALE_AM)
    ---------- ------------
    부산               2000
    서울               2300
    인천               2100    

    구매금액
    SITE_CD      PROC_AM
    ---------- ---------
    부산            2100
    서울            2000
    인천            2100
  
    년월일과 사업장만을 연결한다면 문장은 다음과 같이 될 것이다.

     SELECT A.SITE_CD,
            SUM(A.PROC_AM) PROC_AM,
            SUM(B.SALE_AM) SALE_AM
     FROM   T02 A,
            T01 B
     WHERE  B.YMD     = '19990501'
     AND    A.YMD     = B.YMD
     AND    A.SITE_CD = B.SITE_CD
     GROUP BY
           A.SITE_CD

     결과는 어떤가?..다음과 같다.
  
     SITE_CD      PROC_AM   SALE_AM
     ---------- --------- ---------
     부산            4200      2000
     서울            4000      2300
     인천            4200      2100

     판매금액은 원하는 값이 나오지만 구매금액은 2배가 나왔다..
     어찌하여 이런일이 일어나는가?.
     그 질문에 답하기 전에 분석을 해보자.
     위의 문장에서 GROUP BY를 빼고 SUM을 빼보자.

     SELECT A.SITE_CD,
            A.PROC_AM,
            B.SALE_AM
     FROM   T02 A,
            T01 B
     WHERE  B.YMD     = '19990501'
     AND    A.YMD     = B.YMD
     AND    A.SITE_CD = B.SITE_CD

     결과는 아래와 같다.

     SITE_CD      PROC_AM   SALE_AM
     ---------- --------- ---------
     부산            2100       900
     부산            2100      1100
     서울            2000      1000
     서울            2000      1300
     인천            2100      1200
     인천            2100       900

     우리는 년월/사업장을 KEY로 가진 T02 와
            년월/사업장/판매구분을 KEY로 가진 T01을 년월/사업장으로
     연결했다.
     T02의 5월1일자에는 3개의 RECORD가 있으며
     T01의 5월1일자에는 6개의 RECORD가 있다.
     MASTER TABLE인 T02를 기준으로 보면 T02의 한RECORD마다
     T01에는 직판/구매 두개의 DETAIL이 존재한다.
     결과는 DETAIL의 RECORD 수와 같은 6개가 나온다.
     1:M JOIN의 전형이며 이때의 결과는 M이 된다.
     우리가 이미 알고 있는 내용을 눈으로 직접 확인해 보았다.
     왜 구매금액이 2배가 되었는지에 대한 대답이 저절로 나온다.
     6개의 RECORD를 SUM하면 판매금액 입장에서는 맞지만
     구매금액 입장에서는 2배로 뻥튀기가 된다.
     이 내용은 잠시후에 다룰 문제2 에서 유용하게 이용될것이다.
     지금은 2배로 늘어나는 구매금액을 어떻게 바르게 읽어올 것인가?.
     즉, 어떻게 1:M을 1:1조인으로 연결해 줄것인가를 해결하고
     그것을 이용해 올바른 답을 구하는 일이다.

해법1.
     어떻게 판매구분을 무시할 것인가?..와
     어떻게 뻥튀기를 방지할 것인가?...는 동일한 문제이고
     동일한 해법을 요구한다.즉,
     어떻게 뻥튀기를 방지할것인가 하는 해법이 바로
     DETAIL의 또하나의 KEY 인 판매구분을
     문제를 일으키지 않고 무시할 것인가 하는 문제와 일맥상통한다.
     DETAIL인 T01을 T02와 같은 KEY로 변형하자면 년월일/사업장별로
     GROUP BY를 하여 KEY를 일치시킨후 JOIN을 걸어주면된다.
     GROUP BY를 통해 가공한 DATASET을 INLINE VIEW로 이용하여
     MASTER TABLE인 T02와 JOIN을 걸어보자.
     아래와 같이..

     SELECT A.YMD,
            A.SITE_CD,
            ROUND(100 * (B.SALE_AM - A.PROC_AM)/B.SALE_AM) PERCENT
     FROM   T02 A,
           (SELECT YMD,SITE_CD,
                   SUM(SALE_AM) SALE_AM
            FROM   T01
            WHERE  YMD BETWEEN '19990501' AND '19990502'
            GROUP BY
                   YMD,SITE_CD
           )    B
     WHERE  A.YMD     = B.YMD
     AND    A.SITE_CD = B.SITE_CD

     T01을 GROUP BY한 B DATASET은 2틀에 걸쳐 6개의 RECORD를 RETURN한다.

     YMD      SITE_CD      SALE_AM
     -------- ---------- ---------
     19990501 부산            2000
     19990501 서울            2300
     19990501 인천            2100
     19990502 부산            2200
     19990502 서울            2600
     19990502 인천            2300

     T02의 이틀에 걸친 DATA를 보자.

     YMD      SITE_CD      PROC_AM
     -------- ---------- ---------
     19990501 부산            2100
     19990501 서울            2000
     19990501 인천            2100
     19990502 부산            1900
     19990502 서울            2100
     19990502 인천            1800

     두개의 DATASET이 년월/사업장을 KEY를 정확이 일치한다.
     이것을 JOIN 한것이 위의 답이다.

뒷풀이.
    1:1 JOIN 문제를 통해서 1:M 조인도 함께 생각해 보았다.
    다음문제를 통해 1:M 문제를 다루어 보자.
    DATA의 최종결과를 뽑아내기위해서는 한단계씩 밟아가는 과정이
    있다.
    이 과정을 이해하고 만들어 내는 힘은 많은 생각을 필요로 하고
    많은 생각을 하는 능력은 다양한 문제를 통해서 이다.
    한번 읽어보고 이해했다고 하기보다는 직접 QUERY를 만들어보고
    DATA를 단계별로 분해해 보는 것이 도움이 될 것이다.

문제2.
    우리는 조건을 통해서 구매물량이 50:50으로 직판/대리 로
    나누어 판매가 된다는 것을 알고 있다.
    이번에는 일자/사업장/판매구분 별로 마진율을 구해보자.

    결과가 다음과 같이 나오면된다.

    YMD      SITE_CD    SALE A.PROC_AM*0.5   SALE_AM   PERCENT
    -------- ---------- ---- ------------- --------- ---------
    19990501 부산       직판          1050       900       -14
    19990501 부산       대리          1050      1100         5
    19990501 서울       직판          1000      1000         0
    19990501 서울       대리          1000      1300        30
    19990501 인천       직판          1050      1200        14
    19990501 인천       대리          1050       900       -14
    19990502 부산       직판           950      1000         5
    19990502 부산       대리           950      1200        26
    19990502 서울       직판          1050      1200        14
    19990502 서울       대리          1050      1400        33
    19990502 인천       직판           900      1300        44
    19990502 인천       대리           900      1000        11

해법2.
    문제1도 어려운 문제는 아니지만 문제1에서 이미 해법을 제시했기에
    문제1을 정독했다면 쉽게 답이 나올 것이다.
    두개의 테이블을 조인하되 년월과 사업장으로만 JOIN을 하면
    문제1에서 예시한대로 DETAIL에 맞도록 1:M 조인이된다.
    GROUP BY 하거나 SUM이 필요 없이 계산식만 이용하면 된다.
    다음 문장이 문제의 답이다.

    SELECT B.YMD,
           B.SITE_CD,
           B.SALE_TY,
           A.PROC_AM*0.5,
           B.SALE_AM,
           ROUND(100*(B.SALE_AM-A.PROC_AM*0.5)/(A.PROC_AM*0.5)) PERCENT
    FROM   T02 A,
           T01 B
    WHERE  B.YMD     BETWEEN '19990501' AND '19990502'
    AND    A.YMD     = B.YMD
    AND    A.SITE_CD = B.SITE_CD;
뒷풀이2.
    1:1 조인 과 1:M 조인의 차이에 대해서 알아 보았다.
    그 결과가 어떠한지도 눈으로 확인해 보았다.
    흔히 지나치기 쉬운 오류다.
    QUERY되어 결과는 나오는데 그결과가 맞는 답이 아니라는데
    심각성이 있다.
    조인이 일어나는 과정을 정확히 이해하고 있을 필요가 있다.
    문제가 발생해도 왜,어다가 문제인지 조차 모른다면 안될 것이다.
위로