ㅜ.ㅜ..몇번째 질문글인지..죄송합니다. 0 19 1,893

by 고수로. [2016.12.14 16:34:46]


몇일째 끙끙 앓네요....function 을 동적 쿼리를 이용해서 테이블 형태와 같이 

select * function이름 이용하는작업을 하고있습니다.

리턴을 RETURN PIPELINED 형태로 하게되면 테이블과 같은 형태로 결과값을

조회할수 있는것 같다라는것까지는 알게 되었구요

그런데 생각처럼 진행이 잘 안되고 있습니다.

정말 죄송하지만 샘플 스크립트를 좀 배울수 있을까요...ㅜ.ㅜ

아래는 제가 테스트로 생성해본 스크립트입니다...뭔가 이상하게 문제가 있네요...

몇일전 문의드린 VARCHAR2 변수의 사이즈 문제는 아닌것 같습니다.

아래 스크립트 생성은 문제 없으나 SELECT * FROM TABLE (FN_TEST ('20161213', '20161213', 'C53038811394', 'all', 'all'));

함수 실행을 했을시 아래의 오류가 발생하고 있습니다.. (혹시 참고할만한 스크립트라도 좀

알수 있을까요......)

ORA-06502: PL/SQL: 수치 또는 값 오류
ORA-06512: "HIPLUS.FN_TEST", 줄 82에서

--===========샘플 스크립트 ======================

/* Formatted on 2016-12-13 오전 9:32:25 (QP5 v5.139.911.3011) */
CREATE OR REPLACE FUNCTION FN_TEST (PV_START_DT   IN VARCHAR2,
                                    PV_END_DT     IN VARCHAR2,
                                    PV_CSID       IN VARCHAR2,
                                    PV_CARDNO       IN VARCHAR2,
                                    PV_CARD_TYPE       IN VARCHAR2
                                    )
   RETURN TTAB_SPLIT
   PIPELINED
IS
   VTYP_SPLIT   TTYP_SPLIT;

   TYPE L_CUR_TYPE IS REF CURSOR;

    L_CUR        L_CUR_TYPE;
    L_REC        RECEIPT_PAY_MSG_20161213%ROWTYPE;
    L_SQL        VARCHAR2 (5000);
BEGIN
    L_SQL := '';
    L_SQL := L_SQL || ' SELECT ''PAYMENT'' TYPE, COUNT (CARDNO) CARDNO_MAX, SUM (FARERCV) SUM_PAY  ';
    L_SQL := L_SQL || ' FROM PAYMENT';
    L_SQL := L_SQL || ' WHERE WORKDATE BETWEEN ''' || PV_START_DT || ''' AND ''' || PV_END_DT || '''';
    L_SQL := L_SQL || ' AND CARDNO IN';
    L_SQL := L_SQL || ' (SELECT DISTINCT A.CARDNO AS CARDNO';
    L_SQL := L_SQL || ' FROM CARDLEDGER A,';
    L_SQL := L_SQL || ' REFUND B,';
    L_SQL := L_SQL || ' LOSTAPPLY C,';
    L_SQL := L_SQL || ' BLPRE D,';
    L_SQL := L_SQL || ' CARDRETURN E';
    L_SQL := L_SQL || ' WHERE     A.CARDNO = B.CARDNO(+)';
    L_SQL := L_SQL || ' AND A.CARDNO = C.CARDNO(+)';
    L_SQL := L_SQL || ' AND A.CARDNO = D.CARDNO(+)';
    L_SQL := L_SQL || ' AND A.CARDNO = E.CARDNO(+)';
    L_SQL := L_SQL || ' AND A.CSID = ''' || PV_CSID || '''';
    L_SQL := L_SQL || ' AND C.LOSTSTAT(+) = ''01''';
    L_SQL := L_SQL || ' AND (   (C.CARDNO IS NULL AND E.CARDNO IS NULL)';
    L_SQL := L_SQL || ' OR (C.REFUND_YN = ''N'')';
    L_SQL := L_SQL || ' OR (E.REFUND_YN = ''N'')';
    L_SQL := L_SQL || ' OR (C.REFUND_YN = ''Y''';
    L_SQL := L_SQL || ' AND B.WORKDATE >';
    L_SQL := L_SQL || ' TO_CHAR (ADD_MONTHS (SYSDATE, -12),';
    L_SQL := L_SQL || ' ''YYYYMMDD''))';
    L_SQL := L_SQL || ' OR (E.REFUND_YN = ''Y''';
    L_SQL := L_SQL || ' AND B.WORKDATE >';
    L_SQL := L_SQL || ' TO_CHAR (ADD_MONTHS (SYSDATE, -12),';
    L_SQL := L_SQL || ' ''YYYYMMDD''))))';

    IF PV_CARDNO != 'all' THEN
    L_SQL := L_SQL || ' AND CARDNO = ''' || PV_CARDNO || '''';
    END IF;
    IF PV_CARD_TYPE != 'all' THEN
    L_SQL := L_SQL || ' AND ''01'' = ''' || PV_CARD_TYPE || '''';
    END IF

    L_SQL := L_SQL || ' UNION ALL';

    L_SQL := L_SQL || ' SELECT  ''PAYMENT_TR'' TYPE,';
    L_SQL := L_SQL || ' COUNT(CARDNO) CARDNO_MAX,';
    L_SQL := L_SQL || ' SUM(FARERCV) SUM_PAY';
    L_SQL := L_SQL || ' FROM   PAYMENT_TR';
    L_SQL := L_SQL || ' WHERE  WORKDATE BETWEEN ''' || PV_START_DT || ''' AND ''' || PV_END_DT || ''' AND CARDNO IN (';
    L_SQL := L_SQL || ' SELECT';
    L_SQL := L_SQL || ' DISTINCT A.CARDNO AS CARDNO';
    L_SQL := L_SQL || ' FROM CARDLEDGER A, REFUND B, LOSTAPPLY C, BLPRE D, CARDRETURN E';
    L_SQL := L_SQL || ' WHERE   A.CARDNO=B.CARDNO(+)';
    L_SQL := L_SQL || ' AND A.CARDNO=C.CARDNO(+)';
    L_SQL := L_SQL || ' AND A.CARDNO=D.CARDNO(+)';
    L_SQL := L_SQL || ' AND A.CARDNO=E.CARDNO(+)';
    L_SQL := L_SQL || ' AND A.CSID = ''' || PV_CSID || '''';
    L_SQL := L_SQL || ' AND C.LOSTSTAT(+)=''01''';
    L_SQL := L_SQL || ' AND (';
    L_SQL := L_SQL || ' (C.CARDNO IS NULL AND E.CARDNO IS NULL)';
    L_SQL := L_SQL || ' OR';
    L_SQL := L_SQL || ' (C.REFUND_YN=''N'')';
    L_SQL := L_SQL || ' OR';
    L_SQL := L_SQL || ' (E.REFUND_YN=''N'')';
    L_SQL := L_SQL || ' OR';
    L_SQL := L_SQL || ' ( C.REFUND_YN =''Y''';
    L_SQL := L_SQL || ' AND B.WORKDATE > TO_CHAR(ADD_MONTHS(SYSDATE, -12), ''YYYYMMDD''))';
    L_SQL := L_SQL || ' OR';
    L_SQL := L_SQL || ' ( E.REFUND_YN =''Y''';
    L_SQL := L_SQL || ' AND B.WORKDATE > TO_CHAR(ADD_MONTHS(SYSDATE, -12), ''YYYYMMDD''))';
    L_SQL := L_SQL || ' )';
    L_SQL := L_SQL || ' )';
    
    IF PV_CARDNO != 'all' THEN
    L_SQL := L_SQL || ' AND CARDNO = ''' || PV_CARDNO || '''';
    END IF;
    IF PV_CARD_TYPE != 'all' THEN
    L_SQL := L_SQL || ' AND ''01'' = ''' || PV_CARD_TYPE || '''';
    END IF    
          
   OPEN L_CUR FOR L_SQL;

   LOOP
      FETCH L_CUR INTO L_REC;
      PIPE ROW (TTYP_SPLIT (L_REC.GROUPID, L_REC.GROUPNAME, L_REC.MSG));
   END LOOP;

   CLOSE L_CUR;
   
   RETURN;
END;

 

 

by 우리집아찌 [2016.12.14 16:51:53]

여러행 반환은 펑션으론 못해보고 package로 해결했었습니다

http://www.gurubee.net/lecture/1843


by jkson [2016.12.14 17:05:37]

입력 파라메터가 쿼리에 들어갈 때 뭔가 타입 불일치가 일어나는 것 같구요.

아마도 입력되는 날짜형 변경하면서 오류난 것 같네요.

전에 말씀 드렸듯이 굳이 동적쿼리로 안 해도 static 쿼리로도 가능합니다.

추후 관리할 때도 static 쿼리가 더 용이하실 거구요.

대충 변환해보았는데.. 일단 한번 해보시구요.

CREATE OR REPLACE FUNCTION FN_TEST(PV_START_DT IN VARCHAR2
                                 , PV_END_DT IN VARCHAR2
                                 , PV_CSID IN VARCHAR2
                                 , PV_CARDNO IN VARCHAR2
                                 , PV_CARD_TYPE IN VARCHAR2)
 RETURN TTAB_SPLIT
 PIPELINED
IS
 VTYP_SPLIT TTYP_SPLIT;
BEGIN
 FOR R IN (SELECT 'PAYMENT' TYPE, COUNT(CARDNO) CARDNO_MAX, SUM(FARERCV) SUM_PAY
             FROM PAYMENT
            WHERE WORKDATE BETWEEN PV_START_DT AND PV_END_DT
              AND CARDNO IN (SELECT DISTINCT A.CARDNO AS CARDNO
                               FROM CARDLEDGER A
                                  , REFUND B
                                  , LOSTAPPLY C
                                  , BLPRE D
                                  , CARDRETURN E
                              WHERE A.CARDNO = B.CARDNO(+)
                                AND A.CARDNO = C.CARDNO(+)
                                AND A.CARDNO = D.CARDNO(+)
                                AND A.CARDNO = E.CARDNO(+)
                                AND A.CSID = PV_CSID
                                AND C.LOSTSTAT(+) = '01'
                                AND ((C.CARDNO IS NULL
                                  AND E.CARDNO IS NULL)
                                  OR (C.REFUND_YN = 'N')
                                  OR (E.REFUND_YN = 'N')
                                  OR (C.REFUND_YN = 'Y'
                                  AND B.WORKDATE > TO_CHAR(ADD_MONTHS(SYSDATE, -12), 'YYYYMMDD'))
                                  OR (E.REFUND_YN = 'Y'
                                  AND B.WORKDATE > TO_CHAR(ADD_MONTHS(SYSDATE, -12), 'YYYYMMDD'))))
                AND CARDNO = DECODE(PV_CARDNO,'all',CARDNO,PV_CARDNO)--CARDNO가 null이 아니므로 이렇게 사용
--              AND ((PV_CARDNO != 'all'--CARDNO가 인덱스 컬럼인 것 같네요. 이렇게 하면 인덱스 활용 못함;
--                AND CARDNO = PV_CARDNO)
--                OR PV_CARDNO = 'all')
--              AND ((PV_CARD_TYPE != 'all'
--                AND '01' = PV_CARD_TYPE)
--                OR PV_CARD_TYPE = 'all')
           UNION ALL
           SELECT 'PAYMENT_TR' TYPE, COUNT(CARDNO) CARDNO_MAX, SUM(FARERCV) SUM_PAY
             FROM PAYMENT_TR
            WHERE WORKDATE BETWEEN PV_START_DT AND PV_END_DT
              AND CARDNO IN (SELECT DISTINCT A.CARDNO AS CARDNO
                               FROM CARDLEDGER A
                                  , REFUND B
                                  , LOSTAPPLY C
                                  , BLPRE D
                                  , CARDRETURN E
                              WHERE A.CARDNO = B.CARDNO(+)
                                AND A.CARDNO = C.CARDNO(+)
                                AND A.CARDNO = D.CARDNO(+)
                                AND A.CARDNO = E.CARDNO(+)
                                AND A.CSID = PV_CSID
                                AND C.LOSTSTAT(+) = '01'
                                AND ((C.CARDNO IS NULL
                                  AND E.CARDNO IS NULL)
                                  OR (C.REFUND_YN = 'N')
                                  OR (E.REFUND_YN = 'N')
                                  OR (C.REFUND_YN = 'Y'
                                  AND B.WORKDATE > TO_CHAR(ADD_MONTHS(SYSDATE, -12), 'YYYYMMDD'))
                                  OR (E.REFUND_YN = 'Y'
                                  AND B.WORKDATE > TO_CHAR(ADD_MONTHS(SYSDATE, -12), 'YYYYMMDD'))))
                AND CARDNO = DECODE(PV_CARDNO,'all',CARDNO,PV_CARDNO)
--              AND ((PV_CARDNO != 'all'
--                AND CARDNO = PV_CARDNO)
--                OR PV_CARDNO = 'all')
--              AND ((PV_CARD_TYPE != 'all'
--                AND '01' = PV_CARD_TYPE)
--                OR PV_CARD_TYPE = 'all')
                )
 LOOP
  PIPE ROW (TTYP_SPLIT(R.GROUPID, R.GROUPNAME, R.MSG));--커서에 없는 내용이네요. 적당히 수정
 END LOOP;
 
 RETURN;
END;

for문쪽 쿼리 뽑아다가 직접 파라메터 값 넣어도 오류 나는지 확인해보세요.

만약 오류 나면 안나게 수정하시면 됩니다.


by jkson [2016.12.16 15:41:19]

cardno가 index컬럼일 가능성이 높을 것 같아 조건 수정했습니다. 생각 없이 만들었네요.


by 마농 [2016.12.14 17:32:49]

글쎄요...
위 오류난 소스가 아닌 다른 소스를 올리신 듯 하네요.
위 오류(ORA-06502)는 버퍼 부족 오류 맞습니다.


1. END IF 에
   - 세미콜론 누락이 두군데 보이구요.
2. LOOP 구문에
   - EXIT 구문이 안보이네요.
   - FETCH 구문 바로 아래 EXIT 구문 필요합니다.
   - EXIT WHEN L_CUR%NOTFOUND;
3. PV_CARD_TYPE 에 대한 조건이
   - 테이블 컬럼과 비교하는게 아니라 상수값과 비교하네요?
   - 이게 어떤 의도로 사용된 것인지?
4. L_REC 는
   - L_REC    RECEIPT_PAY_MSG_20161213%ROWTYPE;
   - 3개 컬럼(GROUPID, GROUPNAME, MSG)으로만 이루어진 것 맞겠죠?


by 고수로. [2016.12.14 18:04:56]

jkson, 마농 선배님. 답변 항상 감사드립니다.

일단 jkson님 스크립트를 이용하여 적용했을시 문제는 없이 함수 생성은 완료 되었습니다.

동적쿼리를 정적 쿼리로 가능하다라고 가이드를 해주신 쿼리문이 이해가 잘안되네요.

IF PV_CARDNO != 'all' THEN 인경우는 조건문을 전혀 걸어주면안되는데요. 위 작성해주신 부분을 보면

AND ((PV_CARDNO != 'all' AND CARDNO = PV_CARDNO) OR PV_CARDNO = 'all')

위와같이 되어있습니다. 함수에서 all이라는 인자값을 받게될경우 조건문이 들어가면 안되는데요..

해당 스크립트 이해를 잘 못하겠습니다.ㅜ.ㅜ..

 

마농님  varchar2 변수에 쿼리 버퍼 부족오류가 맞는건가요.

그렇다면 varchar2보다 큰 타입에 넣어서 할수 있는 방법은 없는걸까요..

 


by 마농 [2016.12.14 18:10:16]

오류메세지 자체는 그게 맞지만..

위 소스는 그 오류와 무관해 보이네요.

다른 오류들도 여렇 보이고 있구요.


by jkson [2016.12.14 19:02:45]

 ((PV_CARDNO != 'all' AND CARDNO = PV_CARDNO) OR PV_CARDNO = 'all') 이 부분 설명하자면

pv_cardno가 all 값을 받으면 or 앞부분은 false가 되고 or 뒷부분은 true가 되므로 결론적으로 아무런 조건도 걸리지 않습니다. pv_cardno 가 all이 아닌 값을 받으면 or 뒷부분은 false가 되지만 앞부분은 true가 되고 cardno = pv_cardno 조건을 타게 됩니다. 이해가 되셨나요?


by 고수로. [2016.12.14 18:18:05]

...암울하네요......

마농님이 일단 말슴주신 세미콜론 에러 및 EXIT 문구는 해결했으나,,

버퍼 문제가 남아있네요...


by 마농 [2016.12.14 18:21:22]

글쎄요???

해당 쿼리는 2000 바이트도 안 되 보입니다.

5000 바이트 변수 선언 했으니 이문제는 아닙니다.

버퍼에 집착하지 말고 다른 오류 찾아보세요.

 

처음부터 너무 복잡한 쿼리를 적용하려고 하시네요.
우선 간단한 예제 쿼리로 정상동작하는지 부터 확인하셔야 할 듯.

그리고 컨셉을 바꾸는 것도 고려해 보세요.

지금 도전하시는 과제는 상당히 어려운 과제네요.

쉬운 방법을 찾아보세요.

꼭 멀티로우 펑션을 사용하고 다이나믹쿼리을 사용해야만 하는지?


by 고수로. [2016.12.14 18:42:08]

일단 제가 질문글에 올린 스크립트에 마농님께서 주신 오류내용 해결하고,

union all 아래 쿼리부분을 지우고서, union all 윗부분 쿼리를 아래에 붙여넣기 후,

실행했을시는 해당 오류가 발생을 안하네요.

그런데 순수 쿼리를 토드에서 실행했을시는 이상없이 결과값이 나오고는 있습니다.

후==33......><


by jkson [2016.12.14 19:21:53]

아래 윗부분이 다른 것은 테이블과 커서의 TYPE 컬럼 값이 다른데요. 제 생각에는 커서의 TYPE을 받아서 TTYP_SPLIT 으로 변환해줄 때 해당 컬럼의 사이즈가 작은 게 아닌가 싶습니다. TTYP_SPLIT 타입에서  'PAYMENT'까지는 되지만 'PAYMENT_TR'은 안 되는 7~9크기의 VARCHAR2 타입을 선언하고 있지 않나요?  TTYP_SPLIT 타입 선언문까지 전체 스크립트 올려보세요.


by 고수로. [2016.12.14 19:50:04]

jkson 님 .ㅜ.ㅜ.. 정말 정말 감사합니다.

말씀주신 부분이 문제였습니다.. 진짜 눈물나네요....이걸로 몇일을 눈치보며..

시간끄는지..혼자 끙끙 앓고있었는데....><;;

항상 친절한 답글 가르침 감사합니다.

마농님, 우리집아찌, jkson 선배님들 오늘도 또 하나 배워갑니다.

^^;.....


by jkson [2016.12.14 20:00:23]

다행이네요. 되도록 static 쿼리가 가능한 상황이면 다이나믹 쿼리는 사용하시지 마시구요. 만약 사용한다고 하더라도 변수는 바인드변수를 사용하셔야지 기술하신대로 사용하시면 쿼리 실행될 때마다 하드파싱이 일어나서 성능적으로 좋지 않습니다. 수고하셨어요.


by 고수로. [2016.12.14 21:11:35]

순조롭게 잘 진행이 되는줄 아랐는데..

UNION 이 많은 쿼리라...VARCHAR2 버퍼 한계를 넘는 순간 오류가 발생하네요..

ORA-06502: PL/SQL: 수치 또는 값 오류: 문자열 버퍼가 너무 작습니다
ORA-06512: "HIPLUS.FN_TEST", 줄 296에서

아.....갑자기 이시간에 다시..멍해지고 눈물이납니다....ㅜ.ㅜ


by jkson [2016.12.15 07:57:51]

해결 방법은 지난 댓글에 다 있습니다.


by 마농 [2016.12.15 08:35:13]

1. 쿼리문이 길어서 그런다면 쿼리변수 크기를 32k 로 늘리시면 되구요.
  Union 이 얼마나 많길래 그런가요?
  쿼리로 32k 채우기 쉽지 않을 걸요?
2. 레코드 타입 변수의 크기가 작은 거라면?
  해당 컬럼 사이즈 늘려주시면 되구요.


알 수 없는 오류들, 해결하기 어려운 오류들이 수없이 많습니다.
그러나, 해당 오류는 원인과 해결방법이 아주 명백합니다.
힘내세요.


by 마농 [2016.12.14 22:23:53]
멀티로우 펑션과 다이나믹 쿼리를 적용하기 이전에.
원본 쿼리 자체 튜닝 부터 하셔야 할 듯 합니다.
쿼리 자체를 획기적으로 줄일 수 있을 듯 합니다.

by 고수로. [2016.12.15 08:50:15]

늦은시간에도 답변을 달아주시네요.ㅜ.ㅜ.

감사합니다. !!! 좀 더 고민해보겠습니다. 할수 있을것 같습니다!!!!!


by 마농 [2016.12.15 09:13:20]

1. 쿼리 개선
  - IN 서브쿼리는 동일하고, 메인테이블만 바꿔가며 집계하는 모양새입니다.
  - IN 서브쿼리를 재사용할 수 있도록 해야 합니다.
  - WITH 구문을 활용하는 방법이 있구요.
  - 동적쿼리를 사용한다면 동일 쿼리를 테이블명만 바꿔가며 반복처리 하는 방법도 있습니다.
  - 그런데 IN 서브쿼리에 문제점이 있어 보입니다.


-- IN 서브쿼리 문제점 --
2. D 테이블은 불필요합니다.
  - D 가 아우터 조인되고 있으나 where, select 절 어디에도 사용이 되고 있지 않습니다.
  - 있으나 마나 한거구요
  - 혹시 이것 때문에 Distinct 를 사용하는 것일 수도 있습니다.
3. OR 조건절이 틀린게 아닐까 생각됩니다.
  - 검증이 필요할 듯.
4. pv_card_type 에대한 비교가
  - 상수값 '01' 과 비교하는게 이상하네요.

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입