멋진 토론이네요

From : http://database.sarang.net/?inc=read&aid=22145&criteria=oracle&subcrit=&id=&limit=20&keyword=cube&page=1

위 쿼리에서 그룹별로 소계를 내야하는데요..
작성자
서상서
작성일
2005-03-27 13:52:24
조회수
4,588

안녕하세요? 위의 쿼리에서 그룹별로 소계를 내야하는데,,,,좋은 방법이 있으면 알려주세요..

rollup말고 복제를이용해서 하는 방법이요.

---------쿼리는 다음과 같습니다.-------------------------

select a.org_id,f.maker_group,
f.maker_code,
DECODE('D','D',a.w_date,
'W',e.week_no,
'M',substr(a.w_date,1,6),
'Q',substr(a.w_date,1,4)||' '||TO_CHAR(TO_DATE(a.w_date,'YYYYMMDD'),'Q')) as w_date,
sum(a.input_qty) as input_qty
from (SELECT * FROM intf_modis_item_pol
WHERE W_DATE BETWEEN '20050301' AND '20050326'
AND ORG_ID = 1) x,
intf_modis_item_pol a,
mqas_date e,
mqas_maker f
where x.org_id = 1
and x.org_id = a.org_id
and x.w_date = a.w_date
and x.model_id = a.model_id
and x.p_factory = a.p_factory
and x.layer = a.layer
and x.line = a.line
and x.item_id = a.item_id
and x.top_bottom = a.top_bottom
and x.maker_id = a.maker_id
AND x.maker_lot_no = a.maker_lot_no
and x.w_date = e.d_date
and x.org_id = f.org_id
and x.maker_id = f.maker_id
and 'POL' = f.item_type
group by a.org_id,
DECODE('D','D',a.w_date,
'W',e.week_no,
'M',substr(a.w_date,1,6),
'Q',substr(a.w_date,1,4)||' '||TO_CHAR(TO_DATE(a.w_date,'YYYYMMDD'),'Q')),
f.maker_group,f.maker_code
---------------------------------------------------------------

위쿼리 결과는 다음과 같습니다.

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

org_id maker_group maker_code w_date input_qty

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

1 LGC aaaa 20050101 3334544

1 LGC BBBB 20050101 44444544

1 NITTO XXXXX 20050101 66666

1 NITTO HHHHH 20050101 777777

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

위 결과와 같이 업체코드의 업체그룹별로 일자별 혹은 주기,분기별로 투입량이 나옵니다.

문제는 그룹별로 소계가 나와야 합니다.

좋은 방법있으시면 알려주세요!!


그럼 수고하세요!!

이 글에 대한 댓글이 총 10건 있습니다.

rollup을 못 쓰시는 버전이신가요?

아니면 rollup이 원하는 결과와 다르게 나오기 때문인가요?

복제(?)를 사용하는 방법이라는 것이 어떤 방법이신지?


김흥수(protokhs)님이 2005-03-27 15:06:47에 작성한 댓글입니다.

sql이 좀 이상합니다만...

암튼 "복제를 이용한 소계내기"의 원리는 다음과 같습니다.

예를 들어,


그룹ID, 판매자, 판매수량

  A       홍길동     5

  A       이순신     10

  A       강감찬     1

  B       윤봉길     10

  B       김유신      5

  C       변학도     50


위와 같이 데이터가 있을때...

그룹별 중간중간에 소계를 넣으려면...

각 row를 두개씩 복제를 합니다. 그런다음 복제된것중 한개는 원래 값을 보존하고 나머지 한개는 그룹ID는 보존하고 판매자의 값을 식별할수 있는 값 (혹은 null)로 바꿔서 그룹ID, 판매자 로 grouping을 하면 됩니다.

전체합계까지 내고 싶다면 세개씩 복제해서 세개중 한개는 원래값 보존, 나머지 두개중 한개는 판매자의 값을 통일, 나머지 한개는 그룹ID 및 판매자 모두 값을 통일시켜서 그룹ID, 판매자로 grouping하면 됩니다.

예제) 그룹별로 소계내기

--테스트용 테이블 스크립트

create table tmp_sales(
 group_id varchar2(1),
 sales_emp varchar2(10),
 sales_qty number(7))
 
 --데이터 입력
 insert into tmp_sales
 select 'A', '홍길동', 5 from dual
 union all
select   'A',       '이순신',     10 from dual
union all
select   'A',       '강감찬',     1 from dual
union all
select  'B',       '윤봉길',     10 from dual
union all
select  'B',       '김유신',      5 from dual
union all
select  'C',       '변학도',     50 from dual

commit;


--복제를 이용한 소계
select group_id, nvl(sales_emp,'소계') sales_emp, sales_qty
from (
 select group_id,
        decode(no, 1, sales_emp) sales_emp,
        sum(sales_qty) sales_qty
 from tmp_sales,
         (select 1 no from dual
          union all
          select 2 no from dual) copy_t
 group by group_id,
        decode(no, 1, sales_emp)
 )--정렬이 흐트러 지지 않게 하기 위해 인라인뷰로 한번더 감쌌습니다.

결과)

  GROUP_ID SALES_EMP SALES_QTY

A 강감찬 1
A 이순신 10
A 홍길동 5
A 소계 16
B 김유신 5
B 윤봉길 10
B 소계 15
C 변학도 50
C 소계 50



지나가다님이 2005-03-30 10:46:49에 작성한 댓글입니다. Edit

아!

카티션 프로덕트를 이용한 반복적인 select(내부적)와 그의 집계를 말하는 것이군요!


알려주셔서 감사합니다....

한가지 의문점이 있는데요....

제 생각에는 rollup이 없던 시기에

소계내기는


select ...

union all

select ...

union  all

select ...


이런 형태의 분할 정복 방법을 주로 사용하였는데요...

혹시 이 복제를 사용한 방법과 분할 정복을 사용한 방법 중 어떠한 방법이 더 성능이 우수한지 테스트해보셨는지....

단지 직관적으로는 두 방법이 거의 성능상의 차이가 없거나 있어도 근소할 것 같은데요...(근거는 둘다 어차피 처리해야 하는 record의 건수가 논리적으로는 동일 하며 복제가 실제 디스크를 읽는 것은 더 적을 것으로 생각되지만 반면에 한번에 sort할 데이타의 양이 늘어나므로 논리적 읽기의 회피가 결국은 디스크상의 소트로 나타나서 물리적 읽기를 다시 증가 시킬 것 같거든요?)


김흥수(protokhs)님이 2005-03-31 09:04:26에 작성한 댓글입니다.

맞습니다.

님께서 말씀하신대로 일수도 있으나 제가 보는 관점은 조금 다릅니다.

"분할정복방법"이라는 용어가 공식용어인지는 모르겠으나...

이 방법을 사용할때 주의할점은 임의의 데이터가 두번이상 읽혀졌는가하는 점이라고 봅니다.

가령, 월별로 현재월의 수치와 이전월의 수치를 보고자 할때,

select sum(현재월), sum(이전월)

from (

        select sum(수치) 현재월, 0 이전월

         from 테스트

         where 년월 = '200503'

         union all

         select 0 현재월, sum(수치) 이전월

         from 테스트

          where 년월 = '200502'

        )

이렇게 했을경우, 문제가 없습니다. 단, 년월로 인덱스가 있고 년월조건이 아주 똑똑해서 해당년월에 걸리는 놈들만 정확하게 엑세스 해낸다는 조건하에서만 그렇습니다. 이경우, 임의의 데이터는 union all로 묶은 위아래 쿼리중 무조건 한곳에서만 엑세스 됩니다.

이런 경우라면 님께서 말씀하신 분할정복방법을 사용하셔도 좋습니다.

그러나 소계나, 합계등은 좀 다릅니다.

임의의 데이터는 소계에도 들어가야되고 합계에도 들어가야 됩니다.

두번 엑세스 된다는 말이죠.

이런 경우는 한번만 (디스크)엑세스해서 메모리상에서 복제하는것이 훨씬 이득입니다.

물론 메모리가 부족하거나 대량의 데이터를 소팅하기 위해 디스크 IO가 발생할수 있지만, 이런 디스크IO는 옵티마이저가 최적화된 실행계획으로 데이터를 엑세스 해내는 일련의 과정과는 개념이 다른 듯 싶습니다.

요지는 옵티마이저가 실행계획을 근거로 최초로 데이터를 엑세스 하는 데 드는 비용을 줄이는것이 더 중요한 점이라 할수 있겠습니다.


지나가다님이 2005-03-31 11:27:17에 작성한 댓글입니다. Edit

상세한 설명 감사합니다.

제가 호기심이 많은지라...

테스트 해보았습니다.

(아! 분할 정복법은 제 이야기가 아니라 일반적인 알고리즘 상의 용어입니다.-> 크고 복잡한 문제를 작고 단순한 문제로 만든 다음 다시 병합하는 모든 방법을 분할 정복법이라고 합니다.)

님께서 말씀하신대로 복제를 이용한 방법이 논리적 읽기의 수가 작았습니다.물론 이것은 예측 대로이고요...

약 30000건을 대상으로 한 테스트 결과 제 컴퓨터의 메모리가 충분하지 않은 관계로(1기가) 디스크에서 소트가 발생하였고 그 결과 복제의 방법이 미미하게나마 오히려 느려지더군요. 그렇지만 차이가 날 정도는 아니었습니다.

제가 임의로 만든 테이블 (all_objects 와 all_tab_columns 를 가지고 만들었습니다.)로 테스트 한 결과는

SQL> desc t_repl
 이름                                      널?      유형
 ----------------------------------------- -------- ----------------------------
 OWNER                                     NOT NULL VARCHAR2(30)
 OBJECT_TYPE                               NOT NULL VARCHAR2(18)
 OBJECT_NAME                               NOT NULL VARCHAR2(30)
 CNT                                                NUMBER


SQL> set timing on
SQL> set autotrace traceonly
SQL> select owner,no,object_type,object_name,cnt
  2  from
  3  (
  4  select
  5   a.owner,
  6   b.no,
  7   decode(b.no,1,a.object_type,2,a.object_type) object_type,
  8   decode(b.no,1,a.object_name) object_name,
  9   sum(a.cnt) cnt
 10  from t_Repl a,
 11    (select 1 no from dual union all select 2 from dual union all select 3 from dual) b
 12  group by
 13   a.owner,
 14   b.no,
 15   decode(b.no,1,a.object_type,2,a.object_type),
 16   decode(b.no,1,a.object_name)
 17  )
 18  /

40215 개의 행이 선택되었습니다.

경   과: 00:00:01.04

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=86764815 Card=152461
          98 Bytes=686078910)

   1    0   SORT (GROUP BY) (Cost=86764815 Card=15246198 Bytes=6860789
          10)

   2    1     MERGE JOIN (CARTESIAN) (Cost=637137 Card=976435392 Bytes
          =43939592640)

   3    2       VIEW (Cost=33 Card=24504 Bytes=73512)
   4    3         UNION-ALL
   5    4           TABLE ACCESS (FULL) OF 'DUAL' (Cost=11 Card=8168)
   6    4           TABLE ACCESS (FULL) OF 'DUAL' (Cost=11 Card=8168)
   7    4           TABLE ACCESS (FULL) OF 'DUAL' (Cost=11 Card=8168)
   8    2       BUFFER (SORT) (Cost=86764815 Card=39848 Bytes=1673616)
   9    8         TABLE ACCESS (FULL) OF 'T_REPL' (Cost=26 Card=39848
          Bytes=1673616)



Statistics
----------------------------------------------------------
          0  recursive calls
          6  db block gets
        257  consistent gets
       1003  physical reads
          0  redo size
    1441224  bytes sent via SQL*Net to client
      29983  bytes received via SQL*Net from client
       2682  SQL*Net roundtrips to/from client
          0  sorts (memory)
          2  sorts (disk)
      40215  rows processed

SQL> select
  2   owner,
  3   1 no,
  4   object_type,
  5   object_name,
  6   cnt
  7  from t_repl
  8  union all
  9  select
 10   owner,
 11   2 no,
 12   object_type,
 13   '타입소계',
 14   sum(cnt)
 15  from t_repl
 16  group by
 17   owner,
 18   object_type
 19  union all
 20  select
 21   owner,
 22   3 no,
 23   '소계',
 24   '',
 25   sum(cnt)
 26  from t_repl
 27  group by
 28   owner
 29  order by
 30   1,2,3
 31  /

40215 개의 행이 선택되었습니다.

경   과: 00:00:00.09

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=523 Card=40733 Bytes
          =1696274)

   1    0   SORT (ORDER BY) (Cost=335 Card=40733 Bytes=1696274)
   2    1     UNION-ALL
   3    2       TABLE ACCESS (FULL) OF 'T_REPL' (Cost=26 Card=39848 By
          tes=1673616)

   4    2       SORT (GROUP BY) (Cost=172 Card=841 Bytes=21866)
   5    4         TABLE ACCESS (FULL) OF 'T_REPL' (Cost=26 Card=39848
          Bytes=1036048)

   6    2       SORT (GROUP BY) (Cost=137 Card=44 Bytes=792)
   7    6         TABLE ACCESS (FULL) OF 'T_REPL' (Cost=26 Card=39848
          Bytes=717264)



Statistics
----------------------------------------------------------
          0  recursive calls
          6  db block gets
        744  consistent gets
        264  physical reads
          0  redo size
    1441631  bytes sent via SQL*Net to client
      29983  bytes received via SQL*Net from client
       2682  SQL*Net roundtrips to/from client
          2  sorts (memory)
          1  sorts (disk)
      40215  rows processed

SQL>



위의 결과를 보시면 아시겠구요...


제 생각은 이렇습니다.

* 복제나 union all을 사용한 소계 방식이나 크게 성능상의 차이가 없다.(그 이유는 위에서 제가 예측한 이유->1.논리적 대상의 크기가 차이가 없다.2. 대량의 집계의 경우 디스크 상의 정렬로 오히려 복제가 오버헤드를 발생시킨다.=>즉 테이블을 한번만 읽어서 번 시간을 소트하는데 다시 쓴다는...  3. 소량의 자료의 경우 어차피 차이가 없다.)


김흥수(protokhs)님이 2005-03-31 13:08:25에 작성한 댓글입니다.
이 댓글은 2005-03-31 13:11:31에 마지막으로 수정되었습니다.

약 1000만건이 들어 있는 현업 데이터에서 일자조건으로 10만건만 뽑아내서 테스트했습니다.


먼저 복제를 이용한 소계...

참고로 저는 복제를 위해 copy_t테이블을 만들어 둡니다.


select decode(no, 1, ma15vcod, 2, ma15vcod) ma15vcod,
    decode(no, 1, ma15icod, 2, '소계') ma15icod,
    sum(ma15tqty)
from (
 select ma15vcod, ma15icod, ma15tqty
 from ma_ma1500pf
 where ma15date between '2004-01-01' and sysdate and
    ma15seqn > 0
  ), copy_t
where no <= 3
group by decode(no, 1, ma15vcod, 2, ma15vcod),
    decode(no, 1, ma15icod, 2, '소계')


SELECT STATEMENT Optimizer Mode=CHOOSE                     
  SORT GROUP BY                     
    NESTED LOOPS                     
      TABLE ACCESS BY INDEX ROWID ACCOUNT.MA_MA1500PF                    
        INDEX RANGE SCAN ACCOUNT.PK_MA1500PF                    
      INDEX RANGE SCAN ACCOUNT.PK_COPY_T                    


여기서 저는 주관테이블이 먼저 엑세스 되고 복제용 테이블이 두번째로 엑세스 되면서 Lested Loops로 풀렸는데 님은 상수값으로 된 더미테이블을 만들어서 그런지 더미가 먼저 만들어져서 주관테이블이 두번째로 엑세스 되면서 Merge Join으로 풀렸더군요. 아마 여기서 오버헤드가 발생했을 가능성이 커 보입니다.


recursive calls  0
db block gets 0
consistent gets 198652
physical reads 0
redo entries 0
execute count 2
bytes sent via SQL*Net to client 144161
bytes received via SQL*Net from client 18152
SQL*Net roundtrips to/from dblink 0
sorts (memory) 3


다음은 union all로 여러번 읽은 방법입니다.

select ma15vcod, ma15icod, ma15tqty
from (
 select ma15vcod, 1 no, ma15icod, ma15tqty
 from ma_ma1500pf
 where ma15date between '2004-01-01' and sysdate and
    ma15seqn > 0  
 union all
 select ma15vcod, 2 no, '소계', sum(ma15tqty)
 from ma_ma1500pf
 where ma15date between '2004-01-01' and sysdate and
    ma15seqn > 0 
 group by ma15vcod  
 union all 
 select '총계', 3 no, '총계', sum(ma15tqty)
 from ma_ma1500pf
 where ma15date between '2004-01-01' and sysdate and
    ma15seqn > 0
 )
order by ma15vcod, no  


SELECT STATEMENT Optimizer Mode=CHOOSE                     
  SORT ORDER BY                     
    VIEW                     
      UNION-ALL                     
        TABLE ACCESS BY INDEX ROWID ACCOUNT.MA_MA1500PF                    
          INDEX RANGE SCAN ACCOUNT.PK_MA1500PF                    
        SORT GROUP BY                     
          TABLE ACCESS BY INDEX ROWID ACCOUNT.MA_MA1500PF                    
            INDEX RANGE SCAN ACCOUNT.PK_MA1500PF                    
        SORT AGGREGATE                     
          TABLE ACCESS BY INDEX ROWID ACCOUNT.MA_MA1500PF                    
            INDEX RANGE SCAN ACCOUNT.PK_MA1500PF                    

recursive calls  1610
db block gets 3440
consistent gets 301782
physical reads 3179
redo entries 171
execute count 159
bytes sent via SQL*Net to client 3609375
bytes received via SQL*Net from client 438106
SQL*Net roundtrips to/from dblink 0
sorts (memory) 3


참고로 H/W사양은 P-3 550 Dual, Memory 2G입니다.

오라클 버전은 8.0.5이고요

관리를 거의 안해서 튜닝이 거의 안되있는 상탭니다.

이런 환경을 감안하더라도...

수행시간은 약 7초 10초로 3초정도 차이로 복제방법이 빠릅니다만...

흥수님의 논리도 일리는 있어 보입니다.

하지만 제 상식이나 경험에서는 한번읽어서 메모리복제가 훨씬 좋아 보입니다.

모코아 컨설팅의 조광모님께서도 적극 권장하시더군요.

물론 그렇다고 다 맞는것은 아닙니다만.


암튼 간만에 저도 여러가지 부분을 보게 되어서 기뻤습니다.

감사하단 말씀 전하면서...화이팅 ^^


지나가다님이 2005-03-31 15:57:03에 작성한 댓글입니다.
이 댓글은 2005-03-31 18:14:52에 마지막으로 수정되었습니다. Edit

넷상이 아니라면 많은 토론을 해보고 싶군요...

테스트하시고 올려 주셔서 감사하구요

한가지 말씀드릴 사항이 있는데요..

두번째 질의의 union all 방법에 보면 recursive call 이 1610 이되어 있는데요

이 경우 제 경험상 필수적인 recursive call일 수도 있지만 parsing 과정의 부산물일 수도 있습니다.


그래서 저는 가능한 공정한 테스트를 원할 경우

1. 먼저 두 SQL을 한번씩 수행하여 질의 계획을 메모리에 상주시킨다.

2. 한번은 A를 먼저 실행하고 다른 한번은 B를 먼저 실행하여 각각의 physical 읽기를 확인 하여 수행 순서에 따라 큰 변화가 없으면 인정한다.


주로 이렇게 하거든요


물론 제가 올린 테스트도 그런 과정을 거친 것입니다.

저 역시 테스트때 사전에 SQL의 질의 계획을 메모리에 올리는 과정에서 recursive call이 (제 경우는 복제가 그랬습니다만...) 올라가는 것을 보고 순서를 바꾸어 계속 테스트한 다음 결과를 올린 것입니다만...


님께서도 한번 동일한 질의를 연속으로 해보시고 recursive call이 제거되는지 확인해보심이 어떠실런지....

그리고 복제의 경우 db block get 이 없다는 점은 이 질의가 이미 실행되어 해당 테이블의 데이타가 케시에 올라와 있는 상황이라는 점과

질의계획상에 코스트가 없는 것으로 보아 규칙기반인 것으로 보이는데요...


제 테스트에서 merge join이 나온것은 사실 제가 먼저 규칙기반으로 테스트 한 다음 별다른 차이가 없어서 analyze 한 후 다시 테스트 한 결과 그렇게 나왔으며 사실 그로인해 테스트가 규칙 기반의 경우보다 약 두배 빨라졌고 그 결과를 올린 것입니다.


그리구 전부 메모리 소트 되었군요...(음 메모리의 충분함이 좋군요..)


등등의 점등을 고려하여 한번 더 해보시고 결과를 알려주시면 ....

더 없이 고맙겠습니다.

억지같은 제 주장에 관심 갖고 대답을 올려주셔서 감사합니다.


많은 토론! 많은 실험!

개발자 화이팅!


아! 참고로 저는 두번 읽으면 느리다는 의견에는 웬지 반대라는 입장입니다....

 한가지 더...

제 테스트를 보시면 아시겠지만 저의 경우는 union all 이 두번 읽기가 아니라 사실은 3번 읽기입니다.

그렇게 한 이유는 만약 실제 데이타의 읽기 횟수가 늘어날 수록 union all 방법이 불리하다면 3번 읽기의 테스트는 아주 비관적으로 나와야 맞습니다..

그러나 결과를 보시면 3번 읽기임에도 큰 차이를 보이지 않습니다.

실무에서도 1단계의 소계만을 보기보다는 다단계의 소계를 보는 경우가 더 많습니다.


그리구 사실 이런 논의 자체가 이미 무의미할 수도 있습니다.

왜냐면 이미 rollup등이 있으니까요.

그리구 rollup이 없던 시대에도 대부분 소계처리는 서버에서 수행하지 않았었습니다.

이런 일(소계를 서버에서 처리하는 일)은 대부분 보고서 툴이 후져서 소계를 제대로 지원 못하는 경우 말고는 없었습니다.

소계를 서버에서 처리하는 것은 union all이던 복제를 이용한 방법이던 제 생각에는 모두 일종의 꽁수입니다.

그 이유는 client로 전달되어야 할 데이타의 양이 늘어나고.

두 방법 모두 논리적 처리 대상 레코드 수가 소계의 레벨만큼 증가하여 서버에 부담을 주며

클라이언트에서 처리할 경우 데이타가 정렬되어 있음을 전제로 하면 데이타 소계를 구하는 알고리즘은 데이타 양에 선형적인 속성을 가지고 있습니다.즉 그냥 소계없이 데이타를 구성하는 것과 비교하여 거의 비용이 들지를 않는다는 말이구요...

오라클 서버에서 제공하는 cube를 제외한 rollup 이나 grouping sets 같은 방법 역시 정렬의 부산물로 결과를 얻는 (그전에 client에서 하던것과 동일한 알고리즘) 방법을 사용하므로 과거버전의 꽁수들(union all이나 복제방법)보다는 좋은 성능을 얻을 수 있는 것이라 생각합니다.


단 카티션 프로덕트를 피해야하는 질의로 간주하지 않고 최대한 활용한 점.

제경우처럼 full-scan이 유도되는 빠른 디스크 읽기를 사용하는 경우가 아닌 행의 부분접근의 경우 고비용이되는 디스크 읽기를 줄인다는 점 등이 님께서 말씀하신 복제를 이용한 질의의 우수성이라 생각됩니다.


반대로 뒤집어서 말하면 단지 데이타에 대한 N번의 중복 접근이 좋지 않으며 이를 피하기 위해 카티션 프로덕트를 사용한다.

라는 착안점은 좀 더 깊이 생각을 해봐야 하는 점이라 생각합니다.(왜냐면 이것은 상황에 따라 달라지는 부분이지 알고리즘 측면에서 결코 본질적으로 우수한 방법은 아닙니다.)


한가지 더 말씀을 드리면 사실 저는 이 복제를 이용한 방법이 있다는 것을 이번에 알았구요

이점 감사드리구요

누구인지는 모르지만 이러한 방법(아이디어)을 최초에 생각해내신 분께 존경의 말씀을 드리고 싶습니다.

단 이러한 방법은 카티션 프로덕트의 사용의 연장선상에 있으며 특별히 복제를 사용한 방법이라는 이름이 붙었다는 것은 아마도 최초로 만드신 분이나 혹은 그 후에 이 방법을 주로 사용하셨던 분들이 이름을 붙이신 것이리라 생각합니다. 그러나 우리가 알아야 할 점은 우리가 무언가 특별한 방법에 특별한 의미를 두어 이를 기리고 의미를 부여하는 순간부터 사실은 이러한 방법이 화석화 된다는 점입니다.

뒤집어 말하면 도전 정신과 실험 정신으로 이 SQL을 처음 만드셨던 분의 정신을 배우는 것이 이 SQL을 활용하여 문제를 해결하고 이를 단순히 받아들이는 것보다 더 중요하다는 점을 말씀드리고 싶습니다.


! 항상 선배가 하는 말은 일단 틀렸다고 생각하고 뒤집어라 !

아마 이 정신이 아닐까 싶습니다.

음... 너무 장황하게 떠들면 난척해서 죄송합니다...

꾸뻑!

그럼 즐근하십시오.

언제 시간되시면 술이라도 한잔...

김흥수(protokhs)님이 2005-03-31 22:35:55에 작성한 댓글입니다.
이 댓글은 2005-04-01 02:30:23에 마지막으로 수정되었습니다.

음...

간만에 댓글을 여러번 달게 되네요..^^

테스트는 흥수님 말씀대로 여러번 번갈아 가며 해본 결과고요.

8.0.5버전이라 rollup이나 analytic function이 지원되지 않습니다. 흑흑..

그래서 copy_t라는 복제용 테이블을 만들어 놓고 쓰고 있고요.

복제를 활용한 방법은 굳이 "소계"가 아니더라도 활용범위가 많습니다.

Join횟수를 줄이거나 배타적관계로 조인해야할 경우, 무조건 outer join을 해야하지만 복제를 적절히 이용하면 이런 부분에 대한 문제를 어느정도 해결은 할수 있습니다.

근원적인 문제는 디비 디자인이고 데이터 모델링이지만 발등에 떨어진 급한불 끄는 데는 어느 정도 활용하는 편입니다.

analytic function으로 해결하던것을 전부는 아니지만 복제로 어느정도 해결은 하고 있습니다.

그리고 예전에 MS-SQL로 테스트해본 것이 생각나는데 내부적으로는 rollup도 메모리상의 데이터복제가 이루어 지고 있다는 점입니다.

이는 오라클에서도 마찬가질거라 추측되며...

그리고 옵티마이저 모드는 비용기반입니다.

SGA튜닝이 거의 안되있는 상황이고요.

10만건 이상짜리 table full scan하는 SQL이 중간중간에 많이 들어있어서..

이놈들 다고치려면 거의 프로그램80%을 손대야 하는 상황이라서 엄두는 못내고 있지만...

참고로 유지보수를 뒤늦게 맡게 되서 전임자들이 해논짓에 완전 독박쓰고 있습니다.


저는 두가지관점으로 봅니다.

말씀처럼 한번 읽어 복제하는데 양이 많고 메모리 부족해서 똑같이 디스크 소트가 발생한다면 결국 비용차이는 없는것 아닌가..하는 말도 맞다고 생각합니다.

그래서 메모리가 부족하니 증설합시다.. 해서 늘렸습니다.

두 방법다 메모리상에서 소트되고 아주 잘 돌아 갑니다.

그러나 데이터를 엑세스 하는 방법만 놓고 볼때는 효율과 비효율은 반드시 존재합니다.

옵티마이저가 똑똑하게, 아주 효율적으로 움직이게 해주는것은 메모리 증설이나 하드웨어가 아니라는 거죠.

가장 중요한 점은 어떻게 얼마나 효율적으로 목적 데이터를 엑세스해오느냐...일단 엑세스 해온 데이터를 디스크 상에서 소팅하든 메모리 상에서 소팅하든 그건 하드웨어 성능에 달린것이지 데이터의 분포에 따른 인덱스사용결정, 테이블 엑세스 우선순위 결정, 조인 방법결정 등과 같은 옵티마이저의 핵심기능은 아니라는 점입니다.

데이터 모델링에서 부터 출발한 디비디자인, 그리고 그에 맞게 잘 작성된 SQL문장이 가장 우선입니다.

나머지는 하드웨어 성능에 맡기는 것입니다.

각설하고...

짧은 지식에 여러가지 댓글다느라 머리가 아픕니다..^^

암튼 많이 배우고 갑니다.

행복하세요...


지나가다님이 2005-04-01 10:36:06에 작성한 댓글입니다. Edit

대화 즐거웠습니다.

김흥수(protokhs)님이 2005-04-01 14:33:14에 작성한 댓글입니다.

저 제가 집에와서 다시 자세히 님의 테스트를 보고 약간 이상한 점이 발견되어 말씀드립니다.


님의 테스트에 보면


TABLE ACCESS BY INDEX ROWID ACCOUNT.MA_MA1500PF                    
          INDEX RANGE SCAN ACCOUNT.PK_MA1500PF            


이 부분이 있는데요...

이 것으로 미루어보아 님의 테이블은 키가 ma15vcod,ma15icod

이외에도 적어도 ma15date 나 ma15seqn 이거 둘중의 하나 또는 둘다가 primary key에 포함되어 있다는 것을 추측할 수 있습니다.


그렇다면 님께서 하신 테스트의 두 SQL은 그 결과값이 다른 별개의 SQL이 됩니다.

그 이유는


님의 복제를 이용한 방법은  ma15vcod 별 ma15icod 별 계와

ma15icod 별 소계 및 총계로 출력되지만


union all을 이용한 방법은


제일 첫 SQL. 부분이

select ma15vcod, 1 no, ma15icod, ma15tqty
 from ma_ma1500pf
 where ma15date between '2004-01-01' and sysdate and
    ma15seqn > 0  

이렇게 되어 있으므로


select ma15vcod, 1 no, ma15icod, sum(ma15tqty )
 from ma_ma1500pf
 where ma15date between '2004-01-01' and sysdate and
    ma15seqn > 0  
group by ma15vcod,ma15icod


이렇게 해 주어야 동일한 결과가 나옵니다.


물론 저도 불리한 점을 감안해서 한 것이지만..

Order by 부분이 님의 것에는 빠져있으므로


union all의 거에도 inline-view로 감싸고 order by 한 부분을 빼주어야 공정한 테스트가 됩니다...

Posted by trust
,