11. PL/SQL
1. PL/SQL이란
- Procedual Language extension to Structured Query Language
- SQL과 일반 프로그래밍 언어의 특성을 결합
- 변수 상수 선언 가능
- 조건문 반복문 사용 가능
- DBMS의 역할이 커지면서 SQL을 넘어서는 일반 프로그래밍 언어의 기능이 필요하게 되었다
- 1989년 오라클 6버전부터 등장
a. 기본구조
- 선언부 (Declarative part)
- 실행부에서 사용할 변수나 상수 선언
- 변수와 상수 선언은 반드시 선언부에서만 선언
- Declare 사용
- 실행부 (Executable part)
- 셀지 처리할 로직 부분 담당
- 변수에 값 할당, if, while, sql 등
- 실행부에 여러개의 sql문장들을 순차적으로 사용
- 블록 단위로 한번에 처리
- Begin으로 시작, End로 끝
- 예외처리부 (Exception-building part)
- 로직 처리의 오류처리부분
- Exception 사용
- 기본구조
declare
counter INTEGER; --선언부
begin
counter := 10;
counter := counter /0;
dbms_output.put_line(counter); --실행부
exception when others then
dbms_output.put_line('ERRORS'); --예외처리부
end;
- if문 예
```sql declare –선언부 - 변수 선언 counter number;
begin –실행부 - 로직처리 counter :=1; –변수에 값 할당
--로직처리
counter := counter/0;
if counter is not null then
dbms_output.put_line('counter => ' || counter);
end if;
exception when others then --예외처리부
dbms_output.put_line('0으로 나누면 안됩니다'); end;
–dbms 출력 : 0으로 나누면 안됩니다
- for문 예
```sql
declare
i number;
rst number;
begin
for i in 1..10 loop
rst := i*2;
dbms_output.put_line(rst);
end loop;
exception when others then
dbms_output.put_line('error');
end;
2
4
6
8
10
12
14
16
18
20
- while문 예
declare
i number;
result number;
begin
i := 1;
while i<=10 loop
result := i*3;
dbms_output.put_line(i||'*3 = ' ||result);
i:=i+1;
end loop;
exception when others then
dbms_output.put_line('error');
end;
1*3 = 3
2*3 = 6
3*3 = 9
4*3 = 12
5*3 = 15
6*3 = 18
7*3 = 21
8*3 = 24
9*3 = 27
10*3 = 30
b. 변수와 상수
변수명 데이터타입;
상수명 constant 데이터타입;
변수명 테이블명.컬럼명%type;
c. 조건문
- if문
if 조건 then
처리문1;
elsif 조건2 then
처리문2;
else
처리문3;
end if;
--java와 다르게, else if 가 아니라 elsif, e가 없다
declare
grade char;
result clob;
begin
grade := 'B';
if grade = 'A' then
result := 'Excellent';
elsif grade = 'B' then
result := 'Good';
elsif grade = 'C' then
result := 'Fair';
elsif grade = 'D' then
result :='Poor';
else
result := 'Not found!';
end if;
dbms_output.put_line(grade || '=>' || result);
exception when others then
dbms_output.put_line('error');
end;
- case문
case 변수
when 조건 then 명령문
when 조건2 then 명령문
else 명령문
end case;
declare
grade char;
result varchar2(50);
begin
grade := 'C';
case grade
when 'A' then
result := 'Excellent';
when 'B' then
result := 'Good';
when 'C' then
result := 'Fair';
when 'D' then
result := 'Poor';
else
result := 'Not found';
end case;
dbms_output.put_line(grade || '=>' || result);
exception when others then
dbms_output.put_line('error');
end;
d. 반복문
- loop 문
loop
처리문장
end loop
declare
i number;
result number;
begin
i := 1;
loop
result := i*2;
exit when result > 20;
dbms_output.put_line(result);
i := i+1;
end loop;
exception when others then
dbms_output.put_line('errer');
end;
- while문
while 조건 loop
처리문장
end loop;
declare
i number;
result number;
begin
i := 1;
result :=0;
while result < 20 loop
result := i*2;
dbms_output.put_line(result);
i := i+1;
end loop;
exception when others then
dbms_output.put_line('error');
end;
- for문
for 카운터 in [reverse] 최소값..최대값 loop
처리문장
end loop;
declare
i number;
result number;
begin
for i in 1..10 loop
result := i*5;
dbms_output.put_line('i,result ='||i||','||result);
end loop;
dbms_output.put_line('---for문 reverse---');
result := 0;
for i in reverse 1..10 loop
result := i*5;
dbms_output.put_line('i,result ='||i||','||result);
end loop;
exception when others then
dbms_output.put_line('error');
end;
i,result =1,5
i,result =2,10
i,result =3,15
i,result =4,20
i,result =5,25
i,result =6,30
i,result =7,35
i,result =8,40
i,result =9,45
i,result =10,50
---for문 reverse---
i,result =10,50
i,result =9,45
i,result =8,40
i,result =7,35
i,result =6,30
i,result =5,25
i,result =4,20
i,result =3,15
i,result =2,10
i,result =1,5
e. goto, null 문
- goto 문
- 특정 위치로 분기하는 문장
- null 문
- 아무것도 처리하지 않는 문장
- goto문
declare
i number;
result number;
begin
goto second;
for i in 1..10 loop
result := i*5;
dbms_output.put_line('i,result ='||i||','||result);
end loop;
dbms_output.put_line('---for문 reverse---');
<<second>>
result := 0;
for i in reverse 1..10 loop
result := i*5;
dbms_output.put_line('i,result ='||i||','||result);
end loop;
exception when others then
dbms_output.put_line('error');
end;
-- 첫번째 for 문을 생략하고 <<second>>아래의 구문으로 이동하여 두번째 for 문만 실행
2. PL/SQL 서브프로그램
- pl/sql 블록을 데이터 베이스 객체로 저장
- 필요할 때마다 호출해서 사용
- 내장프로시저, 함수
a. 함수
- 특정 기능을 수행한 뒤, 결과값을 반환하는 서브프로그램
- 호출 : 함수명(파라미터)
create or replace function 함수명
( 파라미터1 데이터타입,
파라미터2 데이터타입,...
)
return 데이터타입
is [as]
begin
처리내용
return 반환값;
end;
- 함수 사용 예
-- 함수 생성
create or replace function get_gender(
p_ssn varchar2
)
return varchar2
is
v_gender varchar2(10);
begin
select case when substr(p_ssn,7,7) in ('1','3') then '남자'
else '여자' end
into v_gender
from dual;
return v_gender;
exception when others then
dbms_output.put_line('error');
end;
--함수 실행
select get_gender('9412151111111') from dual;
select gno, gname, jumin, get_gender(jumin) 성별, length(gname) 이름길이
from gogak;
b. 프로시저
- 특정 기능을 수행하지만 값을 반환하지 않는 서브프로그램
- 호출 : execute 프로시저명(파라미터);
create or replace procedure 프로시저명
( 파라미터1 데이터타입,
파라미터2 데이터타입,...)
is [as]
변수선언부
begin
처리내용
end;
- 프로시저 사용 예
--프로시저 생성
--pd2테이블에 입력하는 프로시저
create or replace procedure pd2_insert
(
--파라미터
--pd2 테이블에 insert 할 때 필요한 파라미터들
p_pdcode char,
p_pdname varchar2,
p_price number,
p_company varchar2
)
is
--변수선언
begin
insert into pd2
values(pd2_seq.nextval,p_pdcode, p_pdname,p_price,p_company,sysdate);
commit;
exception when others then
dbms_output.put_line('error');
rollback;
end;
--저장 프로시저 실행
execute pd2_insert('D01','마이크',30000,'보스');
select * from pd2;
--pd2 테이블 컬럼을 수정하는 프로시저 만들기
create or replace procedure pd2_update
(
--파라미터
p_no pd2.no%type,
p_pdcode pd2.pdcode%type,
p_pdname pd2.pdname%type,
p_price pd2.price%type,
p_company pd2.company%type
)
is
--변수 선언부
v_cnt number(3);
begin
select count(*) into v_cnt
from pd2
where no=p_no;
--해당 데이터가 존재하면 update
if v_cnt>0 then
update pd2
set pdcode = p_pdcode, pdname= p_pdname, price=p_price,
company = p_company
where no = p_no;
end if;
commit;
exception when others then
dbms_output.put_line('error');
end;
--실행
exec pd2_update(3,'D02','마이크2',4000,'보스2');
select * from pd2;
3. 예외
- exception 이하 dbms_output.put_line(‘error’) 메세지
- dbms 내 임시 확인을 위한 출력 메세지
- 자바 연동 시 제대로 된 에러 작동이 일어나지 않음
- raise_application_error(-999,’메세지’);
- 에러번호 999와 함께 에러 메세지 출력
- 자바에서도 같은 에러 발생시 확인 가능
- 사용자 정의 예외
create or replace procedure member_insert
(
p_name member.name%type,
p_jumin member.jumin%type,
p_passwd member.passwd%type,
p_id member.id%type
)
is
system_check_insert_fail exception; --사용자 정의 예외
begin
--일요일 23:00:00 ~ 23:59:59 사이 시스템 점검으로 입력 불가
if to_char(sysdate,'d-hh24') = '1-23' then
raise system_check_insert_fail; --사용자 정의 예외 발생시키기
else
insert into member(no,name,jumin,passwd,id)
values(member_seq.nextval, p_name, p_jumin, p_passwd, p_id);
commit;
end if;
exception when others then
raise_application_error(-20998,'시스템점검중');
rollback;
end;
exec member_insert('홍길동','9907081113333','123','hong');
오류 보고 -
ORA-20998: 시스템점검중
4. 커서
a. 개요
- 먼저 프로시저를 통해 한번에 여러개의 레코드를 출력할 수 있는지 확인
select * from pd2;
create or replace procedure pd2_select
is
begin
select * from pd2;
--error
exception when others then
dbms_output.put_line('error');
end;
- error, select 절에 into절이 들어가야 한다.
- into를 통해 하나의 값을 받아온다
- 레코드 하나 출력
- 이를 보완하기 위해 커서 이용
b. 커서 사용
- 커서를 통해 메모리상에 위치한 쿼리의 결과집합에 접근 가능
- 명시적 커서
- 커서 선언 : 쿼리 정의
- cursor 커서명 is select 문장;
- 커서 열기 : 쿼리 실행
- open 커서명;
- 패치 : 쿼리결과 접근, 루프를 돌며 개별 값에 접근
- fetch 커서명 is 변수 ..;
- 커서 닫기 : 메모리상 존재하는 쿼리결과 소멸
- close 커서명;
- 커서 선언 : 쿼리 정의
create or replace procedure pd2_select
is
--1.커서선언
cursor pd2_csr is
select no,pdcode,pdname,price
from pd2;
--변수선언
pd2_rcd pd2%rowtype;
begin
--2.커서 열기
open pd2_csr;
--3.패치
loop
fetch pd2_csr into pd2_rcd.no, pd2_rcd.pdcode, pd2_rcd.pdname,
pd2_rcd.price;
exit when pd2_csr %notfound;
dbms_output.put_line(pd2_rcd.no|| pd2_rcd.pdcode|| pd2_rcd.pdname||
pd2_rcd.price);
end loop;
--4.커서닫기
close pd2_csr;
exception when others then
raise_application_error(-20006,'pd2 select error');
end;
exec pd2_select();
2B01LG노트북25000
3B04키보드19000
1A01삼성노트북25000
- %notfound : 커서에서만 사용가능한 속성
- 더이상 할당 할 데이터가 없다는 뜻
c. for loop 커서
create or replace procedure pd2_select2
is
cursor pd2_csr is
select no, pdcode, pdname, price from pd2;
begin
for pd2_row in pd2_csr loop
dbms_output.put_line(pd2_row.no || ' ' || pd2_row.pdcode ||
' ' || pd2_row.pdname || ' ' || pd2_row.price);
end loop;
exception when others then
raise_application_error(-20007, 'pd2 select error!');
end;
exec pd2_select2;
d. SYS_REFCURSOR
create or replace procedure pd2_select3
(
pd2_cursor out SYS_REFCURSOR
)
is
begin
open pd2_cursor for
select no, pdcode, pdname, price, company, regdate
from pd2;
exception when others then
raise_application_error(-20009,'에러');
end;
5. 참고사항
a. rowtype
- table명.coulmn명%type
- 해당 테이블의 컬럼의 데이터타입과 동일하다는 뜻
- %rowtype
- %type과 유사하나, 한 개 이상의 값에 대해 적용
- 로우타입 변수를 선언해 테이블에 있는 row를 대입 가능
create or replace procedure prof_info
(
p_profno professor.profno%type
)
is
v_prof_row professor%rowtype; --professor 테이블의 한 개의 row(레코드) 정보를 담을 수 있는 타입
v_result varchar2(2000);
begin
select * into v_prof_row
from professor
where profno = p_profno;
v_result := v_prof_row.profno || ' ' || v_prof_row.name ||
' ' || v_prof_row.position;
dbms_output.put_line(v_result);
exception when others then
raise_application_error(-20002,'professor 조회 에러');
end;
exec prof_info(1003);
1003 송도권 전임강사
b. in,out
- in 매개변수
- 일반적인 매개변수, 입력 용 매개변수
- in 매개변수에는 값을 넣는다
- out 매개변수
- 결과를 출력하는 용도의 매개변수
- out 매개변수에는 변수를 넣는다
create or replace procedure prof_info2
(
p_profno in professor.profno%type, --in매개변수
o_name out professor.name%type, --out매개변수
o_pay out professor.pay%type --out매개변수
)
is
begin
select name, pay into o_name, o_pay
from professor
where profno= p_profno;
exception when others then
raise_application_error(-20003,'professor 조회 에러');
end;
--out 매개변수가 있는 프로시저 실행하기
declare
v_name professor.name%type,
v_pay professor.pay%type;
begin
prof_info2(1002,v_name,v_pay);
dbms_output.put_line('이름:' || v_name || ',급여:'||v_pay);
exception when others then
dbms_output.put_line('error');
end;
--이름:박승곤,급여:380