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 24116 게시물 읽기
No. 24116
connect by 사이클 검증 SQL을 검증해주시길...
작성자
김흥수(protokhs)
작성일
2005-09-16 14:35
조회수
3,682

먼저

기존의 제 생각은 'connect by의 사이클 검증은 SQL로는 불가능하다'라는 것이었습니다.

그 이유는 '사이클 검증을 위해서는 재귀적 검증이 필요하고 오라클에서 재귀적 검증은 오로지 connect by에 의해서만 가능하고 그렇다면 결국 검증을 위한 connect by가 loop가 되어 에러가 날 것이니 connect by의 사이클 검증은 SQL로는 불가능하다' 고 생각했었습니다.

그러나 장진주님의 사이클 검증 SQL을 보고는 새로운 충격 비슷한걸 받았습니다.

그리고는 속으로 '음... 내가 고정관념을 가지고 편견을 가지고 있었구나' 생각했습니다.

그리고는 나름대로 다시 사이클 검증 SQL의 일반적 해법을 찾으려고 시도했습니다.(첨언하면 장진주님이 제시하신 사이클 검증 SQL은 특수한 경우에만 가능한 것이었습니다.이 것에대해서는 제가 손사장님이 제시하신 문제에 댓글로 올려놓은 것이 있으니 참고하십시오.)

그리고는 다음과 같은 것을 찾았는데....

제가 생각할 때 스스로 납득이 되지 않아 이렇게 글을 올립니다.

 

테이블은 아래의 구조 입니다.

SQL> desc t_hir
이름 널? 유형
----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER
NAME VARCHAR2(100)
P_ID NUMBER

 

데이타는 다음과 같이 입력하고 두개의 사이클을 만들었습니다.

 

ID NAME P_ID
1 김흥수 9
2 오길자 4
3 백승기 1
4 김유빈 2
5 홍길동
6 전우치 5
7 전봉준 5
8 커 1
9 나 8

 

여기서 사이클은

김흥수->커->나->김흥수

오길자->김유빈->오길자

가 존재합니다.

 

다음과 같은 SQL을 날려보면

select
m,
max(p)
from(
Select
(
select
max(sys_connect_by_path(b.name,'-'))
from t_hir b
start with
b.p_id = a.ID
connect by
prior decode(a.id,b.ID,null,b.id) = b.P_ID
) p,
(
select
max(b.id)
from t_hir b
start with
b.p_id = a.ID
connect by
prior decode(a.id,b.ID,null,b.id) = b.P_ID
) m
From t_hir a
Where exists (
select
null
from t_hir b
where b.id = a.id
start with
b.p_id = a.ID
connect by
prior b.ID = b.P_ID
)
)
group by m

M MAX(P)
4 -오길자-김유빈
9 -커-나-김흥수

 

이런 결과가 나오며 사이클이 검증됩니다.(참고적으로 일단 사이클이 있다는 것은 레벨의미가 없다는 것이구요 '김흥수-커-나' 형태나 '커-나-김흥수','나-김흥수-커' 모두가 우선 순위나 상위 개념이 없는 동등한 결과입니다.)

특징은 여기에는 가장 단순한 계층구조 형태이며 다른 전제조건이 없다는 점입니다.

 

그런데 제가 납득이 가지 않는 부분은 위의 SQL에서 밑줄친 부분이 connect by 에서 loop가 발견되었습니다라는 에러가 나지 않는다는 점입니다.

아무리 생각해도 다른 조건이 없으므로 에러가 나야 정상일 것 같은데....

안납니다.

또한 위의 SQL은 in으로도 가능한데...

in을 쓸 경우는

 

Where a.ID in (
select
b.id
from t_hir b
start with
b.p_id = a.ID
connect by
prior b.ID = b.P_ID
)

 

이런 식이 됩니다.

 

마찬가지로 에러가 나야 정상인것 같은데.....

 

안납니다.

 

왜 에러가 안나는지에 대해 속 시원히 답을 주실 분을 기다리겠습니다.

^^

 

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

안녕하세요. 장석문입니다.

Filter Operation 특성이 아닌가 생각됩니다.

 

connect by절은 Self조인을 계속 반복하게 되고 이때 Loop를 체크하게 됩니다. 그런데 이번 SQL에서는 Loop가 발견되기 직전에 원하는 결과를 얻게 되고 SQL실행을 멈추게 (Filert 부분만)되므로 에러를 리턴하지 않게 되는것 입니다.

실행계획을 살표보시면 in이건 exists건 조건절의 a.ID부분 때문에  filter Operation 으로 실행되며 filter는 한건만 만족하면 되므로 Loop를 인지하지 못하는것 입니다.

 

김흥수님과 여러분들  덕분에 저도  Loop를 검증하는 좋은 방법을 배운것 같습니다.

 

즐거운 하루되세요.

 

장석문(math90)님이 2005-09-20 09:43에 작성한 댓글입니다.
이 댓글은 2005-09-20 09:52에 마지막으로 수정되었습니다.

재미있군요. 도움이 될 것 같습니다.

장진주(zozogirl)님이 2005-09-20 09:58에 작성한 댓글입니다.

장석문님께...

 

감사 거듭 감사드립니다.

 

in이나 exists가 일종의 stop 쿼리라는 사실을 깜빡했군요..

C에서 ||연산이 참이면 이후의 것은 검사 하지 않듯이...

 

하지만 역시...

이거는 일종의 꽁수인듯...

 

그리고 한가지 더

장석문님의 말씀을 듣고는 제가 한가지 더 테스트 한 것이 있습니다.

그리고는 좀 이상한 것을 발견 했지요...

 

오라클이 connect by의 루프 사이클을 오류로 검출하는 것이 님의 말씀 대로 검 출 직전 까지는 에러로 보고되지 않는 다는 점이구요...

또 한가지는 우리가 생각할 때 오류로 보고되어야 할 시점이 지나서 오류로 보고 되더군요...

 

이 방법은 오류 검출을 PL-SQL로 한 것입니다.

먼저 다음과 같은 테이블을 만들고요

SQL> desc t_hir
 이름                                      널?      유형
 ----------------------------------------- -------- ----------------------------
 ID                                                 NUMBER
 CONT                                               VARCHAR2(40)
 P_ID                                               NUMBER

 

데이타를 다음과 같이 입력합니다..

 

ID CONT P_ID
1 김흥수 4
2 오길자 1
3 백승기 2
4 홍길동 3
5 전우치 1
6 김삿갓 5

 

사이클이 생기는 것은

김흥수-오길자-백승기-홍길동-김흥수 입니다.

 

다음과 같은 함수를 만들고

 

create or replace function f_get_cycle
return varchar2
is
 ls_path    varchar2(4000) := '';
 cursor cur_a
 is
 select /*+first_rows*/
  sys_connect_by_path(cont,'-') r
 from t_hir
 connect by prior id = p_id
 start with id = 1;
begin
 for i in cur_a loop
  ls_path := i.r;
 end loop;
 return null;
exception when others then
 return ls_path;
end;
/

(일단 /*+FIRST_ROWS*/를 넣은 것은 혹시나 싶어서 입니다...

그리고 이 함수의 기본 아이디어는 에러가 나면 에러 전 시점의 패스를 출력하겠다는 것입니다.)

 

사실 이 함수를 통해 제가 기대한 것은..

-김흥수-오길자-백승기-홍길동

이런 결과 였습니다...

 

그런데 실제 수행 결과는

 

 

SQL> select f_get_cycle from dual;

F_GET_CYCLE
--------------------------------------------------------------------------------
-김흥수-오길자-백승기-홍길동-김흥수-오길자-백승기

 

이런 결과더군요..

 

음...

 

이건 또 왜 그럴까요?

 

(아! 그리구 제가 이런 테스트를 한 이유는요... 장석문님의 말씀대로 filter의 효과라고 보더라도...약간 납득이 안 가는 것이 filter로 걸러지는 바로 그 순간이 또한 loop가 검증되는 순간이었기 때문입니다.그렇다면 오라클이 loop임이 확인 되는 순간-그래서 에러가 발생하는 순간-이 논리적으로 볼때와 실제가 미묘한 차이가 있는 것 아닌가... 하고 생각하고 테스트 해보니 이런 결과가 나타났습니다.그리고 제가 이방법이 일종의 꽁수라고 생각하는 것도 이러한 이유때문입니다.왜냐하면 loop로 인한 에러 발견이 항상 지연된다는 보장이 없으므로...)
김흥수(protokhs)님이 2005-09-21 02:40에 작성한 댓글입니다.
이 댓글은 2005-09-21 05:08에 마지막으로 수정되었습니다.
[Top]
No.
제목
작성자
작성일
조회
24119오라클 + PHP + APACHE에서 아파치의 환경설정
초보자
2005-09-17
1496
24118[알바급구]게시판 코딩해줄 개발자 아르바이트를 구합니다.(ASP,ORACLE)
최규운
2005-09-17
1602
24117아우터조인 관련, 오류좀 찾아주세요 [6]
이혁
2005-09-16
8455
24116connect by 사이클 검증 SQL을 검증해주시길... [3]
김흥수
2005-09-16
3682
24115이런 방식의 설계에서 키 중복을 막을 수 있는 방법이 있는지요? [2]
이승배
2005-09-16
2152
24114OCI has fetched truncated data 워닝에러 .. [1]
초보돌진
2005-09-16
4442
24113[질문] 최대값 [1]
강명원
2005-09-15
2142
Valid XHTML 1.0!
All about the DATABASE... Copyleft 1999-2024 DSN, All rights reserved.
작업시간: 0.017초, 이곳 서비스는
	PostgreSQL v16.2로 자료를 관리합니다