사용자지정 두번째 큰값찾기 그릅함수 처리순서 - ODCIAggregateInitialize (초기화) - ODCIAggregateIterate (ROW수만큼 반복) - ODCIAggregateMerge (병합) - ODCIAggregateTerminate (종료처리, 결과 리턴) -- OBJECT SPEC 생성 CREATE OR REPLACE TYPE XX_SecMax IS OBJECT ( MAX NUMBER -- 함수전역변수 ,SECMAX NUMBER -- 함수전역변수 ,STATIC FUNCTION ODCIAggregateInitialize( SCTX IN OUT XX_SecMax ) RETURN NUMBER ,MEMBER FUNCTION ODCIAggregateIterate( SELF IN OUT XX_SecMax ,VALUE IN NUMBER ) RETURN NUMBER ,MEMBER FUNCTION ODCIAggregateMerge( SELF IN OUT XX_SecMax ,CTX2 IN XX_SecMax ) RETURN NUMBER ,MEMBER FUNCTION ODCIAggregateTerminate( SELF IN OUT XX_SecMax ,RTN_VAL OUT NUMBER ,FLAGS IN NUMBER ) RETURN NUMBER ) ; -- OBJECT BODY 생성 CREATE OR REPLACE TYPE BODY XX_SecMax IS STATIC FUNCTION ODCIAggregateInitialize( SCTX IN OUT XX_SecMax ) RETURN NUMBER IS BEGIN -- 초기화 SCTX := XX_SecMax(NULL,NULL) ; -- 초기변수값(MAX,SECMAX) RETURN ODCIConst.Success ; END ; MEMBER FUNCTION ODCIAggregateIterate( SELF IN OUT XX_SecMax ,VALUE IN NUMBER ) RETURN NUMBER IS BEGIN -- 반복처리 IF SECMAX IS NULL THEN SELF.SECMAX := VALUE ; SELF.MAX := VALUE ; RETURN ODCIConst.Success ; END IF ; IF VALUE > SELF.MAX THEN SELF.SECMAX := SELF.MAX ; SELF.MAX := VALUE ; ELSE IF VALUE <> SELF.MAX AND VALUE > SELF.SECMAX THEN SELF.SECMAX := VALUE ; END IF ; END IF ; RETURN ODCIConst.Success ; END ; MEMBER FUNCTION ODCIAggregateMerge( SELF IN OUT XX_SecMax ,CTX2 IN XX_SecMax ) RETURN NUMBER IS BEGIN -- 병렬처리시 병합 IF CTX2.MAX > SELF.MAX THEN IF CTX2.SECMAX > SELF.SECMAX THEN SELF.SECMAX := CTX2.SECMAX ; ELSE SELF.SECMAX := SELF.MAX ; END IF; SELF.MAX := CTX2.MAX ; ELSE IF CTX2.MAX <> SELF.MAX AND CTX2.MAX > SELF.SECMAX THEN SELF.SECMAX := CTX2.MAX ; END IF ; END IF ; RETURN ODCIConst.Success ; END ; MEMBER FUNCTION ODCIAggregateTerminate( SELF IN OUT XX_SecMax ,RTN_VAL OUT NUMBER ,FLAGS IN NUMBER ) RETURN NUMBER IS BEGIN -- 결과 리턴 RTN_VAL := SELF.SECMAX ; RETURN ODCIConst.Success ; END ; END ; -- 함수 생성 CREATE OR REPLACE FUNCTION XX_FnSecMax ( IN_DATA IN NUMBER ) RETURN NUMBER AGGREGATE USING XX_SecMax ; -- 테스트 WITH TBL AS ( SELECT 1 CD, 3 DT FROM DUAL UNION ALL SELECT 1 CD, 5 DT FROM DUAL UNION ALL SELECT 1 CD, 4 DT FROM DUAL UNION ALL SELECT 2 CD, 5 DT FROM DUAL UNION ALL SELECT 3 CD, 4 DT FROM DUAL UNION ALL SELECT 3 CD, 7 DT FROM DUAL UNION ALL SELECT 3 CD, 9 DT FROM DUAL UNION ALL SELECT 3 CD, 9 DT FROM DUAL UNION ALL SELECT 3 CD, 2 DT FROM DUAL ) SELECT CD ,XX_FnSecMax(DT) SECMAX FROM TBL GROUP BY CD ; 결과 1 4 2 5 3 7 -- PARTITION BY SELECT CD ,XX_FnSecMax(DT) OVER (PARTITION BY CD) SECMAX FROM TBL ; 결과 1 4 1 4 1 4 2 5 3 7 3 7 3 7 3 7 3 7
-- WM_CONCAT기능 함수(오름차순) -- TABLE ARRAY 생성 CREATE OR REPLACE TYPE XX_VC2000TB IS TABLE OF VARCHAR2(2000) ; -- OBJECT SPEC 생성 CREATE OR REPLACE TYPE XX_WmConcat IS OBJECT ( RTN_STR VARCHAR(30000) ,VAL_TAB XX_VC2000TB ,STATIC FUNCTION ODCIAggregateInitialize( SCTX IN OUT XX_WmConcat ) RETURN NUMBER ,MEMBER FUNCTION ODCIAggregateIterate( SELF IN OUT XX_WmConcat ,VALUE IN VARCHAR2 ) RETURN NUMBER ,MEMBER FUNCTION ODCIAggregateMerge( SELF IN OUT XX_WmConcat ,CTX2 IN XX_WmConcat ) RETURN NUMBER ,MEMBER FUNCTION ODCIAggregateTerminate( SELF IN OUT XX_WmConcat ,RTN_VAL OUT VARCHAR2 ,FLAGS IN NUMBER ) RETURN NUMBER ) ; -- OBJECT BODY 생성 CREATE OR REPLACE TYPE BODY XX_WmConcat IS STATIC FUNCTION ODCIAggregateInitialize( SCTX IN OUT XX_WmConcat ) RETURN NUMBER IS BEGIN SCTX := XX_WmConcat('',XX_VC2000TB()) ; RETURN ODCIConst.Success ; END ; MEMBER FUNCTION ODCIAggregateIterate( SELF IN OUT XX_WmConcat ,VALUE IN VARCHAR2 ) RETURN NUMBER IS BEGIN SELF.VAL_TAB.EXTEND ; SELF.VAL_TAB(SELF.VAL_TAB.COUNT) := VALUE ; RETURN ODCIConst.Success ; END ; MEMBER FUNCTION ODCIAggregateMerge( SELF IN OUT XX_WmConcat ,CTX2 IN XX_WmConcat ) RETURN NUMBER IS BEGIN IF CTX2.VAL_TAB IS NOT NULL AND CTX2.VAL_TAB.COUNT > 0 THEN FOR IDX IN CTX2.VAL_TAB.FIRST .. CTX2.VAL_TAB.LAST LOOP SELF.VAL_TAB.EXTEND ; SELF.VAL_TAB(SELF.VAL_TAB.COUNT) := CTX2.VAL_TAB(IDX) ; END LOOP ; END IF ; RETURN ODCIConst.Success ; END ; MEMBER FUNCTION ODCIAggregateTerminate( SELF IN OUT XX_WmConcat ,RTN_VAL OUT VARCHAR2 ,FLAGS IN NUMBER ) RETURN NUMBER IS LV_TEMP VARCHAR2(2000) ; -- ASCENDING SORT PROCEDURE SP_SORT_QUICK( P_FIRST NUMBER ,P_LAST NUMBER ) IS LV_PIVOT VARCHAR2(2000) ; LV_RIGHT NUMBER ; LV_LEFT NUMBER ; BEGIN LV_PIVOT := SELF.VAL_TAB(FLOOR((P_FIRST + P_LAST) / 2)) ; LV_RIGHT := P_FIRST ; LV_LEFT := P_LAST ; LOOP WHILE SELF.VAL_TAB(LV_RIGHT) < LV_PIVOT -- ASC:< , DESC:> LOOP LV_RIGHT := LV_RIGHT + 1 ; END LOOP ; WHILE SELF.VAL_TAB(LV_LEFT) > LV_PIVOT -- ASC:> , DESC:< LOOP LV_LEFT := LV_LEFT - 1 ; END LOOP ; IF LV_RIGHT <= LV_LEFT THEN LV_TEMP := SELF.VAL_TAB(LV_RIGHT) ; SELF.VAL_TAB(LV_RIGHT) := SELF.VAL_TAB(LV_LEFT) ; SELF.VAL_TAB(LV_LEFT) := LV_TEMP ; LV_RIGHT := LV_RIGHT + 1 ; LV_LEFT := LV_LEFT - 1 ; END IF ; EXIT WHEN LV_RIGHT > LV_LEFT ; END LOOP ; IF P_FIRST < LV_LEFT THEN SP_SORT_QUICK(P_FIRST ,LV_LEFT) ; END IF ; IF P_LAST > LV_RIGHT THEN SP_SORT_QUICK(LV_RIGHT ,P_LAST ) ; END IF ; END ; BEGIN IF SELF.VAL_TAB IS NOT NULL AND SELF.VAL_TAB.COUNT > 1 THEN SP_SORT_QUICK(1 ,SELF.VAL_TAB.COUNT) ; END IF ; RTN_VAL := ''; IF SELF.VAL_TAB IS NOT NULL AND SELF.VAL_TAB.COUNT > 0 THEN FOR IDX IN SELF.VAL_TAB.FIRST .. SELF.VAL_TAB.LAST LOOP RTN_VAL := RTN_VAL || SELF.VAL_TAB(IDX) ; END LOOP ; END IF ; RETURN ODCIConst.Success ; END ; END ; -- 함수 생성 CREATE OR REPLACE FUNCTION XX_FnWmConcat ( IN_DATA IN VARCHAR2 ) RETURN VARCHAR2 AGGREGATE USING XX_WmConcat ; -- 테스트 WITH TBL AS ( SELECT 1 CD, 'B' DT FROM DUAL UNION ALL SELECT 1 CD, 'X' DT FROM DUAL UNION ALL SELECT 1 CD, 'C' DT FROM DUAL UNION ALL SELECT 2 CD, 'D' DT FROM DUAL UNION ALL SELECT 3 CD, 'D' DT FROM DUAL UNION ALL SELECT 3 CD, 'R' DT FROM DUAL UNION ALL SELECT 3 CD, 'A' DT FROM DUAL UNION ALL SELECT 3 CD, 'Z' DT FROM DUAL UNION ALL SELECT 3 CD, 'X' DT FROM DUAL ) SELECT CD ,SUBSTR(XX_FnWmConcat('/' || DT),2) CONC FROM TBL GROUP BY CD ; 결과 1 B/C/X 2 D 3 A/D/R/X/Z
-- SORT 다른방법 MEMBER FUNCTION ODCIAggregateTerminate( SELF IN OUT XX_WmConcat ,RTN_VAL OUT VARCHAR2 ,FLAGS IN NUMBER ) RETURN NUMBER IS TYPE TAB_TYPE IS TABLE OF VARCHAR2(2000) INDEX BY VARCHAR2(2100) ; SORT_TAB TAB_TYPE ; SPACE_CHR VARCHAR2(10) := ' ' ; SAVE_INDEX VARCHAR2(2100) ; BEGIN -- INDEX TABLE ARRAY 이용 RTN_VAL := ''; IF SELF.VAL_TAB IS NOT NULL AND SELF.VAL_TAB.COUNT > 0 THEN FOR IDX IN SELF.VAL_TAB.FIRST .. SELF.VAL_TAB.LAST LOOP SORT_TAB(SELF.VAL_TAB(IDX) || SPACE_CHR || IDX) := SELF.VAL_TAB(IDX) ; -- 중복제거 -- SORT_TAB(SELF.VAL_TAB(IDX)) := SELF.VAL_TAB(IDX) ; END LOOP ; -- 오름차순 SAVE_INDEX := SORT_TAB.FIRST ; WHILE SAVE_INDEX IS NOT NULL LOOP RTN_VAL := RTN_VAL || SORT_TAB(SAVE_INDEX) ; SAVE_INDEX := SORT_TAB.NEXT(SAVE_INDEX) ; END LOOP ; /* -- 내림차순 SAVE_INDEX := SORT_TAB.LAST ; WHILE SAVE_INDEX IS NOT NULL LOOP RTN_VAL := RTN_VAL || SORT_TAB(SAVE_INDEX) ; SAVE_INDEX := SORT_TAB.PRIOR(SAVE_INDEX) ; END LOOP ; */ END IF ; RETURN ODCIConst.Success ; END ;
-- XX_WmConcat 의 다른 방법 -- 2차원 INDEX TABLE ARRAY 이용 -- 중복제거,NULL제거,정렬 기능포함 MEMBER FUNCTION ODCIAggregateTerminate( SELF IN OUT XX_WmConcat ,RTN_VAL OUT VARCHAR2 ,FLAGS IN NUMBER ) RETURN NUMBER IS TYPE TAB_1_TYPE IS TABLE OF VARCHAR2(1) INDEX BY PLS_INTEGER ; TYPE TAB_2_TYPE IS TABLE OF TAB_1_TYPE INDEX BY VARCHAR2(2000) ; WORK_TAB TAB_2_TYPE ; V_KEY_1 NUMBER ; V_KEY_2 VARCHAR2(2000) ; V_UNIQ VARCHAR2(1); V_NULL VARCHAR2(1); V_ORDR VARCHAR2(1); BEGIN RTN_STR := ''; IF SELF.VAL_TAB IS NOT NULL AND SELF.VAL_TAB.COUNT > 0 THEN -- VALUE의 1번째 : A=중복포함 ,U=중복제거 -- 2번째 : A=NULL포함 ,N=NULL제거 -- 3번째 : A=오름차순 ,D=내림차순 -- 4번째 : 구분자 -- 5번째~: DATA V_UNIQ := UPPER(SUBSTR(SELF.VAL_TAB(1),1,1)) ; V_NULL := UPPER(SUBSTR(SELF.VAL_TAB(1),2,1)) ; V_ORDR := UPPER(SUBSTR(SELF.VAL_TAB(1),3,1)) ; FOR IDX IN SELF.VAL_TAB.FIRST .. SELF.VAL_TAB.LAST LOOP IF V_NULL 'N' OR SUBSTR(SELF.VAL_TAB(IDX),5) IS NOT NULL THEN IF V_UNIQ 'U' THEN WORK_TAB(SUBSTR(SELF.VAL_TAB(IDX),4))(IDX) := '' ; -- 전체 ELSE WORK_TAB(SUBSTR(SELF.VAL_TAB(IDX),4))(1) := '' ; -- 중복제거 END IF ; END IF ; END LOOP ; IF V_ORDR 'D' THEN -- 오름차순 V_KEY_2 := WORK_TAB.FIRST ; WHILE V_KEY_2 IS NOT NULL LOOP V_KEY_1 := WORK_TAB(V_KEY_2).FIRST ; WHILE V_KEY_1 IS NOT NULL LOOP RTN_STR := RTN_STR || V_KEY_2 ; V_KEY_1 := WORK_TAB(V_KEY_2).NEXT(V_KEY_1) ; END LOOP ; V_KEY_2 := WORK_TAB.NEXT(V_KEY_2) ; END LOOP ; ELSE -- 내림차순 V_KEY_2 := WORK_TAB.LAST ; WHILE V_KEY_2 IS NOT NULL LOOP V_KEY_1 := WORK_TAB(V_KEY_2).FIRST ; WHILE V_KEY_1 IS NOT NULL LOOP RTN_STR := RTN_STR || V_KEY_2 ; V_KEY_1 := WORK_TAB(V_KEY_2).NEXT(V_KEY_1) ; END LOOP ; V_KEY_2 := WORK_TAB.PRIOR(V_KEY_2) ; END LOOP ; END IF ; END IF ; RTN_VAL := RTN_STR ; RETURN ODCIConst.Success ; END ;
-- 테스트 WITH TBL AS ( SELECT 1 CD, 'B' DT FROM DUAL UNION ALL SELECT 1 CD, '' DT FROM DUAL UNION ALL SELECT 1 CD, 'B' DT FROM DUAL UNION ALL SELECT 1 CD, 'C' DT FROM DUAL UNION ALL SELECT 2 CD, 'D' DT FROM DUAL UNION ALL SELECT 2 CD, 'S' DT FROM DUAL UNION ALL SELECT 2 CD, 'D' DT FROM DUAL UNION ALL SELECT 3 CD, 'D' DT FROM DUAL UNION ALL SELECT 3 CD, '' DT FROM DUAL UNION ALL SELECT 3 CD, 'X' DT FROM DUAL UNION ALL SELECT 3 CD, 'A' DT FROM DUAL UNION ALL SELECT 3 CD, '' DT FROM DUAL UNION ALL SELECT 3 CD, 'Z' DT FROM DUAL UNION ALL SELECT 3 CD, 'X' DT FROM DUAL ) SELECT CD ,SUBSTR(XX_FnWmConcat('AAA,' || DT),2) DT_AAA -- 중복포함 NULL포함 오름차순 ,SUBSTR(XX_FnWmConcat('ANA,' || DT),2) DT_ANA -- 중복포함 NULL제거 오름차순 ,SUBSTR(XX_FnWmConcat('AAD,' || DT),2) DT_AAD -- 중복포함 NULL포함 내림차순 ,SUBSTR(XX_FnWmConcat('AND,' || DT),2) DT_AND -- 중복포함 NULL제거 내림차순 ,SUBSTR(XX_FnWmConcat('UAA/' || DT),2) DT_UAA -- 중복제거 NULL포함 오름차순 ,SUBSTR(XX_FnWmConcat('UNA/' || DT),2) DT_UNA -- 중복제거 NULL제거 오름차순 ,SUBSTR(XX_FnWmConcat('UAD/' || DT),2) DT_UAD -- 중복제거 NULL포함 내림차순 ,SUBSTR(XX_FnWmConcat('UND/' || DT),2) DT_UND -- 중복제거 NULL제거 내림차순 FROM TBL GROUP BY CD 결과 1 ,B,B,C B,B,C C,B,B, C,B,B /B/C B/C C/B/ C/B 2 D,D,S D,D,S S,D,D S,D,D D/S D/S S/D S/D 3 ,,A,D,X,X,Z A,D,X,X,Z Z,X,X,D,A,, Z,X,X,D,A /A/D/X/Z A/D/X/Z Z/X/D/A/ Z/X/D/A
위 오류 수정 ( '' 가 지워졌습니다.)
IF V_NULL !=
'N'
OR
SUBSTR(SELF.VAL_TAB(IDX),5)
IS
NOT
NULL
THEN
IF V_UNIQ !=
'U'
THEN
WORK_TAB(SUBSTR(SELF.VAL_TAB(IDX),4))(IDX) :=
''
;
-- 전체
ELSE
WORK_TAB(SUBSTR(SELF.VAL_TAB(IDX),4))(1) :=
''
;
-- 중복제거
END
IF ;
END
IF ;
END
LOOP ;
IF V_ORDR !=
'D'
THEN
-- 오름차순