IT

[Oracle] 오라클 SQL 튜닝, 집계함수, 누적 구하기, JOIN, GROUP BY

정미나 2018. 1. 14. 16:42

2012년 1월에 팔린 상품의 일별 상품별 판매수량과 판매금액 및 각각의 랭킹 구하기


* 결과값


* 쿼리1

SELECT O.ORD_DT "판매일자", 

            I.ITEM_ID "상품ID",

            I.ITEM_NM "상품명",

            SUM(O.ORD_ITEM_QTY) "판매수량",

            SUM(O.ORD_ITEM_QTY*P.PRICE) "판매금액",

            RANK() OVER(PARTITION BY O.ORD_DT ORDER BY SUM(O.ORD_ITEM_QTY) DESC) "판매수량랭킹",

            RANK() OVER(PARTITION BY O.ORD_DT ORDER BY SUM(O.ORD_ITEM_QTY*P.PRICE) DESC

           "판매금액랭킹"

  FROM ORD_ITEM O  

   LEFT OUTER JOIN ITEM I

    ON O.ITEM_ID = I.ITEM_ID,

  (

    SELECT P.ITEM_ID,

                SUM(P.SALE_PRICE) PRICE

      FROM UITEM_PRICE P

    GROUP BY P.ITEM_ID

  ) P

  WHERE I.ITEM_ID = P.ITEM_ID

      AND O.ORD_DT BETWEEN '20120101' AND '20120131'

  GROUP BY O.ORD_DT, I.ITEM_ID, I.ITEM_NM

  ORDER BY O.ORD_DT, I.ITEM_ID;


* 실행계획

-----------------------------------------------------------------------------------------------------------------

| Id  | Operation                            | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |

-----------------------------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT                     |                |      1 |        |   2852 |00:00:01.78 |   43431 |

|   1 |  SORT ORDER BY                       |                |      1 |    501 |   2852 |00:00:01.78 |   43431 |

|   2 |   WINDOW SORT                        |                |      1 |    501 |   2852 |00:00:01.78 |   43431 |

|   3 |    WINDOW SORT                       |                |      1 |    501 |   2852 |00:00:01.77 |   43431 |

|   4 |     HASH GROUP BY                    |                |      1 |    501 |   2852 |00:00:01.77 |   43431 |

|*  5 |      HASH JOIN                       |                |      1 |    535K|   1546K|00:00:01.28 |   43431 |

|   6 |       NESTED LOOPS                   |                |      1 |     92 |     92 |00:00:00.01 |      12 |

|   7 |        TABLE ACCESS FULL             | ITEM           |      1 |     92 |     92 |00:00:00.01 |       7 |

|   8 |        VIEW PUSHED PREDICATE         |                |     92 |      1 |     92 |00:00:00.01 |       5 |

|*  9 |         FILTER                       |                |     92 |        |     92 |00:00:00.01 |       5 |

|  10 |          SORT AGGREGATE              |                |     92 |      1 |     92 |00:00:00.01 |       5 |

|  11 |           TABLE ACCESS BY INDEX ROWID| UITEM_PRICE    |     92 |      1 |    130 |00:00:00.01 |       5 |

|* 12 |            INDEX RANGE SCAN          | UITEM_PRICE_PK |     92 |      1 |    130 |00:00:00.01 |       3 |

|  13 |       TABLE ACCESS BY INDEX ROWID    | ORD_ITEM       |      1 |    535K|   1546K|00:00:00.73 |   43419 |

|* 14 |        INDEX RANGE SCAN              | ORD_ITEM_X01   |      1 |    535K|   1546K|00:00:00.20 |    5815 |

-----------------------------------------------------------------------------------------------------------------

 

Predicate Information (identified by operation id):

---------------------------------------------------

 

   5 - access("O"."ITEM_ID"="I"."ITEM_ID")

   9 - filter((COUNT(*)>0 AND '20120101'<='20120131'))

  12 - access("P"."ITEM_ID"="I"."ITEM_ID")

  14 - access("O"."ORD_DT">='20120101' AND "O"."ORD_DT"<='20120131')


※ JOIN 전에 상품+주문 테이블을 GROUP BY 해주는 게 더 효율적


* 쿼리2

SELECT O.ORD_DT "판매일자",

            O.ITEM_ID "상품ID",

            O.ITEM_NM "상품명",

            O.QTY "판매수량",

            P.PRICE*O.QTY "판매금액",

            RANK() OVER(PARTITION BY ORD_DT ORDER BY O.QTY DESC) "판매수량랭킹",

            RANK() OVER(PARTITION BY ORD_DT ORDER BY P.PRICE*O.QTY DESC) "판매금액랭킹"

  FROM

      (

            SELECT O.ORD_DT, 

                        I.ITEM_ID,

                  I.ITEM_NM,

                  SUM(O.ORD_ITEM_QTY) QTY

        FROM ORD_ITEM O  

         LEFT OUTER JOIN ITEM I

           ON O.ITEM_ID = I.ITEM_ID

    WHERE O.ORD_DT BETWEEN '20120101' AND '20120131'

    GROUP BY O.ORD_DT, I.ITEM_ID, I.ITEM_NM

  ) O,

  (

            SELECT P.ITEM_ID, 

                       SUM(P.SALE_PRICE) PRICE

              FROM UITEM_PRICE P

            GROUP BY P.ITEM_ID

       ) P  

WHERE O.ITEM_ID = P.ITEM_ID

ORDER BY O.ORD_DT, O.ITEM_ID;


* 실행계획

---------------------------------------------------------------------------------------------------------------

| Id  | Operation                          | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |

---------------------------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT                   |                |      1 |        |   2852 |00:00:01.70 |   43429 |

|   1 |  SORT ORDER BY                     |                |      1 |  46047 |   2852 |00:00:01.70 |   43429 |

|   2 |   WINDOW SORT                      |                |      1 |  46047 |   2852 |00:00:01.69 |   43429 |

|   3 |    WINDOW SORT                     |                |      1 |  46047 |   2852 |00:00:01.69 |   43429 |

|*  4 |     HASH JOIN                      |                |      1 |  46047 |   2852 |00:00:01.69 |   43429 |

|   5 |      VIEW                          |                |      1 |     92 |     92 |00:00:00.01 |       3 |

|   6 |       HASH GROUP BY                |                |      1 |     92 |     92 |00:00:00.01 |       3 |

|   7 |        TABLE ACCESS BY INDEX ROWID | UITEM_PRICE    |      1 |    130 |    130 |00:00:00.01 |       3 |

|   8 |         INDEX FULL SCAN            | UITEM_PRICE_PK |      1 |    130 |    130 |00:00:00.01 |       1 |

|   9 |      VIEW                          |                |      1 |  46047 |   2852 |00:00:01.68 |   43426 |

|  10 |       HASH GROUP BY                |                |      1 |  46047 |   2852 |00:00:01.68 |   43426 |

|* 11 |        HASH JOIN RIGHT OUTER       |                |      1 |    535K|   1546K|00:00:01.25 |   43426 |

|  12 |         TABLE ACCESS FULL          | ITEM           |      1 |     92 |     92 |00:00:00.01 |       7 |

|  13 |         TABLE ACCESS BY INDEX ROWID| ORD_ITEM       |      1 |    535K|   1546K|00:00:00.73 |   43419 |

|* 14 |          INDEX RANGE SCAN          | ORD_ITEM_X01   |      1 |    535K|   1546K|00:00:00.20 |    5815 |

---------------------------------------------------------------------------------------------------------------

 

Predicate Information (identified by operation id):

---------------------------------------------------

 

   4 - access("O"."ITEM_ID"="P"."ITEM_ID")

  11 - access("O"."ITEM_ID"="I"."ITEM_ID")

  14 - access("O"."ORD_DT">='20120101' AND "O"."ORD_DT"<='20120131')


 그닥 극적인 효과는 없지만 조회하는 ORD_DT 범위가 넓어질수록 효과가 나타남


2012년 1월에 팔린 상품의 일별 매장별 판매수량과 판매금액 및 누적 판매금액 구하기


* 결과값


* 쿼리

SELECT O.ORD_DT "판매일시", 

            MAX(O.BRANCH_CD) "매장코드",

            SUM(I.ORD_ITEM_QTY) "판매수량",

            SUM(I.ORD_ITEM_QTY*P.PRICE) "판매금액",

            SUM(SUM(I.ORD_ITEM_QTY*P.PRICE)) OVER(ORDER BY O.ORD_DT 

RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "누적"

  FROM ORD O,

           ORD_ITEM I,

           (

                  SELECT P.ITEM_ID,

                              SUM(P.SALE_PRICE) PRICE

                    FROM UITEM_PRICE P

                  GROUP BY P.ITEM_ID

          ) P

WHERE I.ITEM_ID = P.ITEM_ID

    AND O.ORD_NO = I.ORD_NO

    AND I.ITEM_ID = P.ITEM_ID

    AND O.ORD_DT BETWEEN '20120101' AND '20120131'

    AND O.BRANCH_CD = :BRANCH_CD

GROUP BY O.ORD_DT, O.BRANCH_CD

ORDER BY O.ORD_DT;


* 실행계획

-------------------------------------------------------------------------------------------------------------

| Id  | Operation                        | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |

-------------------------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT                 |                |      1 |        |     31 |00:00:00.33 |   25978 |

|   1 |  WINDOW BUFFER                   |                |      1 |      3 |     31 |00:00:00.33 |   25978 |

|   2 |   SORT GROUP BY                  |                |      1 |      3 |     31 |00:00:00.33 |   25978 |

|*  3 |    HASH JOIN                     |                |      1 |    749 |   7683 |00:00:00.32 |   25978 |

|   4 |     VIEW                         |                |      1 |     92 |     92 |00:00:00.01 |       3 |

|   5 |      HASH GROUP BY               |                |      1 |     92 |     92 |00:00:00.01 |       3 |

|   6 |       TABLE ACCESS BY INDEX ROWID| UITEM_PRICE    |      1 |    130 |    130 |00:00:00.01 |       3 |

|   7 |        INDEX FULL SCAN           | UITEM_PRICE_PK |      1 |    130 |    130 |00:00:00.01 |       1 |

|   8 |     NESTED LOOPS                 |                |      1 |        |   7683 |00:00:00.32 |   25975 |

|   9 |      NESTED LOOPS                |                |      1 |    749 |   7683 |00:00:00.31 |   22873 |

|* 10 |       TABLE ACCESS BY INDEX ROWID| ORD            |      1 |    305 |   3134 |00:00:00.29 |   16579 |

|* 11 |        INDEX RANGE SCAN          | ORD_X01        |      1 |  62331 |    625K|00:00:00.08 |    2354 |

|* 12 |       INDEX RANGE SCAN           | ORD_ITEM_PK    |   3134 |      2 |   7683 |00:00:00.01 |    6294 |

|  13 |      TABLE ACCESS BY INDEX ROWID | ORD_ITEM       |   7683 |      2 |   7683 |00:00:00.01 |    3102 |

-------------------------------------------------------------------------------------------------------------

 

Predicate Information (identified by operation id):

---------------------------------------------------

 

   3 - access("I"."ITEM_ID"="P"."ITEM_ID")

  10 - filter("O"."BRANCH_CD"=:BRANCH_CD)

  11 - access("O"."ORD_DT">='20120101' AND "O"."ORD_DT"<='20120131')

  12 - access("O"."ORD_NO"="I"."ORD_NO")


※  Index에서 Table Random Access하는 과정(11→10)에서 비효율이 발생 했으므로 Index 조정이 필요