구분값에 따라 행을 열로 변경 0 5 773

by 이노리 [SQL Query] [2020.02.25 14:15:44]


WITH MASTER_TABLE AS (
    SELECT 1 AS GRP_SEQ, 'AG0000001' AS GRP_ID FROM DUAL
), DETAIL_TABLE AS (
    SELECT 1 AS DETAIL_SEQ, 1 AS GRP_SEQ, '카테고리 > 전자기기' AS GRP_PATH, 'I' AS GRP_GUBUN FROM DUAL
    UNION ALL
    SELECT 2 AS DETAIL_SEQ, 1 AS GRP_SEQ, '카테고리 > 전자기기 > 컴퓨터' AS GRP_PATH, 'E' AS GRP_GUBUN FROM DUAL
    UNION ALL
    SELECT 3 AS DETAIL_SEQ, 1 AS GRP_SEQ, '카테고리 > 전자기기 > 노트북' AS GRP_PATH, 'I' AS GRP_GUBUN FROM DUAL
    UNION ALL
    SELECT 4 AS DETAIL_SEQ, 1 AS GRP_SEQ, '카테고리 > 전자기기 > 외장하드' AS GRP_PATH, 'I' AS GRP_GUBUN FROM DUAL
)
 SELECT
     A.GRP_SEQ, A.GRP_ID, B.GRP_PATH, B.GRP_GUBUN
 FROM MASTER_TABLE A
 INNER JOIN DETAIL_TABLE B ON A.GRP_SEQ = B.GRP_SEQ
;

 

안녕하세요.
제목을 한참 고민했는데요.

우선 데이터는 위와 같습니다.

총 4row 인데 GRP_GUBUN 값이 I인 경우, E인 경우 2가지가 존재합니다.

 

해당 데이터로 아래와 같은 결과값을 내고 싶습니다.

다만 GRP_GUBUN이 I인 값과 E인 값 어느쪽이 더 row가 많을 지는 알 수 없습니다. (데이터에 따라 달라집니다.)

현재 위에 적은 테스트 데이터 상에는 I인 값이 3건으로 더 많지만 실제 데이터는 I가 많을 수도 있고 E가 많을 수도 있고 같을 수도 있습니다.

 

--------------------------------------------------------------------------
GRP_SEQ     GRP_ID      GRP_PATH_I                  	GRP_PATH_E
--------------------------------------------------------------------------
    1       AG0000001   카테고리 > 전자기기             	카테고리 > 전자기기 > 컴퓨터
--------------------------------------------------------------------------
    1       AG0000001   카테고리 > 전자기기 > 노트북	NULL
--------------------------------------------------------------------------
    1       AG0000001   카테고리 > 전자기기 > 외장하드	NULL
--------------------------------------------------------------------------

 

 

GRP_GUBUN이 I인 항목이 3row, E인 항목이 1row 인데

셀프조인 시 row를 3개로 출력까지 했으나 E인 항목이 1 row만 출력되는 것이 아니라 동일한 내용으로 3row가 출력됩니다.

1row만 출력되고 나머지 항목이 null로 뜨게끔 할 수 없을까요?

 

셀프 조인이 아닌 다른 방법으로 원하는 모양으로 결과값을 가져올 수 있는 방법이 있다면 조언 부탁 드립니다.

by 마농 [2020.02.25 15:25:31]
WITH detail_table AS
(
SELECT 1 detail_seq, 1 grp_seq, 'AG0000001' grp_id, '카테고리 > 전자기기' grp_path, 'I' grp_gubun FROM dual
UNION ALL SELECT 2, 1, 'AG0000001', '카테고리 > 전자기기 > 컴퓨터'  , 'E' FROM dual
UNION ALL SELECT 3, 1, 'AG0000001', '카테고리 > 전자기기 > 노트북'  , 'I' FROM dual
UNION ALL SELECT 4, 1, 'AG0000001', '카테고리 > 전자기기 > 외장하드', 'I' FROM dual
)
-- 1. Group By, Min(Decode)
SELECT grp_seq
     , grp_id
     , rn
     , MIN(DECODE(grp_gubun, 'I', grp_path)) grp_path_I
     , MIN(DECODE(grp_gubun, 'E', grp_path)) grp_path_E
  FROM (SELECT grp_seq, grp_id, grp_gubun, grp_path
             , ROW_NUMBER() OVER(PARTITION BY grp_seq, grp_id, grp_gubun ORDER BY detail_seq) rn
          FROM detail_table
        )
 GROUP BY grp_seq, grp_id, rn
 ORDER BY grp_seq, grp_id, rn
;
-- 2. PIVOT
SELECT *
  FROM (SELECT grp_seq, grp_id, grp_gubun, grp_path
             , ROW_NUMBER() OVER(PARTITION BY grp_seq, grp_id, grp_gubun ORDER BY detail_seq) rn
          FROM detail_table
        )
 PIVOT (MIN(grp_path) grp_path FOR grp_gubun IN ('I' i, 'E' e))
 ORDER BY grp_seq, grp_id, rn
;

 


by 이노리 [2020.02.25 15:28:47]

앗! 저는 FULL OUTER JOIN을 써봤는데 이런식으로 하니 간단하게 되네요!

답변 정말 감사합니다! 실제 디비 구조에 맞게 쿼리로 짜보겠습니다.


by 이노리 [2020.02.25 15:26:50]
WITH MASTER_TABLE AS (
    SELECT 1 AS GRP_SEQ, 'AG0000001' AS GRP_ID FROM DUAL
), DETAIL_TABLE AS (
    SELECT 1 AS DETAIL_SEQ, 1 AS GRP_SEQ, '카테고리 > 전자기기' AS GRP_PATH, 'I' AS GRP_GUBUN FROM DUAL
    UNION ALL
    SELECT 2 AS DETAIL_SEQ, 1 AS GRP_SEQ, '카테고리 > 전자기기 > 컴퓨터' AS GRP_PATH, 'E' AS GRP_GUBUN FROM DUAL
    UNION ALL
    SELECT 3 AS DETAIL_SEQ, 1 AS GRP_SEQ, '카테고리 > 전자기기 > 노트북' AS GRP_PATH, 'I' AS GRP_GUBUN FROM DUAL
    UNION ALL
    SELECT 4 AS DETAIL_SEQ, 1 AS GRP_SEQ, '카테고리 > 전자기기 > 외장하드' AS GRP_PATH, 'I' AS GRP_GUBUN FROM DUAL
)
 SELECT
     CASE WHEN A.GRP_SEQ IS NULL THEN B.GRP_SEQ ELSE A.GRP_SEQ END AS GRP_SEQ,
     CASE WHEN A.GRP_ID IS NULL THEN B.GRP_ID ELSE A.GRP_ID END AS GRP_ID,
     A.INCLUDE_NM,
     B.EXCLUDE_NM
 FROM (
         SELECT
             MT.GRP_SEQ, MT.GRP_ID,
             DT.GRP_GUBUN,
             DECODE(DT.GRP_GUBUN, 'I', DT.GRP_PATH) AS INCLUDE_NM,
             DECODE(DT.GRP_GUBUN, 'E', DT.GRP_PATH) AS EXCLUDE_NM,
             ROW_NUMBER() OVER (ORDER BY DT.DETAIL_SEQ DESC) AS ROW_NUM
         FROM DETAIL_TABLE DT
         INNER JOIN MASTER_TABLE MT ON DT.GRP_SEQ = MT.GRP_SEQ
         WHERE DT.GRP_GUBUN = 'I'
        ) A
 FULL OUTER JOIN (
     SELECT
         MT.GRP_SEQ, MT.GRP_ID,
         DT.GRP_GUBUN,
         DECODE(DT.GRP_GUBUN, 'I', DT.GRP_PATH) AS INCLUDE_NM,
         DECODE(DT.GRP_GUBUN, 'E', DT.GRP_PATH) AS EXCLUDE_NM,
         ROW_NUMBER() OVER (ORDER BY DT.DETAIL_SEQ DESC) AS ROW_NUM
     FROM DETAIL_TABLE DT
              INNER JOIN MASTER_TABLE MT ON DT.GRP_SEQ = MT.GRP_SEQ
     WHERE DT.GRP_GUBUN = 'E'
     ) B ON A.GRP_SEQ = B.GRP_SEQ AND A.ROW_NUM = B.ROW_NUM
;

 

 

자문 자답이지만 하다보니 이렇게 해도 답이 나오네요!


by 마농 [2020.02.25 15:32:52]

테이블 구조가 조금 이상하네요. 정규화 되어 있지 않은 느낌입니다.
grp_seq 가 키라면? grp_id 는 상세테이블에 있으면 안되는 거죠.
grp_id 가 상세테이블에 있다면, 마스터 테이블과의 조인이 불필요하구요.
작성하신 쿼리의 순번 부여하는 부분은 Partition by 구문이 필요해 보입니다.


by 이노리 [2020.02.25 15:47:29]

아 말씀하신대로 상세테이블에는 GRP_ID가 없어야 하네요. 제가 WITH절에 잘못 기입했네요.

상세테이블에는 GRP_SEQ, DETAIL_SEQ가 있고

올려주신 GROUP BY + MIN(DECODE()) 조합으로 대입시켜보니 원하던 값이 잘 나오네요!

PARTITION BY 구문은 조금 더 공부해보도록 하겠습니다~

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