๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
  • Round and Round
DB/Oracle

[Oracle] PIVOT ์—ฐ์‚ฐ๊ณผ UNPIVOT ์—ฐ์‚ฐ

by ํ‰๊ธ€ํ‰๊ธ€ 2023. 11. 1.

๐Ÿงท PIVOT ์—ฐ์‚ฐ

PIVOT ์—ฐ์‚ฐ์€ ๋ฐ์ดํ„ฐ๋ฅผ ํ–‰์—์„œ ์—ด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ํ•จ์ˆ˜์ด๋‹ค. ์ง€์ •ํ•œ ์—ด์˜ ๊ฐ’์„ ์—ด๋กœ PIVOTํ•˜๊ณ , ์ง‘๊ณ„ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ทธ ์—ด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ง‘๊ณ„ํ•œ๋‹ค.

PIVOT [XML]
    (
    ์ง‘๊ณ„ํ•จ์ˆ˜(expr) [AS ๋ณ„์นญ] 
    FOR column
    IN (
        expr [AS ๋ณ„์นญ] | 
        Subquery | 
        ANY
        ) 
    )
  • ์ง‘๊ณ„ํ•จ์ˆ˜(expr) : ์ง‘๊ณ„ํ•จ์ˆ˜์™€ ์ง‘๊ณ„ํ•  ์—ด์„ ์ง€์ •ํ•˜๊ณ , ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ง‘๊ณ„ํ•จ์ˆ˜๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • FOR ์ ˆ : FOR ์ ˆ์€ PIVOT ํ•  ์—ด์„ ์ง€์ •ํ•œ๋‹ค.
    • ๋‹ค์ˆ˜์˜ ์—ด์„ ๊ธฐ์ˆ ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • FOR ์ ˆ์— ์ง€์ •๋˜์ง€ ์•Š์€ ์—ด์„ ๊ธฐ์ค€์œผ๋กœ ์ง‘๊ณ„๋˜๊ธฐ ๋•Œ๋ฌธ์— Inline View๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉํ•  ์—ด์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค.
  • IN ์ ˆ์€ PIVOT ํ•  ์—ด ๊ฐ’ (= FOR ์ ˆ์—์„œ ์ง€์ •๋œ ์—ด ๊ฐ’)์„ ์ง€์ •ํ•œ๋‹ค.
    • IN์— ์ง€์ •๋œ ์—ด ๊ฐ’์€ ๋ณ„์นญ์„ ํ†ตํ•ด ์›ํ•˜๋Š” ์ด๋ฆ„์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • XML ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด Subquery ํ˜น์€ ANY ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ์ œ # 1

SELECT *
FROM(
    SELECT STANDARD_COST, CATEGORY_ID, SUBSTR(DESCRIPTION,0,1) AS DESCS
    FROM PRODUCTS
    )
PIVOT(
    SUM(STANDARD_COST) 
    FOR CATEGORY_ID 
    IN (1, 2, 5, 4)
    );

 

FOR ์ ˆ์˜ ๊ฐ’(IN ์ ˆ์˜ ๊ฐ’)๋“ค์€ ์—ด๋กœ ๋ณ€ํ™˜๋œ๋‹ค. PIVOT ์ ˆ์€ ์ง‘๊ณ„ํ•จ์ˆ˜์— ์‚ฌ์šฉ๋œ STANDARD_COST ์ปฌ๋Ÿผ๊ณผ FOR ์ ˆ์— ๋ช…์‹œ๋˜์ง€ ์•Š์€ DESCS ์ปฌ๋Ÿผ์„ ๊ธฐ์ค€์œผ๋กœ ์ง‘๊ณ„๋œ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ, Inline View๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉํ•  ์—ด์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค.

 

PIVOT TEST ์ด๋ฏธ์ง€ 1

์˜ˆ์ œ #2 ๋ณ„์นญ

SELECT *
FROM(
    SELECT STANDARD_COST, CATEGORY_ID, SUBSTR(DESCRIPTION,0,1) AS DESCS
    FROM PRODUCTS
    )
PIVOT(
    SUM(STANDARD_COST) AS COST 
    FOR CATEGORY_ID 
    IN (1 AS ID01, 2 AS ID02, 4 AS ID04, 5 AS ID05)
    );

 

FOR์ ˆ๊ณผ IN์ ˆ์€ ๋ชจ๋‘ ๋ณ„์นญ์„ ์ง€์ • ํ•  ์ˆ˜ ์žˆ๋‹ค. IN๋ณ„์นญ_FOR๋ณ„์นญ ํ˜•์‹์œผ๋กœ ์—ด ๋ช…์ด ์ง€์ •๋œ๋‹ค.

 

PIVOT TEST ์ด๋ฏธ์ง€ 2

 

[ ๋ณ„์นญ ์˜ˆ์‹œ ]

  1 1 AS ID01
SUM(COST) 1 ID01
SUM(COST) AS COST 1_COST ID01_COST

 

์˜ˆ์ œ #3 ๋‹ค์ค‘ ์—ด

PIVOT ์ ˆ์€ ๋‹ค์ˆ˜์˜ ์ง‘๊ณ„ํ•จ์ˆ˜๋ฅผ ์ง€์›ํ•œ๋‹ค.

SELECT *
FROM(
    SELECT STANDARD_COST, LIST_PRICE, CATEGORY_ID, SUBSTR(DESCRIPTION,0,1) AS DESCS
    FROM PRODUCTS
    )
PIVOT(
    SUM(STANDARD_COST) AS COST, MAX(LIST_PRICE) AS PRICE 
    FOR CATEGORY_ID 
    IN (1, 2, 4, 5)
    );

 

์ง‘๊ณ„ํ•จ์ˆ˜์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ง‘๊ณ„ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ์„œ๋กœ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋Š” ๋ณ„์นญ์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค. ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ORA-00918: ์—ด์˜ ์ •์˜๊ฐ€ ์• ๋งคํ•ฉ๋‹ˆ๋‹ค ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

 

PIVOT TEST ์ด๋ฏธ์ง€ 3

์ง‘๊ณ„ํ•จ์ˆ˜ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ FOR ์ ˆ์—๋„ ๋‹ค์ˆ˜์˜ ์—ด์„ ๊ธฐ์ˆ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

SELECT *
FROM(
    SELECT STANDARD_COST, LIST_PRICE, CATEGORY_ID, 
           SUBSTR(DESCRIPTION,0,1) AS DESCS, MOD(PRODUCT_ID,2) AS PRD_ID
    FROM PRODUCTS
    )
PIVOT(
    SUM(STANDARD_COST) AS COST, MAX(LIST_PRICE) AS PRICE 
    FOR (PRD_ID, CATEGORY_ID) 
    IN ((0,1) AS ID01,(0,2) AS ID02,(0,4) AS ID04,(0,5) AS ID05,(1,1) AS ID11,(1,2) AS ID12,(1,4) AS ID14,(1,5) AS ID15)
    );

 

FO R์ ˆ์— ๋‹ค์ˆ˜์˜ ์—ด์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, FOR ์ ˆ์˜ ๊ด„ํ˜ธ ์•ˆ์— ์—ด ์ˆœ์„œ์™€ IN ์ ˆ์˜ ๊ด„ํ˜ธ ์•ˆ์˜ ์—ด ๊ฐ’ ์ˆœ์„œ๊ฐ€ ์ผ์น˜ํ•ด์•ผ ํ•œ๋‹ค.

 

PIVOT TEST ์ด๋ฏธ์ง€ 4

์˜ˆ์ œ #4 Subquery & ANY

ORACLE์€ IN ์ ˆ์— ๋™์  ๊ฐ’ ์ง‘ํ•ฉ์„ ์ง์ ‘ ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ค์šฐ๋ฉฐ, ๋Œ€์‹  XML PIVOT์„ ์‚ฌ์šฉํ•˜์—ฌ ๋™์  ๊ฐ’ ์ง‘ํ•ฉ์„ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ XML ์—†์ด Subquery ๋ฐ ANY ์—ฐ์‚ฐ์ž๋กœ ๋™์  ๊ฐ’ ์ง‘ํ•ฉ์„ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์€ ์–ด๋ ต๋‹ค.

 

[ Subquery ]

SELECT *
FROM(
    SELECT STANDARD_COST, CATEGORY_ID
    FROM PRODUCTS
    )
PIVOT XML
    (
    SUM(STANDARD_COST) AS COST 
    FOR (CATEGORY_ID)
    IN (
        SELECT DISTINCT CATEGORY_ID
          FROM PRODUCTS
       )
    );

 

PIVOT TEST ์ด๋ฏธ์ง€ 5

[ ANY ]

SELECT *
FROM(
    SELECT STANDARD_COST, CATEGORY_ID, SUBSTR(DESCRIPTION,0,1) AS DESCS
    FROM PRODUCTS
    )
PIVOT XML
    (
    SUM(STANDARD_COST) AS COST 
    FOR (CATEGORY_ID)
    IN (ANY)
    );

 

PIVOT TEST ์ด๋ฏธ์ง€ 6

์˜ˆ์ œ #5 ๋™์  SQL

ORACLE์—์„œ๋Š” IN ์ ˆ์— ๋™์  ๊ฐ’ ์ง‘ํ•ฉ์„ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜๋Š” ์—†์ง€๋งŒ, Dynamic SQL์„ ํ†ตํ•ด ์ด๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

VAR RESULT REFCURSOR;

DECLARE
    dynamic_sql VARCHAR(4000);
BEGIN
    FOR X IN (SELECT DISTINCT CATEGORY_ID FROM PRODUCTS)
    LOOP
        dynamic_sql := dynamic_sql || '''' || X.CATEGORY_ID || '''' || ', ';
    END LOOP;

    dynamic_sql := SUBSTR(dynamic_sql, 1, LENGTH(dynamic_sql) - 2);

    dynamic_sql :=
        '
            SELECT *
            FROM(
                SELECT STANDARD_COST, CATEGORY_ID
                FROM PRODUCTS
                )
            PIVOT 
                (
                SUM(STANDARD_COST) AS COST 
                FOR (CATEGORY_ID)
                IN (' || dynamic_sql || ')
                )
        ';

    DBMS_OUTPUT.PUT_LINE('Dynamic SQL: ' || dynamic_sql);
    OPEN :RESULT FOR dynamic_sql;

END;
/

PRINT RESULT

 

PIVOT TEST ์ด๋ฏธ์ง€ 7


๐Ÿ–‡๏ธ UNPIVOT

UNPIVOT ์—ฐ์‚ฐ์€ PIVOT ์—ฐ์‚ฐ๊ณผ๋Š” ๋ฐ˜๋Œ€๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์—ด์—์„œ ํ–‰์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค. ์ง€์ •ํ•œ ์—ด์˜ ๊ฐ’์„ ํ–‰์œผ๋กœ UNPIVOTํ•œ๋‹ค.

UNPIVOT [{INCLUDE | EXCLUDE} NULLS]
    (
        column  
        FOR column
        IN  column [AS literal]
    )
  • UNPIVOT ์ ˆ : UNPIVOT ์ ˆ์€ UNPIVOT ๋œ ๊ฐ’์„ ํฌํ•จํ•  ์—ด์˜ ์ด๋ฆ„์„ ์ง€์ •ํ•œ๋‹ค.
    • INCLUDE NULLS : NULL ๊ฐ’ ํฌํ•จ
    • EXCLUDE NULLS : NULL ๊ฐ’ ๋ฏธ ํฌํ•จ
    • ์˜ต์…˜์„ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ NULL ๊ฐ’์„ ํฌํ•จํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • FOR ์ ˆ : FOR ์ ˆ์€ UNPIVOT ์ ˆ์— ๋ช…์‹œํ•œ ์—ด ์ด๋ฆ„์ด ํฌํ•จํ•  ์—ด์˜ ์ด๋ฆ„์„ ์ง€์ •ํ•œ๋‹ค.
  • IN ์ ˆ : IN ์ ˆ์€ UNPIVOT ์ ˆ์— ๋ช…์‹œํ•œ ์—ด ๊ฐ’๊ณผ ๋ฆฌํ„ฐ๋Ÿด ๊ฐ’์„ ์ง€์ •ํ•œ๋‹ค.

์˜ˆ์ œ # 1

SELECT *
  FROM (
        SELECT FIRST_NAME, LAST_NAME
          FROM CONTACTS
        )
UNPIVOT(
        NAMES FOR FIRST_LAST IN (FIRST_NAME, LAST_NAME)
        )

 

IN ์ ˆ์˜ ์—ด ๊ฐ’์ด UNPIVOT์ ˆ์— ๋ช…์‹œํ•œ ์—ด ๊ฐ’์œผ๋กœ ์ด๋™ํ•œ๋‹ค. ๋˜ํ•œ, IN ์ ˆ์˜ ์—ด ์ด๋ฆ„์€ FOR ์ ˆ์— ๋ช…์‹œํ•œ ์—ด ๊ฐ’์œผ๋กœ ์ด๋™ํ•œ๋‹ค.

 

UNPIVOT TEST ์ด๋ฏธ์ง€ 1

์˜ˆ์ œ # 2 ๋ณ„์นญ

SELECT *
  FROM (
        SELECT FIRST_NAME, LAST_NAME
          FROM CONTACTS
        )
UNPIVOT(
        NAMES FOR FIRST_LAST IN (FIRST_NAME AS 'FIRST', LAST_NAME AS 'LAST')
        )

 

IN ์ ˆ์— ๋ณ„์นญ์„ ์ง€์ •ํ•˜๋ฉด, FOR ์ ˆ์— ๋ช…์‹œํ•œ ์—ด์˜ ๊ฐ’์ด ์•„๋‹Œ ๋ณ„์นญ์œผ๋กœ ์—ด ๊ฐ’์„ ๋ณ€๊ฒฝ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

UNPIVOT TEST ์ด๋ฏธ์ง€ 2

์˜ˆ์ œ # 3 ์˜ต์…˜

SELECT *
  FROM (
        SELECT FIRST_NAME, '' AS LAST_NAME
          FROM CONTACTS
        )
UNPIVOT INCLUDE NULLS
        (
        NAMES FOR FIRST_LAST IN (FIRST_NAME AS 'FIRST', LAST_NAME AS 'LAST')
        );

 

UNPIVOT ์—ฐ์‚ฐ์€ NULL ๊ฐ’์„ ํฌํ•จํ•˜์ง€ ์•Š์ง€๋งŒ INCLUDE NULLS์„ ํ†ตํ•ด NULL ๊ฐ’์„ ๊ฒฐ๊ณผ์— ํฌํ•จ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

UNPIVOT TEST ์ด๋ฏธ์ง€ 3

 


Reference

'DB > Oracle' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Oracle] LATERAL & APPLY JOIN ์—ฐ์‚ฐ  (0) 2023.11.02
[Oracle] Oracle ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค  (0) 2023.10.27