210614_1(데이터베이스8, PL/SQL, 커서, 프로시저)
PL/SQL (제어구조가있는 sql) --6/11
구조
declare 선언부 : 변수
begin 실행부
절차구조가 있는 sql
end ;
/ -- 실행하라
테스트1
1. emp에서 사번이 7900인 직원의 이름 사번을 출력하는 plsql을 작성하고 실행하시오
SQL> ed ex01 --작성
declare
vempno number(4) ;
vename varchar2(20) ;
begin
select empno,ename into vempno,vename
from emp
where empno=7900;
-- 결과 화면 출력
dbms_output.put_line('-------실행결과--------');
dbms_output.put_line(vempno||' '||vename);
end;
/
SQL> @ex01 --실행
PL/SQL 처리가 정상적으로 완료되었습니다.
SQL> set serveroutput on <=== 결과 출력 설정 세션당 한번은 해줘야한다.
SQL> @ex01
-------실행결과--------
7900 JAMES
테스트2
declare
vemp emp%rowtype ---> vemp.ename, vemp.empno ,,,,, 전체를 포함한다
1. emp에서 사번이 7900인 직원의 이름,사번,입사일자,급여,부서번호를 출력하는 plsql을 작성하고 실행하시오
vemp emp%rowtype을 이용해라
declare
vemp emp%rowtype ;
begin
select * into vemp
from emp
where empno=7900;
-- 결과 화면 출력
dbms_output.put_line('-------실행결과--------');
dbms_output.put_line('이름 사번 입사일자 급여 부서번호');
dbms_output.put_line(
vemp.ename||' '||
vemp.empno||' '||
vemp.hiredate||' '||
vemp.sal||' '||
vemp.deptno
);
end;
/
-------실행결과--------
이름 사번 입사일자 급여 부서번호
JAMES 7900 81/12/03 950 30
테스트3
declare vempno emp.empno%type; 한개컬럼/데이터형을 모를때
vdname varchar2(20) :=null; ':=' null로 초기화하라
emp에서 사번이 7900의 사번 이름 부서번호를 선택하여 사번이 10이면 Accounting
사번이 20이면 Research
사번이 30이면 Sales
사번이 40이면 Operations
로 처리하여 사번 이름 부서번호 부서명을 출력하라
----------------------조건식
if 조건 then
처리
end if;
ed ex03 -- 작성
declare
vempno emp.empno%type;
vename emp.ename%type;
vdeptno emp.deptno%type;
vdname varchar2(20) :=null;
begin
select empno,ename,deptno into vempno, vename, vdeptno
from emp
where empno=7900;
if vdeptno = 10 then vdname :='Accounting';
end if;
if vdeptno = 20 then vdname :='Reseach';
end if;
if vdeptno = 30 then vdname :='Sales';
end if;
if vdeptno = 40 then vdname :='Operations';
end if;
-- 결과 화면 출력
dbms_output.put_line('-------실행결과--------');
dbms_output.put_line('사번 이름 부서번호 부서명');
dbms_output.put_line(
vempno||' '||
vename||' '||
vdeptno||' '||
vdname
);
end;
/
SQL> @ex03 -- 실행
-------실행결과--------
사번 이름 부서번호 부서명
7900 JAMES 30 Sales
PL/SQL 처리가 정상적으로 완료되었습니다.
6/14
ex03 을 if elsif elsif end if; 로 전환하기 -- if들중 하나를 선택할때 많이 씌인다.
ed ex04 -- 작성
declare
vempno emp.empno%type;
vename emp.ename%type;
vdeptno emp.deptno%type;
vdname varchar2(20) :=null;
begin
select empno,ename,deptno into vempno, vename, vdeptno
from emp
where empno=7900;
if vdeptno = 10 then vdname :='Accounting';
elsif
vdeptno = 20 then vdname :='Reseach';
elsif
vdeptno = 30 then vdname :='Sales';
elsif
vdeptno = 40 then vdname :='Operations';
end if;
-- 결과 화면 출력
dbms_output.put_line('-------실행결과--------');
dbms_output.put_line('사번 이름 부서번호 부서명');
dbms_output.put_line(
vempno||' '||
vename||' '||
vdeptno||' '||
vdname
);
end;
/
@ex04 -- 실행
-------실행결과--------
사번 이름 부서번호 부서명
7900 JAMES 30 Sales
PL/SQL 처리가 정상적으로 완료되었습니다.
if 조건 then 참처리else 거짓처리 end if; -- ex05
1. vempno, vename, vcomm을 선언하자
2. 사번이 7900인 직원의 사번, 이름, 커미션을 선택하여
3. 커미션이 0보다 크면 xxx은 커미션이 xxx입니다 라고 출력하고
아니면 xxx은 커미션이 없습니다. 라고 출력하시오 (null인 경우 0으로처리하라)
declare
vempno emp.empno%type;
vename emp.ename%type;
vcomm emp.comm%type;
begin
select empno,ename,NVL(comm,0) into vempno, vename, vcomm
from emp
where empno=7900;
if vcomm> 0 then
dbms_output.put_line(vename||'의 커미션은'||vcomm||'입니다');
else
dbms_output.put_line(vename||'는 커미션이 없습니다.');
end if;
end;
/
SQL> @ex05
JAMES는 커미션이 없습니다.
PL/SQL 처리가 정상적으로 완료되었습니다.
반복문 -- ex06
for 변수 in 시작..끝 loop
반복처리식;
end loop;
문제) 구구단 만들기
for i in 1..9 loop
tot := vdan * i;
dbms_output.put_line(vdan ||'*'|| i || '=' tot);
end loop;
-- 2단을 출력하는 PL/SQL을 만들고 실행하시오
declare
i number(2) := 0;
vdan number(2) := 2;
tot number(2) := 0;
begin
for i in 1..9 loop
tot := vdan * i; -- 값 초기화 :=
dbms_output.put_line(vdan ||'*'|| i || '='||tot);
end loop;
end;
/
SQL> @ex06
2*1=2
2*2=4
2*3=6
2*4=8
2*5=10
2*6=12
2*7=14
2*8=16
2*9=18
위의 구구단 결과를 db에 저장하는 PL/SQL을 만들고 실행하시오 -- ex07
create table gugudan (
vdan number(2),
i number (2),
tot number (4)
); -- 테이블을 먼저 생성해놓는다
declare
i number(2) := 0;
vdan number(2) := 2;
tot number(2) := 0;
begin
for i in 1..9 loop
tot := vdan * i;
dbms_output.put_line(vdan ||'*'|| i || '='||tot);
insert into gugudan values(vdan,i,tot); --db에 저장하는 처리식
end loop;
end;
/
SQL> @ex07
2*1=2
2*2=4
2*3=6
2*4=8
2*5=10
2*6=12
2*7=14
2*8=16
2*9=18
PL/SQL 처리가 정상적으로 완료되었습니다.
SQL> select * from gugudan;
VDAN I TOT
---------- ---------- ----------
2 1 2
2 2 4
2 3 6
2 4 8
2 5 10
2 6 12
2 7 14
2 8 16
2 9 18
위의 결과에 시퀀스를 추가하는 gugu_seq
컬럼 idx 을 부여하는 컬럼추가 하시오 -- ex08
create sequence gugu_seq increment by 1 start with 1 ; -- 1부터 1씩 증가하는 시퀀스를 생성하고
alter table gugudan add (idx number(4)); -- 테이블에 idx컬럼을 추가하여 만들어놓는다
declare
i number(2) := 0;
vdan number(2) := 2;
tot number(2) := 0;
begin
for i in 1..9 loop
tot := vdan * i;
dbms_output.put_line(vdan ||'*'|| i || '='||tot);
insert into gugudan values(vdan,i,tot,gugu_seq.nextval);
end loop;
end;
/
SQL> @ex08
PL/SQL 처리가 정상적으로 완료되었습니다.
VDAN I TOT IDX
---------- ---------- ---------- ----------
2 3 6 3
2 4 8 4
2 5 10 5
2 6 12 6
2 7 14 7
2 8 16 8
2 9 18 9
2 1 2 10
2 2 4 11
왠지 모르겠는데 꼬임
사용자 정의의 데이터 형 -- PL/SQL 테이블
PL/SQL 테이블이란 : 컬럼의 자료형이 들어간 연속된 공간 즉, 배열과 유사하다
기본형 : type 타입명 is table of 테이블.컬럼%type index by binary_integer;
변수명 타입명;
ex) vname varchar2(20);
type enames is table of
emp.ename%type
index by binary_integer;
venames enames;
ex09
emp에서 사번이 7900인 직원의 이름을 선택하여 저장하는 테이블타입 enames
변수 venames를 설정하고
'vename 첫번째 담긴 이름은'|| venames(0)||' 입니다'
라고 출력하하는 plsql을 작성하고 확인하시오
declare
vename varchar2(20);
type enames is table of emp.ename%type index by binary_integer;
venames enames;
begin
select ename into vename
from emp
where empno=7900;
venames(0) :=vename;
dbms_output.put_line('venames 첫번째 담긴 이름은 :'||venames(0)||' 입니다');
end;
/
SQL> @ex09
venames 첫번째 담긴 이름은 :JAMES 입니다
PL/SQL 처리가 정상적으로 완료되었습니다.
PL/SQL 레코드 타입
PL/SQL 레코드 : 하나의 행을 담을 수 있는 사용자 정의 타입
기본형 : type 이름 is recode (
컬럼 데이터형,
컬럼 데이터형,
컬럼 데이터형
);
변수명 타입이름;
부서번호가 10인 데이터의 정보를 dept에서 선택하여 부서번호, 부서명, 지역을 출력하시오 select * into 변수명
declare
type dept_recode is record(
deptno dept.deptno%type,
dname dept.dname%type,
loc dept.loc%type
);
r_dept dept_recode ;
begin
select * into r_dept
from dept
where deptno=10;
dbms_output.put_line('부서번호 부서명 지역');
dbms_output.put_line(r_dept.deptno||' '||r_dept.dname||' '||r_dept.loc);
end;
/
SQL> @ex10
부서번호 부서명 지역
10 ACCOUNTING NEW YORK
커서 curosr (다중행 처리)
다중행 처리
* 지금까지 작성한 plsql의 select 문장의 결과는 1행 이었다.
만약 sql의 결과 행이 다중행이라면, cursor를 활용해야 한다.
커서란 sql문장의 실행 단위이며 명시적 커서와 암시적 커서가 있다.
지금까지 사용한 결과가 1행인 경우 암시적 커서를 사용한 것이다.
명시적 커서 기본형
declare
cursor 커서명 is select ~~~
begin
open 커서명;
loop
fetch 커서명 into 변수명 ,,,,, ;
exit when 커서명%notfound; -- 루프탈출
end loop;
close 커서명;
end;
/
ex11. emp에서 부서번호가 20인 직원의 사번, 이름, 급여를 출력하는 plsql을 만들고 실행하시오.
커서명 c1 변수명 vempno, vename, vsal
declare
vempno emp.empno%type;
vename emp.ename%type;
vsal emp.sal%type;
cursor c1 is select empno, ename, sal from emp where deptno = 20;
begin
open c1;
dbms_output.put_line('사번 이름 급여');
loop
fetch c1 into vempno, vename, vsal;
exit when c1%notfound;
dbms_output.put_line(vempno||' '||vename||' '||vsal);
end loop;
close c1;
end;
/
사번 이름 급여
7369 SMITH 800
7566 JONES 2975
7902 FORD 3000
PL/SQL 처리가 정상적으로 완료되었습니다.
커서활용 2 -- open ~fetch ~ close없는 커서 활용
기본형
declare
vemp emp%rowtype;
cursor 커서명 is select~~begin
for vemp in 커서 loop
exit when 커서명%notfound;
반복 처리 문장 ---- vemp.empno
end loop;
end;
/
ex12 ex11을 open patch close없는 문장으로 바꾸어보시오
declare
vemp emp%rowtype;
cursor c1 is select empno, ename, sal from emp where deptno = 20;
begin
dbms_output.put_line('사번 이름 급여');
for vemp in c1 loop
exit when c1%notfound;
dbms_output.put_line(vemp.empno||' '||vemp.ename||' '||vemp.sal);
end loop;
end;
/
SQL> @ex12
사번 이름 급여
7369 SMITH 800
7566 JONES 2975
7902 FORD 3000
커서 상태 정리
%notfound 커서의 자료가 모두 패치 되었는가
%found 커서의 자료가 패치 되지 않은것이 있는가
%isopon 커서가 open되어있는 상태인가
%rowcount 패치(fetch)된 행이 몇개인가
실습1 -- ex13
아래처럼 출력되도록 plsql 작성하고 실행하시오 --ex13
이름 급여
-----------------
SMITH 800
ALLEN 1600
....중략..
-------------------
총합 ~~~
declare
vename emp.ename%type;
vsal emp.sal%type;
tot number(5) :=0;
cursor cr is select ename, sal from emp;
begin
dbms_output.put_line('이름 급여');
dbms_output.put_line('--------------');
for vemp in cr loop
exit when cr%notfound;
tot := tot+ vemp.sal;
dbms_output.put_line(vemp.ename||' '||vemp.sal);
end loop;
dbms_output.put_line('--------------');
dbms_output.put_line('총합 '||tot);
end;
/
SQL> @ex13
이름 급여
--------------
SMITH 800
ALLEN 1600
WARD 1250
JONES 2975
MARTIN 1250
BLAKE 2850
CLARK 2450
KING 5000
TURNER 1500
JAMES 950
FORD 3000
MILLER 1300
--------------
총합 24925
실습2 -- ex14
아래처럼 출력되도록 plsql작성하고 실행하시오
*은 100당 한개
KING *********************<5000>
FORD******************<3000>
JONES**************<2975>
.........중략
SMITH *****<800>
declare
cnt number(3) := 0;
stars varchar2(200);
cursor cr is select ename, sal from emp order by sal desc;
begin
for vemp in cr loop
exit when cr%notfound;
cnt := round(vemp.sal/100);
for i in 1..cnt loop
stars := stars || '*';
end loop;
dbms_output.put_line(vemp.ename|| stars ||'<'||vemp.sal||'>');
stars :=''; -- 초기화가없으면 * 가 중첩된다
end loop;
end;
/
SQL> @ex14
KING**************************************************<5000>
FORD******************************<3000>
JONES******************************<2975>
BLAKE*****************************<2850>
CLARK*************************<2450>
ALLEN****************<1600>
TURNER***************<1500>
MILLER*************<1300>
MARTIN*************<1250>
WARD*************<1250>
JAMES**********<950>
SMITH********<800>
어렵당..
저장프로시져 / 함수 (DB에저장하면 저장프로시져, 반환값이 있으면 저장함수)
일반적으로 프로시져는 파일에 쿼리를 정의하고 파일을 실행시키는 방법이다.
하지만 저장프로시져는 그 내용을 db에 저장하고 실행하는 방법이다.
기본형 -------------------------------------
create or replace procedure 프로시져명(
인자 데이터형, 인자 데이터형,,,, )
is 지역변수 -- 지역변수 생성할 경우
begin
쿼리문(처리식);
end;
/ -- 생성(명령)
execute 프로시져명; -- 실행(명령)
----------------------------------------------
1.dept12의 데이터를 전부 삭제하는 저장 프로시져를 만들고 실행하기
SQL> select * from dept12;
DEPTNO DNAME LOC
---------- ------------------------------ ------------------------------
1 회계 서울
4 무역 인천
5 인사 남양주
ed sp01 -- 프로시저 작성
create or replace procedure del_dept
is
begin
delete from dept12;
end;
/
@sp01 -- 프로시저 생성
프로시저가 생성되었습니다.
execute del_dept; -- 프로시저실행
PL/SQL 처리가 정상적으로 완료되었습니다.
SQL> select * from dept12;
선택된 레코드가 없습니다.
프로시져 정보보기
desc user_source;
이름 널? 유형
-----------------------------------------------------------------------------------------------------
NAME VARCHAR2(30)
TYPE VARCHAR2(12)
LINE NUMBER
TEXT VARCHAR2(4000)
SQL> select text from user_source;
TEXT
--------------------------------
procedure del_dept
is
begin
delete from dept12;
end;
프로시져 에러보기
show error;
인자가 있는 저장 프로시져
emp_copy에서 직원의 이름, JONES를 입력받아서 데이터를 삭제하는 저장 프로시져 del_name을 생성하고 실행하시오.
(변수 in 데이터형) -- 들어오는 변수 (변수 out 데이터형) -- 나가는 변수
ed sp02 -- 작성
create or replace procedure del_name(
vename in emp_copy.ename%type) -- 입력받는 변수
is
begin
delete from emp_copy where ename=vename;
end;
/
@ep02 -- 프로시저생성
execute del_name('JONES') -- 프로시저실행
select * from emp_copy where ename=vename --확인
in out 변수를 사용하기
select empno, sal from emp_copy;
문제) emp에서 사번이 7521인 직원의 이름과 직책을 출력하는 저장 프로시저 job_empno를
생성하고 실행하고 확인하시오
ed sp03 -- 프로시저 작성
create or replace procedure job_empno(
vempno in emp_copy.empno%type --입력받는
, vename out emp_copy.ename%type --출력하는
, vjob out emp_copy.job%type ) --출력하는
is
begin
select ename, job into vename, vjob from emp_copy where empno=vempno;
end;
/
프로시저 작성 후 실행
variable var_name varchar2(15); --변수선언
variable var_job varchar2(15); --변수선언
execute job_empno(7521,:var_name,:var_job) -- 인자를 넣고 프로시저 실행
print var_name var_job -- 출력
VAR_NAME VAR_JOB
------------- ---------------
WARD SALESMAN
반환형이 있는 저장 함수 -- function
저장함수는 저장프로시저와 거의 동일하나 반환값이 있다라는 것만 다르다.
또 하나 특이한 점은 함수이기 때문에 select절에 사용할 수 있다.
기본형
create or replace function 함수명 (
이름 in/out 데이터형,
이름 in/out 데이터형, ,,,,)
return 데이터형 -- 반환형이있기 때문에 꼭 작성해야한다
is 지역변수;
begin
처리식;
return 값; -- 반환형이있기 때문에 꼭 작성해야한다
end;
/
문제) 사번을 받아서 급여의 200%를 돌려주는 저장함수cal_bonus()를 만들고 실행하시오
ed sp04 -- 프로시저 작성
create or replace function cal_bonus (
vempno emp_copy.empno%type
) return number
is vsal number(7,2);
begin
select sal into vsal from emp_copy where empno=vempno;
return vsal*2;
end;
/
SQL> @sp04 --프로시저 생성
함수가 생성되었습니다.
SQL> variable d_sal number ; --변수선언
SQL> execute :d_sal := cal_bonus(7844) -- 프로시저실행
PL/SQL 처리가 정상적으로 완료되었습니다.
SQL> print d_sal -- 출력
D_SAL
----------
3000
select cal_bonus(7844) from emp_copy; -- 함수로 만들었기 때문에 select 절에서 사용가능하다.
CAL_BONUS(7844)
---------------
3000