반복문

루프문 – 반복문(LOOP, WHILE, FOR)은 커서를 사용하기 위한 것입니다.
– 반복문 등의 기능을 개발언어로 제공
– loop, while, for 문 제공 – while, for 조건 평가 기준 있음 (조건 충족 시 반복 실행)
–loop는 사용자에게 기준 자체를 설명하라는 메시지를 표시합니다.
– 대부분의 루프 문의 목적은 커서를 사용하는 것입니다.

1. 루프문
– 루프 문의 기본 구조 제공
– 무한 루프 기능
(사용 유형)
리본
반복문;
(조건 EXIT WHEN);–조건이 참이면 루프가 종료됩니다(while과 다름).
(반복되는 진술;)
루프 끝;

. ‘EXIT WHEN 조건’ : ‘조건’이 참이면 루프를 종료합니다.


사용예) 구구단의 6단을 구현하시오.
  DECLARE
    V_CNT NUMBER:=1; --1~9사시의 수 만들어줌
  BEGIN
    LOOP
      EXIT WHEN V_CNT>9; --9보다 커지면 반복문을 벗어남
      DBMS_OUTPUT.PUT_LINE('6 * '||V_CNT||' = '||6*V_CNT);
      V_CNT:=V_CNT+1;--1개 더해진 값으로 LOOP으로 다시 돌아감
    END LOOP;
  END;

** 커서
– SELECT 문의 결과 집합
1) 암시적 커서 – 이름 없음(생성, 열기, 패치, 닫기) -> 자동 처리(결과가 바로 출력되므로 접근 불가)
. CURSOR문이 없는 SELECT문의 결과 세트
. 이름이 없기 때문에 재사용할 수 없거나 개발자가 커서 내부에 액세스할 수 없습니다.
(결과 출력시 OPEN, 모든 결과 출력시 즉시 CLOSE)


   . 커서속성
   -----------------------------------------------------------------
      속성                     의    미--이름이 없으니까 sql을 씀
   -----------------------------------------------------------------
   SQL%ISOPEN         커서가 OPEN되었으면 참(true)반환 묵시적 커서는               
                      항상 false 임--항상 거짓임                                      
                                                                          
   SQL%NOTFOUND       커서 내에 읽어올(fetch)자료가 있으면 거짓(false)--없지 않으니 자료가 있음            
                      커서 내에 읽어올(fetch)자료가 없으면 참(true)반환--loop           
                                                                        
   SQL%FOUND          커서 내에 읽어올(fetch)자료가 있으면 참(true)반환       
                      커서 내에 읽어올(fetch)자료가 없으면 거짓(false)반환--while      
                                                                           
   SQL%ROWCOUNT       커서내의 자료의 수(행의 수) 반환--행 수 반환해줘                           
   -----------------------------------------------------------------
   --select문에 의해 나온 결과물을 뷰와 커서라고 한다

2) 명시적 커서
. 특정 이름으로 선언 섹션에 정의된 커서


 (사용형식)
   CURSOR 커서명((매개변수list)) IS
     SELECT 문;--선언부에 기술되어야함
    - '매개변수list' : 호출문에 전달해준 커서의 SELECT문에서 사용할 자료의 전달 통로
     타입명 변수명(, 타입명 변수명,...)형식으로 사용하며--타입명만 써야해(크기는 제외)
     타입명에 크기를 기술하지 않음
    - 커서 선언문은 DECLARE ~ BEGIN 사이(선언부)에 기술
    - 일반적 커서의 사용은 커서선언 -> OPEN -> FETCH -> CLOSE 순으로--패치는 반복문 안에 기술
    --선언제외하고 나머지는 BEGIN문 안에 있어야한다
      사용해야함
 사용예)부서번호를 전달받아 해당부서의 직원정보를 출력하는 커서를 작성
       Alias는 사원번호, 사원명, 입사일이며 입사일 순으로 출력하시오
  DECLARE
    V_EID HR.EMPLOYEES.EMPLOYEE_ID%TYPE;
    V_NAME HR.EMPLOYEES.EMP_NAME%TYPE;
    --참조형변수 : 테이블, 컬럼명을 알고 있는 것을 변수에 넣을 때(크기는 몰라)
    V_HDATE DATE;
    CURSOR CUR_EMP01(HR.DEPARTMENTS.DEPARTMENT_ID%TYPE P_DID) IS
       SELECT EMPLOYEE_ID,EMP_NAME,HIRE_DATE
         FROM HR.EMPLOYEES
        WHERE DEPARTMENT_ID=P_DID
        ORDER BY 3;--여기까지가 커서
    BEGIN
    
    END;

3) OPEN 문
. 생성된 커서를 사용하려면 OPEN해야 함 – 입력하려면 먼저 OPEN해야 함
. OPEN 문은 실행 영역(BEGIN 블록)에 작성됩니다. 실행 영역에 한 번만 작성하면 됩니다.


 (사용형식)
   OPEN 커서명((매개변수list))
     - '(매개변수list)':커서 선언문의 매개변수에 전달될 값 기술

  DECLARE
    V_EID HR.EMPLOYEES.EMPLOYEE_ID%TYPE;
    V_NAME HR.EMPLOYEES.EMP_NAME%TYPE;
    --참조형변수 : 테이블, 컬럼명을 알고 있는 것을 변수에 넣을 때(크기는 몰라)
    V_HDATE DATE;
    CURSOR CUR_EMP01(HR.DEPARTMENTS.DEPARTMENT_ID%TYPE P_DID) IS
       SELECT EMPLOYEE_ID,EMP_NAME,HIRE_DATE
         FROM HR.EMPLOYEES
        WHERE DEPARTMENT_ID=P_DID
        ORDER BY 3;--여기까지가 커서
  BEGIN
   OPEN CUR_EMP01(50);--P_DID에 전달됨(50이)
  END;

4) FETCH 문
. 커서 안의 데이터를 한 줄씩 읽어오는 명령어 – 읽기 개념이 없으므로 한 줄 전체를 가져옴
. 일반적으로 반복문으로 기술
(사용 유형)
변수 목록에서 커서 이름 가져오기;
– INTO 이후 커서를 형성하는 SELECT 문의 SELECT 절에 기술된 컬럼 값을 입력한다.
설명된 변수에 순차적으로 저장
– 커서를 구성하는 SELECT 문의 SELECT 절에 기술된 열의 개수, 순서, 종류
변수의 개수, 순서, 종류가 동일해야 함 – 선택한 문서를 가져오는 중이므로 개수가 동일해야 함

 5) CLOSE문
   . 사용이 종료된 커서는 반드시 CLOSE되어야함
   . CLOSE되지 않은 커서는 다시 OPEN될 수 없음
 (사용형식)
   CLOSE 커서명;

  DECLARE
    V_EID HR.EMPLOYEES.EMPLOYEE_ID%TYPE;
    V_NAME HR.EMPLOYEES.EMP_NAME%TYPE;
    --참조형변수 : 테이블, 컬럼명을 알고 있는 것을 변수에 넣을 때(크기는 몰라)
    V_HDATE DATE;
    CURSOR CUR_EMP01(P_DID HR.DEPARTMENTS.DEPARTMENT_ID%TYPE) IS--변수명이 앞으로 와야한다
    --부서번호를 전달받는다
       SELECT EMPLOYEE_ID,EMP_NAME,HIRE_DATE
         FROM HR.EMPLOYEES
        WHERE DEPARTMENT_ID=P_DID
        ORDER BY 3;--여기까지가 커서
  BEGIN
   OPEN CUR_EMP01(50);--P_DID에 전달됨(50이)
   LOOP
    FETCH CUR_EMP01 INTO V_EID,V_NAME,V_HDATE;--읽어올 내용
    EXIT WHEN CUR_EMP01%NOTFOUND;--데이터가 존재하지 않는다면 빠져나가라
    DBMS_OUTPUT.PUT_LINE('사원번호 : '||V_EID);
    DBMS_OUTPUT.PUT_LINE('사원명 : '||V_NAME);
    DBMS_OUTPUT.PUT_LINE('입사일 : '||V_HDATE);
    DBMS_OUTPUT.PUT_LINE('--------------------------------');
   END LOOP;
   DBMS_OUTPUT.PUT_LINE('사원 수 : '||CUR_EMP01%ROWCOUNT);--반복이 아니니까 반복문 밖에서 사원수 기술
   CLOSE CUR_EMP01;--반복문 종료
  END;
  
사용예)마일리지가 많은 5명의 2020년 상반기 구매현황을 조회하시오.
      Alias는 회원번호,회원명,마일리지,구매금액합계
      
 1)커서 : 마일리지가 많은 5명의 회원번호,회원명,마일리지를 조회 
   SELECT A.MEM_ID, A.MEM_NAME, A.MEM_MILEAGE
     FROM (SELECT MEM_ID,MEM_NAME,MEM_MILEAGE
             FROM MEMBER
            ORDER BY 3 DESC) A
    WHERE ROWNUM <= 5;       
      
  DECLARE 
    V_MID MEMBER.MEM_ID%TYPE;
    V_NAME MEMBER.MEM_NAME%TYPE;
    V_MILE NUMBER:=0;  --마일리지
    V_SUM NUMBER:=0;  --구매금액합계
    CURSOR CUR_CART01 IS--커서가 먼저 실행(밑에 나온 실행문 만드는 과정)
      SELECT A.MEM_ID, A.MEM_NAME, A.MEM_MILEAGE
        FROM (SELECT MEM_ID,MEM_NAME,MEM_MILEAGE
                FROM MEMBER
               ORDER BY 3 DESC) A
       WHERE ROWNUM <= 5;
  BEGIN
    OPEN CUR_CART01;--문 열어보자
    LOOP
      FETCH CUR_CART01 INTO V_MID,V_NAME,V_MILE;--커서에서 구한 내용이 여기서 들어감(첫줄 읽어봐)
      EXIT WHEN CUR_CART01%NOTFOUND;--더이상 없으면 빠져나가라
      --구매금액합계
      SELECT SUM(A.CART_QTY*B.PROD_PRICE) INTO V_SUM
        FROM CART A, PROD B
       WHERE A.CART_PROD=B.PROD_ID
         AND SUBSTR(A.CART_NO,1,6) BETWEEN '202001' AND '202006'
         AND A.CART_MEMBER=V_MID;--식별자 번호 같은 것끼리 뽑아라
         --SELECT절을 여기에 쓰려면 선언 변수와 관계도 밝히기
      --출력
      DBMS_OUTPUT.PUT_LINE(V_MID||' '||TRIM(V_NAME)||'  '||--TRIM 앞뒤 공백제거
                           TO_CHAR(V_MILE,'999,999')||TO_CHAR(V_SUM,'99,999,999'));
      DBMS_OUTPUT.PUT_LINE('--------------------------------------');
    END LOOP;
    CLOSE CUR_CART01;
  END;

-----------기본단계
  BEGIN
    OPEN
    LOOP
      FETCH
      
    END LOOP;
    CLOSE
  END;
--------------

2. WHILE 문
– 개발언어에서 WHILE문과 동일한 목적 및 기능 제공
(사용 유형)
WHILE 조건 루프
반복문;
루프 끝;
. ‘조건’: 조건이 참이면 반복

사용예)구구단의 6단을 출력하시오
  DECLARE
    V_CNT NUMBER:=0;
  BEGIN
    WHILE V_CNT<9 LOOP
      V_CNT:=V_CNT+1;
      DBMS_OUTPUT.PUT_LINE('6 * '||V_CNT||' = '||6*V_CNT);
    END LOOP;
  END;

사용예)회원의 직업을 매개변수로 입력 받아 회원이름과 마일리지를 출력하는
      커서를 작성하시오
--1.변수를 먼저 생각하기(최종 출력물)
  DECLARE
    V_NAME MEMBER.MEM_NAME%TYPE;
    V_MILE NUMBER:=0;
    CURSOR CUR_MEM01(P_JOB MEMBER.MEM_JOB%TYPE) IS--여기에 입력받은 직업을 넣는다
      SELECT MEM_NAME,MEM_MILEAGE
        FROM MEMBER
       WHERE MEM_JOB=P_JOB;
  BEGIN
    OPEN CUR_MEM01('학생');--여기에 입력한 값이 위에 전달된다
    FETCH CUR_MEM01 INTO V_NAME,V_MILE;
    --여기에만 하면 참이라 첫번째만 무한반복(WHILE문 밖에도 존재해야해)
    WHILE CUR_MEM01%FOUND LOOP--커서에 남은 자료가 있다면 실행하라
      DBMS_OUTPUT.PUT_LINE(V_NAME||TO_CHAR(V_MILE,'99,999'));
      FETCH CUR_MEM01 INTO V_NAME,V_MILE;
      --그래서 여기에도 해놔야 한다(다음거 읽어봐/WHILE문 안에도 있어야해)
    END LOOP;
  END;
  
 (커서 : 회원의 직업을 매개변수로 입력 받아 회원이름과 마일리지를 출력)
  SELECT MEM_NAME,MEM_MILEAGE
    FROM MEMBER
   WHERE MEM_JOB=P_JOB; 
   ----여기까지가 커서

3. FOR 문 – 실행할 횟수를 알고 있는 경우/커서와 잘 일치하는 경우
– 실행 횟수를 알고 있거나 중요할 때 사용
– FOR 문으로 커서 실행을 위해 OPEN, FETCH 및 CLOSE 문을 생략합니다.

(FOR 문의 일반적인 사용법)
FOR index IN (REVERSE) START VALUE…END VALUE LOOP–REVERSE는 그 반대입니다(시작 값과 종료 값을 변경할 필요가 없습니다).
진술을 반복하다;
루프 끝;
. 인덱스는 시스템에서 자동으로 선언하는 제어 변수입니다.
. 인덱스에 “시작 값”이 할당된 후 “종료 값”보다 작거나 같으면 반복됩니다.
. 인덱스 값은 자동으로 1 증가(감소: REVERSE)

(커서 FOR 문 형식)
FOR 레코드 이름 IN 커서|SELECT 문 LOOP–레코드 이름 = 인덱스 이름
진술을 반복하다;
루프 끝;
. 레코드 이름은 시스템에서 자동으로 선언한 제어 변수입니다.
. 커서의 첫 번째 행에서 마지막 행까지 순차적으로 레코드 이름에 위치 값 지정
. OPEN, FETCH 및 CLOSE 문을 생략하십시오. 사용하지 않더라도 자동으로 사용됩니다.
. 커서의 열을 참조하려면: ‘recordname.columnname of cursorSELECT 문의 열 이름’
.’cursor|SELECT 문’: 선언 섹션에서 커서를 선언하거나 IN 절에서 IN-LINE 하위 쿼리를 사용합니다.
형식으로 지정할 수 있습니다.

사용예)구구단의 6단을 FOR문을 이용하여 출력하시오
  DECLARE
  BEGIN
    FOR I IN 1..9 LOOP
      DBMS_OUTPUT.PUT_LINE('6 * '||I||' = '||6*I);
    END LOOP;
  END;

  DECLARE
  BEGIN
    FOR I IN 1..9 LOOP
      DBMS_OUTPUT.PUT_LINE('3 * '||I||' = '||3*I);
    END LOOP;
  END;

사용예)직업이 '주부'인 회원이름과 마일리지를 출력하는 커서를 작성하시오
--1.변수를 먼저 생각하기(최종 출력물)
  DECLARE
    CURSOR CUR_MEM01 IS--FOR문은 입력받지 않는다
      SELECT MEM_NAME,MEM_MILEAGE
        FROM MEMBER
       WHERE MEM_JOB='주부';
  BEGIN
    FOR REC IN CUR_MEM01 LOOP
      DBMS_OUTPUT.PUT_LINE(REC.MEM_NAME||TO_CHAR(REC.MEM_MILEAGE,'99,999'));
    END LOOP;
  END;

 (in-line SUBQUERY 사용)
  DECLARE
  BEGIN
    FOR REC IN (SELECT MEM_NAME,MEM_MILEAGE
                  FROM MEMBER
                 WHERE MEM_JOB='주부')
     LOOP
      DBMS_OUTPUT.PUT_LINE(REC.MEM_NAME||TO_CHAR(REC.MEM_MILEAGE,'99,999'));0
    END LOOP;
  END;