database.sarang.net
UserID
Passwd
Database
DBMS
MySQL
PostgreSQL
Firebird
ㆍOracle
Informix
Sybase
MS-SQL
DB2
Cache
CUBRID
LDAP
ALTIBASE
Tibero
DB 문서들
스터디
Community
공지사항
자유게시판
구인|구직
DSN 갤러리
도움주신분들
Admin
운영게시판
최근게시물
Oracle Q&A 22263 게시물 읽기
No. 22263
쿼리문을 어떻게 만들어야 될지 모르겠어요....
작성자
김세영
작성일
2005-04-07 15:38
조회수
4,578

아래와 같은 테이블이 있을때

 

고유번호 포지션 포지션별순번
1 1 1
2 1 2
3 1 3
4 1 4
5 2 1
6 2 2
7 2 3
8 3 1
9 3 2
10 3 3
11 4 1
12 4 2
13 4 3
14 4 4


 

포지션, 포지션별 수량, 포지션순번의 max값에 해당하는 고유번호

포지션순번의 max값에 해당하는 고유번호

즉 아래와 같은 결과를 얻고자 합니다.

 

포지션 포지션별수량 Min(고유번호) Max(고유번호)
1 4 1 4
2 3 5 7
3 3 8 10
4 4 11 14


이러한 결과를 얻고 싶은데 어떻게 해야 될지 도저히 모르겠네요..

포지션과 포지션별 수량이야 Group by로 하면된다지만 min, max의 고유번호를

알아오질 못하겠습니다.

쿼리문이 아니라도 어떠한 함수를 참조하라...등...

도움의 손길을....부탁합니다..

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

질문에 답이 다 있는것 같은데요..;; (질문을 잘못하신건지.. 제가 이해를 못한건지..)
group by 포지션 하면 다 나올듯하네요
select 포지션, count(포지션), Min(고유번호), Max(고유번호)
from table
group by 포지션

='ㅅ'=님이 2005-04-07 16:09에 작성한 댓글입니다. Edit

'ㅅ'님의 답변 감사드리구요

고유번호가 순서대로 있어서 햇갈리신것 같은데여

고유번호는 순서가 있는게 아니라

말 그대로 고유번호인지라 'ㅅ'님의 쿼리의 결과를 원하는게

아니였습니다.

고유번호의 2대신 15가 있었다면

제가 원하는 결과가 나오지 않죠...

김세영님이 2005-04-07 16:35에 작성한 댓글입니다. Edit

to_number() 함수를 사용하시면 될듯...

道裕님이 2005-04-07 17:42에 작성한 댓글입니다. Edit

그럼 포지션별 순번의 min, max값으로 고유번호를 구하는건가요?

select a.포지션,
       a.cnt,
       (select b.고유번호 from table b where b.포지션 = a.포지션 and b.포지션별순번 = a.min_s) min_고유번호,
       (select c.고유번호 from table c where c.포지션 = a.포지션 and c.포지션별순번 = a.max_s) max_고유번호
from ( select 포지션, count(포지션) cnt, Min(포지션별순번) max_s, Max(포지션별순번) min_s
       from table
       group by 포지션 ) a
      
별로 좋아보이지는 않네요.. ;;  

='ㅅ'=님이 2005-04-07 18:07에 작성한 댓글입니다.
이 댓글은 2005-04-07 18:07에 마지막으로 수정되었습니다. Edit

='ㅅ'= 님 답변 감사합니다.

위의 테이블의 데이타와 똑같이 test_aa 테이블은 만든후에

(field => A - 고유번호, B - 포지션, C - 포지션별순번)

님께서 알려주신 쿼리문대로 아래와 같이 실행해 봤습니다.

 

 1 : select Q.B, Q.cnt,
 2 :      ( select W.A from test_aa W

 3 :        where W.B = Q.B and W.C = Q.min_s)  min_고유번호,
 4 :      ( select V.A from test_aa V

 5 :        where V.B = Q.B and V.C = Q.max_s)  max_고유번호
 6 : from (
 7 :     select

 8 :          B, count(B) as cnt, Min(C)  max_s, Max(C) min_s
 9 :     from test_aa
10 :     group by B
11 : ) Q

12 : Group by Q.B, Q.cnt, Q.min_s, Q.Max_s;

='ㅅ'= 님의 쿼리에는 예외가 발생하네요

 

12번 라인을 넣어주면 원하는 결과가 나오는군요..

='ㅅ'= 님 답변 감사합니다.

해결 되였습니다.

김세영님이 2005-04-07 20:00에 작성한 댓글입니다.
이 댓글은 2005-04-07 20:09에 마지막으로 수정되었습니다. Edit

 

음...

이건 어쨋든 님들의 SQL은 포지션 및 포지션별 순번에 명시적인 유니크 인덱스가 걸려있는 후보키일때만 에러 안 난다고 확신할 수 있습니다.

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

하나 더

김세영님께서 ='ㅅ'= 님의 SQL이 잘못되었다고 말씀하셔서 제가 한번 동일한 테스트를 해보니 역시 에러가 나더군요...

그런데...

제 생각은 이것은 ='ㅅ'= 님의 잘못이 아니라... 오라클의 논리적인 에러라고 보여집니다.

제 생각에 ='ㅅ'=님의 SQL은 오라클이 정하는 문법에 전혀 위배되지 않다고 봅니다.

그런데도 불구하고 불필요한 group by 절이 쓰여져야 한다는 것은 명백한 오라클 에러라고 생각합니다.

이 부분은 저도 약간 비슷한 경험이 있어서...궁금하여 ASK-TOM에도 올려보았는데... 대답이 없더군요..

이 오류는 아마도 스칼라서브쿼리를 원질의에 통합하는 과정에서 생긴 오류일 거라는 생각이 듭니다....

 

제가 그렇게 생각하는 이유는

논리적으로 보아 다를 것이 없는 아래의 두 SQL이 하나는 오라클의 에러가 되고 하나는 에러가 되지 않습니다...

단지 rownum을 넣기만 한 것입니다.

자세히 봐주십시오.

제 테스트 테이블은 all_objects에서 만들었습니다.

create table t_test_1 (id,owner,name) as select min(object_id),owner,object_name from all_objects group by owner,object_name

/

SQL> select
  2   q.owner,
  3   q.cnt,
  4   (select a1.id from t_test_1 a1 where a1.owner = q.owner and a1.name = q.min_name) min_id,
  5   (select a1.id from t_test_1 a1 where a1.owner = q.owner and a1.name = q.max_name) max_id
  6  from
  7  (
  8   Select
  9    owner,
 10    count(*) cnt,
 11    min(name) min_name,
 12    max(name) max_name
 13   From t_test_1 a
 14   group by
 15    a.owner
 16  )
 17    q
 18  /
  min(name) min_name,
      *
11행에 오류:
ORA-00979: GROUP BY 의 식이 없습니다
==> 전 분명히 그룹바이 했습니다. 맞죠?

 

이 SQL을 김세영님 처럼 group by 안하더라도

 

SQL> select
  2   q.owner,
  3   q.cnt,
  4   (select a1.id from t_test_1 a1 where a1.owner = q.owner and a1.name = q.min_name) min_id,
  5   (select a1.id from t_test_1 a1 where a1.owner = q.owner and a1.name = q.max_name) max_id
  6  from
  7  (
  8   Select
  9    owner,
 10    count(*) cnt,
 11    min(name) min_name,
 12    max(name) max_name,
 13    max(rownum) rn

 14   From t_test_1 a
 15   group by
 16    a.owner
 17  )
 18    q
 19  /

OWNER                                 CNT     MIN_ID     MAX_ID
------------------------------ ---------- ---------- ----------
BOOK                                  370      31007      30678
CALLUSER                               98      31293      31255
CTXSYS                                211      26600      26610
HR                                     34      29519      29520
KHS                                     1      31107      31107
MDSYS                                 201      26311      27673
ODM                                   395      28246      28320
ODM_MTR                                12      28542      28544
OE                                     79      29558      29561
OLAPSYS                               410      29213      29431
ORDPLUGINS                             15      24222      26008

OWNER                                 CNT     MIN_ID     MAX_ID
------------------------------ ---------- ---------- ----------
ORDSYS                                940      25026      25498
OUTLN                                   7        436       4202
PM                                      9      29619      29620
PUBLIC                              11542      17287      16583
QS                                     41      29912      29910
QS_ADM                                  6      29860      30107
QS_CBADM                               24      30072      30070
QS_CS                                  23      30096      30094
QS_ES                                  39      29931      29947
QS_OS                                  39      30039      30037
QS_WS                                  39      29994      29992

OWNER                                 CNT     MIN_ID     MAX_ID
------------------------------ ---------- ---------- ----------
RMAN                                  126      30194      30233
SCOTT                                   6      30141      30142
SH                                     40      29837      29663
SYS                                 13114      17286      16582
SYSTEM                                328       3868       3691
TESTUSER                                8      31068      31073
WKSYS                                 263      27726      27728
WMSYS                                 129       6110       6048
XDB                                   242      27498      27542

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

 

이제 에러가 안납니다.

바뀐 SQL의 밑줄친 부분이 추가된 것 뿐입니다.

 

제가 ronum에 대한 max를 추가한 것은 단지 inline-view 안의 SQL을 전체 SQL에 통합시키지 않고 먼저 평가되게 하기 위해서 한 것입니다.

이 꽁수는 토마스 아저씨 꽁수를 차용한 것이구요(제 아이디어는 아닙니다.)

이런 꽁수에 의해 성능이 달라지는 것은 있을 수 있는 일입니다.

그러나 결과가 달라지는 것은 있을 수 없는 일입니다.(함수평가 부수효과도 아니고...)

이유는 질의의 내부적 수행 순서를 바꾼 것일 뿐 동일한 결과이어야 하는 SQL이기 때문입니다. 더더군다나 에러라니!!!!!

말도 안되는 거죠...

이거 분명히 논리적 오류입니다.

 

아 하나 더 이 것이 오류라는 증거 하나더는 뭐냐면

위의 SQL에서 스칼라서브쿼리만 제거하면 에러 안납니다..

select
 q.owner,
 q.cnt
from
(
 Select
  owner,
  count(*) cnt,
  min(name) min_name,
  max(name) max_name
 From t_test_1 a
 group by
  a.owner
)
  q
order by q.owner

==> 이렇게하면 잘 나옵니다.

또한 간접적으로 알 수 있는 것은 스칼라서브쿼리가 항상 최종적으로 컬럼 리스트가 평가될때 평가되는 것은 아니라는 것이죠...

김흥수(protokhs)님이 2005-04-08 01:38에 작성한 댓글입니다.
이 댓글은 2005-04-08 02:08에 마지막으로 수정되었습니다.

이 SQL이 재미있는 주제라서 그런지 댓글을 많이 달게 되네요

 

사실 ='ㅅ'= 님이 제시하신 SQL은 분석함수를 사용하여도 동일한 결과를 얻을 수 있습니다.

그래서 성능이 궁금하여 성능 비교를 하였습니다.

참고 하시길...

SQL> set autotrace traceonly
SQL> set timing on
SQL> select
  2   q.owner,
  3   q.cnt,
  4   (select a1.id from t_test_1 a1 where a1.owner = q.owner and a1.name = q.min_name) min_id,
  5   (select a1.id from t_test_1 a1 where a1.owner = q.owner and a1.name = q.max_name) max_id
  6  from
  7  (
  8   Select
  9    owner,
 10    count(*) cnt,
 11    min(name) min_name,
 12    max(name) max_name,
 13    max(rownum) rn
 14   From t_test_1 a
 15   group by
 16    a.owner
 17  )
 18    q
 19  order by q.owner
 20  /

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

경   과: 00:00:00.01

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=127 Card=31 Bytes=19
          84)

   1    0   VIEW (Cost=127 Card=31 Bytes=1984)
   2    1     SORT (GROUP BY) (Cost=127 Card=31 Bytes=868)
   3    2       COUNT
   4    3         TABLE ACCESS (FULL) OF 'T_TEST_1' (Cost=17 Card=2879
          1 Bytes=806148)

 

 

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        232  consistent gets
          0  physical reads
          0  redo size
       1493  bytes sent via SQL*Net to client
        525  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         31  rows processed

SQL> select
  2   a.owner,
  3   count(*) cnt,
  4   min_id,
  5   max_id
  6  from
  7   (
  8    Select
  9     a.id,
 10     a.owner,
 11     a.name,
 12     first_value(id) over (partition by a.owner ) min_id,
 13     last_value(id) over (partition by a.owner ) max_id
 14    From t_test_1 a
 15    order by a.owner,a.name
 16   ) a
 17  group by
 18   a.owner,
 19   a.min_id,
 20   a.max_id
 21  order by
 22   a.owner
 23  /

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

경   과: 00:00:00.06

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=137 Card=31 Bytes=96
          1)

   1    0   SORT (GROUP BY) (Cost=137 Card=31 Bytes=961)
   2    1     VIEW (Cost=17 Card=28791 Bytes=892521)
   3    2       WINDOW (SORT) (Cost=17 Card=28791 Bytes=921312)
   4    3         TABLE ACCESS (FULL) OF 'T_TEST_1' (Cost=17 Card=2879
          1 Bytes=921312)

 

 

Statistics
----------------------------------------------------------
          0  recursive calls
          5  db block gets
        161  consistent gets
        440  physical reads
          0  redo size
       1493  bytes sent via SQL*Net to client
        525  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          1  sorts (memory)
          1  sorts (disk)
         31  rows processed

SQL> select
  2   a.owner,
  3   count(*) cnt,
  4   min_id,
  5   max_id
  6  from
  7   (
  8    Select
  9     a.id,
 10     a.owner,
 11     a.name,
 12     first_value(id) over (partition by a.owner ) min_id,
 13     last_value(id) over (partition by a.owner ) max_id
 14    From t_test_1 a
 15    order by a.owner,a.name
 16   ) a
 17  group by
 18   a.owner,
 19   a.min_id,
 20   a.max_id
 21  order by
 22   a.owner
 23  /

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

경   과: 00:00:00.05

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=137 Card=31 Bytes=96
          1)

   1    0   SORT (GROUP BY) (Cost=137 Card=31 Bytes=961)
   2    1     VIEW (Cost=17 Card=28791 Bytes=892521)
   3    2       WINDOW (SORT) (Cost=17 Card=28791 Bytes=921312)
   4    3         TABLE ACCESS (FULL) OF 'T_TEST_1' (Cost=17 Card=2879
          1 Bytes=921312)

 

 

Statistics
----------------------------------------------------------
          0  recursive calls
          5  db block gets
        161  consistent gets
        440  physical reads
          0  redo size
       1493  bytes sent via SQL*Net to client
        525  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          1  sorts (memory)
          1  sorts (disk)
         31  rows processed

SQL> select
  2   a.owner,
  3   count(*) cnt,
  4   min_id,
  5   max_id
  6  from
  7   (
  8    Select
  9     a.id,
 10     a.owner,
 11     a.name,
 12     first_value(id) over (partition by a.owner ) min_id,
 13     last_value(id) over (partition by a.owner ) max_id
 14    From t_test_1 a
 15    order by a.owner,a.name
 16   ) a
 17  group by
 18   a.owner,
 19   a.min_id,
 20   a.max_id
 21  order by
 22   a.owner
 23  /

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

경   과: 00:00:00.06

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=137 Card=31 Bytes=96
          1)

   1    0   SORT (GROUP BY) (Cost=137 Card=31 Bytes=961)
   2    1     VIEW (Cost=17 Card=28791 Bytes=892521)
   3    2       WINDOW (SORT) (Cost=17 Card=28791 Bytes=921312)
   4    3         TABLE ACCESS (FULL) OF 'T_TEST_1' (Cost=17 Card=2879
          1 Bytes=921312)

 

 

Statistics
----------------------------------------------------------
          0  recursive calls
          5  db block gets
        161  consistent gets
        440  physical reads
          0  redo size
       1493  bytes sent via SQL*Net to client
        525  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          1  sorts (memory)
          1  sorts (disk)
         31  rows processed

SQL>

 

 

참고로 말씀드리면 스칼라서브쿼리는 질의 계획이 안나옵니다...

결과는 분석함수가 더 느리군요...

이 SQL은 대량 소트 VS 인덱스를 통한 동일한 테이블에 대한 두번 접근의 대결입니다...==> 두번 접근이 이겼네요...

 

참고로 이 테이블에는 id 에 기본키가 있고... 인덱스만을 뒤지도록

owner,object_name,id 에 인덱스를 걸어두었습니다.

결과적으로 눈에는 안나타나지만 내부적으로는 이 인덱스를 사용하여 두번의 스칼라서브쿼리가 동작하였을 것으로 추측합니다.

일관된 읽기는 스칼라쪽이 좀 많지만 반대로 분석함수쪽은 대량의 정렬작업을 하기 위해 디스크를 사용하고 물리적 읽기가 발생하였군요...

연속적으로 수행하여도 물리적 읽기가 나타나는 것으로 보아 정렬에 의한 부작용인것으로 생각됩니다.

='ㅅ'=의 SQL이 더 좋은 결과를 나타냅니다.

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

답글 주신 ='ㅅ'=님, 김흥수님, 道裕님 감사합니다.

김세영님이 2005-04-08 10:04에 작성한 댓글입니다. Edit

테스트를 못해본 관계로 그런 오류가 생기는지 몰랐네요.. --;
두분 덕분에 많이 배웠습니다.. ^^

='ㅅ'=님이 2005-04-08 10:14에 작성한 댓글입니다. Edit

한번에 해결하기...

/*
create table test_table(
 unique_id number(4) not null,
 position number(4) not null,
 position_seq number(4)
 );
 
 insert into test_table
 select 1, 1, 1 from dual
 union all
 select 2, 1, 2  from dual
 union all
 select 3, 1, 3 from dual
 union all
 select 4, 1, 4 from dual
 union all
 select 5, 2, 1 from dual
 union all
 select 6, 2, 2 from dual
 union all
 select 7, 2, 3 from dual
 union all
 select 8, 3, 1 from dual
 union all
 select 9, 3, 2 from dual
 union all
 select 10, 3, 3 from dual
 union all
 select 11, 4, 1 from dual
 union all
 select 12, 4, 2 from dual
 union all
 select 13, 4, 3 from dual
 union all
 select 14, 4, 4 from dual;
 
 commit;

*/ 
select position, cnt,
    to_number(substr(max_val, instr(max_val, ',') + 1)) max_val,
    to_number(substr(min_val, instr(min_val, ',') + 1)) min_val
from (
 select position,
     count(*) cnt,
     max(to_char(position_seq) || ',' || to_char(unique_id)) max_val,
     min(to_char(position_seq) || ',' || to_char(unique_id)) min_val
 from test_table
 group by position
 )
 
POSITION CNT MAX_VAL MIN_VAL
1 4 4 1
2 3 7 5
3 3 10 8
4 4 14 11

지나가다님이 2005-04-08 14:12에 작성한 댓글입니다. Edit

좋은 아이디어 입니다.

단지 to_char 시에 포맷을 줘서 'FM00......' (posion_seq의 자릿수만큼의 포맷을 준다.) 이렇게 하면 완벽하겠네요....덧붙여서 포맷을 주면 instr 안써도 되니 함수 오버헤드가 약간이나마 줄겠구요

==>포맷을 주어야 하는 이유는

숫자의 경우는 2 < 12 이지만

문자의 경우는 '2' > '12' 이니까...

 

어쨋든 테스트는 안해보았지만 이 아이디어가 위의 SQL들 보다

성능도 더 좋으리라 예상됩니다.

김흥수(protokhs)님이 2005-04-10 23:59에 작성한 댓글입니다.
이 댓글은 2005-04-11 00:03에 마지막으로 수정되었습니다.
[Top]
No.
제목
작성자
작성일
조회
22268조인시 데이타 이상합니다... [1]
송만석
2005-04-07
978
22266일별,월별,년별 통계를 내고 싶은데 초짜라 자문을 구합니다. [3]
천둥이
2005-04-07
4188
22265Oracle 8.0.x 버전 윈도우 설치 하신분 도와주세요.^^ [1]
김동천
2005-04-07
1134
22263쿼리문을 어떻게 만들어야 될지 모르겠어요.... [12]
김세영
2005-04-07
4578
22262SQL*PLUS - 컬럼 포맷팅에 대해.... [1]
김홍식
2005-04-07
2434
22260[급]오라클펑션에서 레코드셋 반환 [3]
학생
2005-04-07
2069
22259개인발령데이타에서 원하는 정보 뽑아내기... [3]
쪼빡
2005-04-07
4047
Valid XHTML 1.0!
All about the DATABASE... Copyleft 1999-2024 DSN, All rights reserved.
작업시간: 0.017초, 이곳 서비스는
	PostgreSQL v16.2로 자료를 관리합니다