Oracle 59일차

[사용자 정의 예외 처리]

정해져 있는 상황(SELECT 결과가 아무것도 없을 때)이 아닌 논리적인 업무 상황을 가정하여 예외 처리를 할 경우 사용자가 직접 예외처리에 대한 상황을 정의해야 함.

** 방법
1. raise
– 예외 처리에 대한 상황을 정의하고 해당 예외에 대한 이름(예외명)을 부여
– 실행부(begin절)에서 예외명 정의, 해당 예외에 대한 출력은 예외처리부(exception)에서 처리
– 프로그램이 정상 종료됨.

2. raise_application_error
– 에러코드와 에러메세지를 정의
– 예외명 없이 바로 예외 상황에 대한 에러 발생
– declare절에서 에외명에 대한 변수 정의 불필요
– exception절에서 해당 예외에 대한 출력을 할 필요 없음
– 프로그램이 비정상 종료됨.
– 에러코드는 20000~20999 에서 맘대로 지정 가능.

** 예제) emp 테이블을 사용하여 사번을 입력하면 해당 사원의 상위 관리자 이름 출력.
7369의 상위 관리자는 FORD입니다.
7369의 상위 관리자가 없습니다.
1111은 존재하지 않습니다.

> !vi emp2.sql
accept vempno prompt '사번을 입력하세요 : '

declare
    vmgr    varchar2(20);
    vcount  number;
    no_mgr  exception;
begin
    select e2.ename, count(e2.ename) into vmgr, vcount
    from scott.EMP e1, scott.emp e2
    where e1.empno = &vempno
    and e1.mgr = e2.empno(+)
    group by e2.ename;
    -- outer join을 안 쓰면 사번이 없는 경우와 매니저가 없는 경우가 구분 불가.
    -- 이러면 vempno가 없는 경우엔 vcount=null, manager 없는 경우엔 vcount=0이 됨.
    
    if vcount=0 then
        raise no_mgr;
    end if;
    
    dbms_output.put_line(&vempno||'s manager is '||vmgr||'.');
exception
    when no_data_found then
    dbms_output.put_line('there is no such empno '||&vempno);
    when no_mgr then
    dbms_output.put_line(&vempno||' does not have manager.');
end;
/

** 예제) professor, student 테이블을 사용하여 교수 번호를 입력하면 해당 교수의 지도 학생 수를 출력.
9999의 지도 학생 : 김신영-일지매
9999의 지도 학생은 없습니다.

> !vi profstud.sql
------------------------------------------------------------------
accept vprofno prompt '교수 번호를 입력하세요 : '

declare
    vcount      number;
    vlist       varchar2(30);
    no_student  exception;
begin
    select listagg(s.name,'-') within group(order by p.name),
           count(s.name)
           into vlist, vcount
    from scott.professor p, scott.student s
    where p.profno = s.profno(+)
    and p.profno = &vprofno
    group by p.profno;
    
    if vcount=0 then
        raise no_student;
    end if;
    
    dbms_output.put_line(&vprofno||'s student is '||vlist);
exception
    when no_data_found then
    dbms_output.put_line('there is no such profno '||&vprofno);
    when no_student then
    dbms_output.put_line(&vprofno||' does not have student.');
end;
/

[cursor]

데이터를 담기 위한 임시 메모리 공간.

사용) 동시에 여러 데이터를 출력하기 위해 커서 선언, 한 줄씩 fetch
for문, dml문이 자동으로 커서를 생성한다.

[커서 예외 처리]

묵시적, 명시적 커서가 정의된 상황에서의 예외 처리.
ex) delete할 대상이 없는 경우 no_data_found로 예외 처리 불가(select문에 한해서만 작동함)
-> SQL%NOTFOUND를 사용하여 예외 처리

** 예제) emp 테이블을 사용하여 사원 번호를 입력받고 해당 사원을 삭제.

> !vi del.sql
-------------------------------------
accept vempno prompt '삭제할 사번을 입력하세요 : '

declare
    vempno number := &vempno;
begin
    delete from scott.emp
    where empno = vempno;
    
    if SQL%NOTFOUND then
        raise_application_error(-20000, 'there is no such data');
    end if;

    commit;
    -- 만약 그냥 raise 쓸 경우에는 commit을 if로 거르는 부분 뒤에 써야지 잘 걸러짐. commit을 먼저 해버리면 commit하면서 cursor가 날라가서 commit도 되고 if에도 걸리는 상황이 됨.

    dbms_output.put_line(vempno||' data is deleted.');
end;
/

[PLSQL 객체]

1. PROCEDURE
– 기능 수행 목적 (조회 후 출력, delete, update 처리, insert)
– 자체적으로 실행 가능 (exec 프로시저명;)
– input값이 존재하지 않을 수 있음.

2. FUNCTION
– 대부분은 input이 필요하지만 안 그런 함수도 있음.
– 반드시 output이 존재하는 객체.
– select, update, delete 등의 sql문 안에서만 실행 가능.

3. TRIGGER
– 특정 동작 전/후 자동으로 실행되는 프로그램 기능을 구현하는 객체
예) 로그온 트리거 (사용자가 로그온 직후 사용자 기록을 특정 테이블에 insert)

4. PACKAGE
– procedure, function을 기능에 따라 묶어둔 객체.
예) dbms_output.put_line() – dbms_output : package, put_line() : function

[PROCEDURE]

입출력값 필수 X
동작을 명령하는 구문 전달

** 문법

create [or replace] procedure 프로시저명
(입력변수1  데이터타입 := 값,  -- 크기 지정 없이 데이터타입만 전달
 입력변수2  데이터타입 := 값,
...)
is|as
    내부변수1  데이터타입;  -- 실행부(BEGIN)에서 만들어진 내부변수 정의, declare를 is|as가 대체한 느낌
    내부변수2  데이터타입;
    ...
    begin
        ...
    end;
/

** 예제1) 사번과 업데이트할 새로운 급여를 입력받고 해당 사원의 급여를 수정.

create or replace procedure scott.emp_sal_update
(vempno     number,
 vnewsal    number)
is
    begin
        update scott.emp
        set sal = vnewsal
        where empno = vempno;
        
        if SQL%NOTFOUND then
            raise_application_error(-20001, 'there is no such empno');
        end if;
        
        commit;
        
        dbms_output.put_line(vempno||'s sal is updated to '||vnewsal);
    end;
/

exec scott.emp_sal_update(7788, 5000);
exec scott.emp_sal_update(1111, 5000);

select * from scott.emp;

** 예제2) 학생번호와 시험성적을 입력받고 학점 기준에 따른 학점으로 업데이트
90이상 A, 80이상 B, 70이상 C, 70미만 D

alter table scott.student add hakjum varchar2(10);
select * from scott.student;

create or replace procedure scott.stud_hakjum
(vstudno    number,
 vjumsu     number)
is
    vhakjum varchar2(10);
begin
    if vjumsu >= 90 then vhakjum := 'A';
    elsif vjumsu >= 80 then vhakjum := 'B';
    elsif vjumsu >= 70 then vhakjum := 'C';
    else vhakjum := 'D';
    end if;
    
    update scott.student
    set hakjum = vhakjum
    where studno = vstudno;
    
    if SQL%NOTFOUND then
        raise_application_error(-20002, 'there is no such studno');
    end if;
    
    commit;
end;
/

exec scott.stud_hakjum(9411, 80);
exec scott.stud_hakjum(1111, 80);
select * from scott.student;

[동적 Dynamic SQL]

SQL 문장이 고정적이지 않고 유동적인 경우의 SQL.
EXECUTE IMMEDIATE로 실행.

ex) 테이블명, 상수명 등이 계속 변경되는 경우의 SQL
- 고정 SQL)
select * from scott.emp;

-- 동적 SQL)
tname := 'EMP'
select count(*) from scott.tname  -- 실행불가(scott.'EMP' 로 전달되므로)
vsql := 'select count(*) from scott.'||tname
execute immediate vsql;

** 예제1) 테이블명을 입력하면 해당 테이블의 전체 건수 출력

create or replace procedure tab_count
(oname  varchar2,
 tname  varchar2)
is
    vsql    varchar2(100);
    vcount  number;
begin
    vsql := 'select count(*) from '||oname||'.'||tname;
    execute immediate vsql into vcount;
    dbms_output.put_line(tname||' 테이블 행의 수 : '||vcount);
end;
/

exec tab_count('scott','emp');

** 예제2) 유저 생성 프로시저(유저명, 롤이름)

주의) SYS계정에서 생성 및 실행!! SYSTEM 유저는 직접 DBA 권한 받은 게 아니기도 하고 create user, grant any privilege 권한이 기본적으로 없어서 SYSTEM에서는 에러 남.

-- create role
create role rl_sel_a;

-- procedure
create or replace procedure create_user
(vuser      varchar2,
 vrole      varchar2)
is
    vsql1   varchar2(50);
    vsql2   varchar2(50);
begin
    execute immediate 'create user '||vuser||' identified by oracle';
    execute immediate 'grant create session to '||vuser;
    execute immediate 'grant '||vrole||' to '||vuser;
    
    dbms_output.put_line(vuser||' is created and '||vrole||' is granted');
end;
/

-- exec 후 조회
exec create_user('itwill1000','rl_sel_a');
select * from dba_users where username like 'ITWILL%' order by created desc;
select * from dba_role_privs where grantee = 'ITWILL1000';

-- 유저 삭제
drop user itwill1000;

Leave a Comment