경제학도의 개발공간

[20180515] PL SQL 본문

BackEnd/Oracle

[20180515] PL SQL

reallifeliver 2018. 5. 15. 11:50
반응형

기본설정







기본 문법


1
2
3
4
5
6
7
8
9
10
DECLARE -- 변수를 선언한다. 
  v_emprow emp%ROWTYPE; -- emp의 row타입을 가지고 있는 변수를 생성 >> java의 객체개념과 비슷 
BEGIN
  select *
    into v_emprow -- empno , ename , ,..... deptno >> select한 결과를 변수에 저장 
  from emp
  where empno=7788;
  
  DBMS_OUTPUT.PUT_LINE(v_emprow.empno || '-' || v_emprow.ename || '-' || v_emprow.deptno);
END; -- 끝
cs



제어문


IF문


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
DECLARE
  vempno emp.empno%TYPE;
  vename emp.ename%TYPE;
  vdeptno emp.deptno%TYPE;
  vname varchar2(20) := null; --기본값 설정
BEGIN
  select empno , ename , deptno
    into vempno , vename , vdeptno
  from emp
  where empno=7788;
  --제어문 if(조건문){실행문}
  IF(vdeptno = 10) THEN vname := 'ACC'-- if(vdeptno==10) { vname = "ACC"}
    ELSIF(vdeptno=20) THEN vname := 'IT';
    ELSIF(vdeptno=30) THEN vname := 'SALES';
  END IF;
  DBMS_OUTPUT.PUT_LINE('당신의 직종은 : ' || vname);
END;
cs




CASE문


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
DECLARE
  vempno emp.empno%TYPE;
  vename emp.ename%TYPE;
  vdeptno emp.deptno%TYPE;
  v_name varchar2(20);
BEGIN
     select empno, ename , deptno
        into vempno, vename , vdeptno
     from emp
     where empno=7788;
     
--    v_name := CASE vdeptno >> 어떠한 조건이 딱 맞아 떨어질 때
--                WHEN 10  THEN 'AA'
--                WHEN 20  THEN 'BB'
--                WHEN 30  THEN 'CC'
--                WHEN 40  THEN 'DD'
--              END;
 
    v_name := CASE -- 조건이 유동적일 
                WHEN vdeptno=10  THEN 'AA'
                WHEN vdeptno in(20,30)  THEN 'BB'
                WHEN vdeptno=40  THEN 'CC'
                ELSE 'NOT'
              END;
    DBMS_OUTPUT.PUT_LINE('당신의 부서명:' || v_name);            
END;
 
cs




LOOP



1
2
3
4
5
6
7
8
9
10
DECLARE
  n number :=0;
BEGIN
  LOOP
    DBMS_OUTPUT.PUT_LINE('n value : ' || n);
    n := n + 1;
    EXIT WHEN n > 5;
  END LOOP;
END;
 
cs


위와 같은 방법 이외에도 다음과 같이 활용할 수 있다 .


while을 활용한 LOOP문


1
2
3
4
5
6
7
8
9
DECLARE
  num number := 0;
BEGIN
  WHILE(num < 6-- EXIT대신 써줄 수 있다.
  LOOP
    DBMS_OUTPUT.PUT_LINE('num 값 : ' || num);
    num := num +1;
  END LOOP;
END;
cs



LOOP-FOR



1
2
3
4
5
BEGIN
  FOR i IN 0..10 LOOP
    DBMS_OUTPUT.PUT_LINE(i);
  END LOOP;
END;
cs


FOR문 활용해 1~100총 합 구하기


1
2
3
4
5
6
7
8
DECLARE
total number :=0;
BEGIN
  FOR i IN 1..100 LOOP
    total := total + i;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('1~100 총합 : ' || total);
END;
cs



추가: ORACLE 11버젼 이후로는 CONTINUE라는 문법이 추가되었다.


1
2
3
4
5
6
7
8
9
10
11
12
 
DECLARE
  total number := 0;
BEGIN
  FOR i IN 1..100 LOOP
    DBMS_OUTPUT.PUT_LINE('변수 : ' || i);
    CONTINUE WHEN i > 5--skip
    total := total + i; -- 1 , 2 , 3 , 4, 5
  END LOOP;
    DBMS_OUTPUT.PUT_LINE('합계 : ' || total);
END;
 
cs




PL SQL활용


EXCEPTION의 활용


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
DECLARE
  v_empno emp.empno%TYPE;
  v_name  emp.ename%TYPE := UPPER('&name');
  v_sal   emp.sal%TYPE;
  v_job   emp.job%TYPE;
BEGIN
  select empno , job ,sal
    into v_empno, v_job , v_sal
  from emp
  where ename = v_name;
  
  IF v_job IN('MANAGER','ANALYST') THEN
     v_sal := v_sal * 1.5;
  ELSE
     v_sal := v_sal * 1.2;
  END IF;
  
  update emp
  set sal = v_sal
  where empno=v_empno;
  
  DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT || '개의 행이 갱신 되었습니다');
  
  --예외처리
  EXCEPTION
    WHEN NO_DATA_FOUND THEN --오라클이 제공하는 상수 변수 
       DBMS_OUTPUT.PUT_LINE(v_name || '는 자료가 없습니다');
    WHEN TOO_MANY_ROWS THEN
       DBMS_OUTPUT.PUT_LINE(v_name || '는 동명 이인입니다');
    WHEN OTHERS THEN
       DBMS_OUTPUT.PUT_LINE('기타 에러가 발생했습니다');
END;
/*
질의는 하나의 행만 RETURN 해야 합니다. PL/SQL 블록 내의 SELECT 문장은 다음 규칙을
적용하는 Embedded SQL 의 ANSI 범주에 속합니다. 질의의 결과는 하나의 행만을 RETURN 해
야  하고  하나의  행  이상  또는  행이  없는  것은  에러를  생성합니다.  PL/SQL 은
NO_DATA_FOUND 와 TOO_MANY_ROWS 를 예외로 블록의 예외 섹션에서 추적할 수 있는 표준 예
외를 조성하여 처리 합니다.
*/
cs



지금까지는 기본문법이었고 이제 좀 더 고급 문법을 소개한다.


고급자원인 만큼 활용에 따른 리스크 또한 크기 때문에 주의해서 활용해야 한다 .



위의 실습은 한 건의 데이터 (하나의 row)만을 처리할 수 있는 방법이었다. 이제 다수의 row를 한번에 처리할 수 있는 CURSOR의 개념을 알아본다.


CURSOR




1. 행단위로  데이터를 처리하는 방법을 제공


2. 여러건의 데이터를 처리하는 방법을 제공


 

기본문법


1
2
3
4
5
6
7
8
9
10
DECLARE
          CURSOR 커서이름 IS 문자(커서가 실행할 쿼리)
   BEGIN
         OPEN 커서이름 (커서가 가지고 있는 쿼리를 실행)
             
         FETCH 커서이름 INTO 변수명들...
          --커서로 부터 데이터를 읽어서 원하는 변수에 저장
         CLOSE 커서이름 (커서닫기) 
   END
 
cs





SQL CURSOR 의 속성을 사용하여 SQL 문장의 결과를 테스트할 수 있다.


[종 류 설 명]


  • SQL%ROWCOUNT 가장 최근의 SQL 문장에 의해 영향을 받은 행의 수

  • SQL%FOUND 가장 최근의 SQL 문장이 하나 또는 그 이상의 행에 영향을 미친다면 TRUE 로 평가한다.

  • SQL%NOTFOUND 가장 최근의 SQL 문장이 어떤 행에도 영향을 미치지 않았다면 TRUE 로  평가한다.

  • SQL%ISOPEN PL/SQL 이 실행된 후에 즉시 암시적 커서를 닫기 때문에 항상 FALSE 로 평가된다.

활용예시는 다음과 같다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
DECLARE
  vempno emp.empno%TYPE;
  vename emp.ename%TYPE;
  vsal   emp.sal%TYPE;
  CURSOR c1  IS select empno,ename,sal from emp where deptno=30;
BEGIN
    OPEN c1; --커서가 가지고 있는 문장 실행
    LOOP -- 데이터 row 건수만큼 회전
      --Memory에 선택된 데이터를 올리낟. 
      /*
        7499 ALLEN 1600
        7521 WARD 1250
        7654 MARTIN 1250
        7698 BLAKE 2850
        7844 TURNER 1500
        7900 JAMES 950
      */
      FETCH c1 INTO vempno , vename, vsal; -- Memory에 올라간 데이터를 하나하나 접근한다.
      EXIT WHEN c1%NOTFOUND; -- 더이상 row 가 없으면 탈출
        DBMS_OUTPUT.PUT_LINE(vempno || '-' || vename || '-'|| vsal);
    END LOOP;
    CLOSE c1;
END;
cs



위의 예시를 좀 더 간소화해 표현하면 다음과 같이 활용할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
DECLARE
  CURSOR emp_curr IS  select empno ,ename from emp;
BEGIN
   
    FOR emp_record IN emp_curr  --row 단위로 emp_record변수 할당, for ~ in 구문은 내부적으로 open을 포함하고 있다
    LOOP
      EXIT WHEN  emp_curr%NOTFOUND;
      DBMS_OUTPUT.PUT_LINE(emp_record.empno || '-' || emp_record.ename);
    END LOOP;
   
    CLOSE emp_curr;
END;
cs





지금까지의 작업은 영속적으로 저장되지 않는다. 우리는 DDL명령어를 통해서 우리가 만든 커서를 영속적으로 저장할 수 있다. 즉, 영속적인 저장을 통해 언제든 별도의 코딩을 없이 객체의 형태로 호출해 활용 할 수 있다.


다음은 그 예시이다.


1
2
3
4
5
6
7
create or replace procedure usp_emplist
is
  BEGIN
    update emp
    set job = 'TTT'
    where deptno=30;
  END;
cs


우리는 위의 코드에서 create or replace를 통해 생성과 수정이 동시에 가능함을 짐작할 수 있다. 위의 코드를 실행하면 


Procedure USP_EMPLIST이(가) 컴파일되었습니다.


라는 메시지가 표시되고, 이로써 우리는 usp_emplist 의 이름으로 프로시저를 생성해 저장하였다.  생성된 프로시저는 다음과 같이 확인할 수 있다.





이렇게 생성된 프로시저는 다음과 같은 코드로 실행시킬 수 있다. 


1
2
3
execute usp_emplist; -- 프로시저 실행
select * from emp where deptno=30
rollback; -- 프로시저는 롤백이 가능하다.
cs



이러한 프로시저의 활용은 다음과 같은 장점을 지닌다.


1. 네트워크의 트래픽시간을 감소시킬 수 있다.

2. 네트워크상의 보안을 강화할 수 있다. 




이러한 프로시저도 파라미터를 받아 처리하도록 설정할 수 있다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- procedure  는 parameter  종류 2가지
--1. input paramter : 사용시 반드시  입력          (IN : 생략하는 default)
--2. output parmater : 사용시 입력값을 받지 않아요 (OUT)
create or replace procedure app_get_emplist
(
  vempno IN emp.empno%TYPE,
  vename OUT emp.ename%TYPE,
  vsal   OUT emp.sal%TYPE
)
is
  BEGIN
    select ename, sal
      into vename , vsal
    from emp
    where empno=vempno;
  END;
 
--오라클 실행 테스트
DECLARE
  out_ename emp.ename%TYPE;
  out_sal   emp.sal%TYPE;
BEGIN
   app_get_emplist(7902,out_ename,out_sal);
   DBMS_OUTPUT.put_line('출력값 : ' || out_ename || '-' || out_sal);
END;
cs


OUTPUT으로 처리한 값은 프로시저를 실행할 때 변수명을 작성해주면 return값으로 받을 수 있다(?)




이제 생성한 프로시저를 자바에서 활용하는 예제를 살펴보자 



생성할 프로시저는 다음과 같다

.


1
2
3
4
5
6
7
8
9
10
11
CREATE OR REPLACE PROCEDURE usp_EmpList
(
p_sal IN number,
 p_cursor OUT SYS_REFCURSOR --APP 사용하기 위한 타입(한 건 이상의 데이터를 select할 때 >> 내부적으로 cursor사용
)
IS
BEGIN
    OPEN p_cursor
       FOR
          SELECT empno, ename, sal FROM EMP WHERE sal > p_sal;
 END;
cs




이 프로시저를 java에서 실행시키면 다음과 같다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
 
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
 
import oracle.jdbc.OracleTypes;
 
/*
CREATE OR REPLACE PROCEDURE usp_EmpList
(
p_sal IN number,
 p_cursor OUT SYS_REFCURSOR --APP 사용하기 위한 타입
)
IS
BEGIN
    OPEN p_cursor
       FOR
          SELECT empno, ename, sal FROM EMP WHERE sal > p_sal;
 END;
*/
public class Ex08_Oracle_Procedure_Select {
 
    public static void main(String[] args) {
         Connection conn = null;
          //명령객체
          CallableStatement cstmt= null//변경 (procedure를 처리하는 객체 
          ResultSet rs = null;
          try{
           Class.forName("oracle.jdbc.OracleDriver");
           conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE""bituser""1004"); 
              
           String sql = "{call usp_EmpList(?,?)}"// 실제 Oracle에서는 execute라는 명령어를 활용해 실행한다는 점에서 헷갈릴 수 있다.
              cstmt = conn.prepareCall(sql);
              
              //usp_EmpList(?,?) input , output
              cstmt.setInt(12000); // input처리를 위한 파라미터 등록 
              cstmt.registerOutParameter(2, OracleTypes.CURSOR); // output에 대한 지정 > output타입이  SYS_REFCURSOR로 지정되어 있다.
           boolean result = cstmt.execute();
              
           rs = (ResultSet)cstmt.getObject(2); // sql에서 2번 째 물음표(Output parameter)에 대한 처리 
          
           while(rs.next()){
            System.out.println(rs.getInt(1+"/" + rs.getString(2+"/" + rs.getInt(3));
           }
          
          }catch(Exception e){
          
          }finally{
           if(rs != null){try{rs.close();}catch(Exception e){}};
           if(cstmt != null){try{cstmt.close();}catch(Exception e){}};
           if(conn != null){try{conn.close();}catch(Exception e){}};
          }
 
    }
 
}
cs








반응형

'BackEnd > Oracle' 카테고리의 다른 글

[20180517] PL SQL Trigger  (0) 2018.05.17