형식:
DECLARE -- 커서 선언
CURSOR 커서이름IS 실행문; -- 보통 select SQL문이 실행문에 들어간다.
BEGIN -- 커서 열기
OPEN 커서이름;
FETCH 커서이름 INTO 바인드변수;
CLOSE 커서이름; --커서닫기
END;
/
OPEN
FETCH
이것만 봐선 무슨 소린지 모르겠다..
예제를 통해서 살펴보아야 겠다.
부서 테이블의 모든 데이터를 출력하기 위한 PL/SQL문 작성
1) 저장 프로시저를 생성한다.
set SERVEROUTPUT ON
create or replace procedure cursor_sample01
is
vdept dept%rowtype;
cursor c1 is
select * from dept;
BEGIN
dbms_output.put_line('부서번호 / 부서명 / 지역명');
dbms_output.put_line('------------------------');
OPEN c1;
LOOP
FETCH c1 into vdept.deptno, vdept.dname, vdept.loc; -- 인출
exit when c1%notfound; -- 커서가 가져올 데이터가 없을 때 TRUE
dbms_output.put_line(vdept.deptno || ' / ' || vdept.dname || ' / ' || vdept.loc);
END LOOP;
CLOSE c1;
END;
/
2) 프로시저 실행.
매개변수가 없으므로 프로시저 명만 입력해도 잘 실행된다.
execute cursor_sample01;
3) 출력한다.
여전히 잘 모르겠지만 일반 loop문 대신 for loop 문으로 같은 내용을 출력해보는 실습을 했다.
for loop문의 장점은 OPEN ~ FETCH ~ CLOSE 없이 보다 간결하게 SQL문을 작성할 수 있다는 점인데,
각 반복문마다 커서를 열고, 각 행을 인출 fetch 하고, close로 커서를 닫는 작업을 자동으로 처리해준다.
set SERVEROUTPUT ON
create or replace procedure cursor_sample02
is
dept dept%rowtype;
cursor c1
is
select*from dept;
BEGIN
dbms_output.put_line('부서번호 / 부서명 / 지역명');
dbms_output.put_line('------------------------');
FOR vdept IN c1 LOOP
EXIT WHEN c1%notfound;
dbms_output.put_line(vdept.deptno || ' / ' || vdept.dname || ' / ' || vdept.loc);
END LOOP;
END;
/
다른 점은 함수는 Return 구문을 사용해서 실행 결과를 되돌려 받을 수 있다는 것이다.
결과를 되돌려받기 위해 자료형과 값을 기술해야 한다.
생성 :
create or replace function 저장함수이름
(매개변수 [in/out/inout] 데이터타입,
...)
is
RETURN 데이터타입;
BEGIN
실행문1;
실행문2;
RETURN 돌려줄값
END;
/
실행 :
execute :variable_name := 함수이름(매개변수);
예제로 사원 테이블에서 특정 사원의 급여를 200% 인상하는 저장 함수를 작성해 보았다.
1) 저장 함수 생성
create or replace function cal_bonus(
vempno in emp.empno%type)
RETURN number
is
vsal number(7,2);
BEGIN
select sal into vsal from emp
where empno = vempno;
RETURN vsal * 2;
END;
/
2) 돌려받을 값을 저장할 바인드 변수를 생성한다.
바인드 변수 생성문은 실행해도 콘솔창에 별다른 변화가 없다.
variable var_sal number;
3) 함수 실행
괄호 안에 넣는 사원 번호를 매개로 하여 해당 사원의 급여를 200% 인상한 값을 가져온다.
execute :var_sal := cal_bonus(7788);
5) 값을 출력한다.
사원번호가 7788인 사원의 200% 인상된 급여는 6000이다.
print var_sal;
6) cal_bonus 저장 함수를 select SQL문에 포함하여 실행해 보았다.
select ename, sal, cal_bonus(7788) as "200%인상" from emp where empno=7788;
사원번호를 매개변수로 전달 받아 그 사원의 이름, 급여, 직책을 구하는 프로시저를 생성해본다.
1) 프로시저 생성
create or replace procedure sal_empno
(vempno in emp.empno%type,
vename out emp.ename%type,
vsal out emp.sal%type,
vjob out emp.job%type)
is
BEGIN
select ename, sal, job into vename, vsal, vjob from emp
where empno = vempno;
END;
/
1. 조건 없이 반복 작업을 하기 위한 BASIC LOOP 2. COUNT를 기본으로 작업의 반복 제어를 하는 FOR LOOP 3. 조건을 전제로 작업의 반복 제어를 하기 위한 WHILE LOOP 4. 종료를 위한 EXIT
1. BASIC LOOP
형식
loop
반복 실행될 문자;
조건식 exit;
end loop;
Q. 1 ~ 5 까지 출력하는 예제
set serveroutput on
DECLARE
n number := 1; -- 변수의 초기값을 1로 설정
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(n);
n := n + 1;
IF n > 5 THEN exit;
END IF;
END LOOP;
END;
/
만약 If ~ then ~ end if 조건문을 설정하지 않으면 무한 루프가 돌아갈 것 같지만,
number 자료형의 용량 한계로 인해 185184까지만 출력된다.
Q. 1+2+3+...+10 의 값을 출력하는 예제
set serveroutput on
DECLARE
n number := 1; --루프를 돌릴 변수
s number := 0; --합이 누적될 변수
BEGIN
LOOP
s := s + n;
n := n + 1;
IF n > 10 THEN exit;
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE('1~10까지의 합:' || S);
END;
/
1. FOR LOOP
반드시 1씩 증가하는 형식이고 2 증가 3 증가 등은 어렵기 때문에 잘 사용되지 않는다.
형식
FOR 루프를돌릴변수명
IN 작은값..큰값
LOOP
반복실행될문장;
END LOOP;
구문
설명
index_counter 루프를 돌릴 변수명
upper_bound나 lower_bound에 도달할 때까지 LOOP를 반복함으로써 1씩 자동적으로 증가하거나 감소되는 값을 가진 암시적으로 선언된 정수입니다.
절차적 언어로, SQL의 단점을 보완해 좀 더 효율적으로 SQL문을 사용할 수 있게 해준다.
구조
1. 선언부
DECLARE로 시작
모든 변수나 상수를 선언하는 부분.
2. 실행부
BEGIN으로 시작
내용을 기술.
3. 예외처리부
EXCEPTION으로 시작
(잘 쓰지 않는다.)
'Hello World'를 SQL Developer 콘솔창에 출력해보는 실습을 해 보았다.
제일 먼저 환경 변수를 설정해주어야 한다.
기본 값이 off 인데 이것을 바꿔주지 않으면 아무리 명령문을 실행해도 콘솔창에 나타나지 않는다.
set serveroutput on
이어서 다음과 같이 작성하고 실행한다.
BEGIN
dbms_output.put_line('Hello World!');
END;
/
1. 변수 선언
declare로 시작하는 선언부에서 선언해야 한다.
1) 스칼라(SCALAR) 형식
2) 레퍼런스 형식
1) 스칼라 형식
변수 선언시 '=' 이 아닌 ' := ' 로 선언해 주어야 한다.
set severoutput on
DECLARE
vempno number(4);
vename varchar2(10);
BEGIN
vempno := 7788;
vename := 'SCOTT';
dbms_output.put_line('사번 / 이름');
dbms_output.put_line(vempno || ' / ' || vename);
END;
/
자바에서는 system.out.println(); 을 쓸때 괄호 안에서 문자와 변수를 섞어쓸 때
vempno + " / " + vename 같이 입력을 했지만
이 쪽에서는 || 를 이용해서 연결을 해주어야 한다.
vempno + " / " + vename (x)
vempno || ' / ' || vename (O)
2) 레퍼런스 형식
여기서 %의 의미는
emp테이블의 empno 타입과 같은 자료형 형식을 똑같이 한다는 뜻이다.
vempno emp.empno%type;
vename emp.ename%type;
%type : 컬럼 하나를 참조하는 레퍼런스 변수 선언
%rowtype : 로우 전체를 참조하는 레퍼런스 변수 선언
2. SELECT문
BEGIN으로 시작되는 선언부 안에 들어가며, into 절이 필요하다.
select 절의 컬럼은 into절의 변수와 1:1 대응을 하기에 개수와 데이터의 형, 길이가 일치해야 한다.
Q. SCOTT 사원의 사번과 이름 검색하기 (레퍼런스 형식으로 변수 선언)
set SERVEROUTPUT ON
DECLARE
vempno emp.empno%type;
vename emp.ename%type;
BEGIN
SELECT empno, ename INTO vempno, vename
FROM emp
WHERE ename = 'SCOTT';
dbms_output.put_line('사번 / 이름');
dbms_output.put_line(vempno || ' / ' || vename);
END;
/
같은 결과가 나오도록 출력했지만,
스칼라 변수 선언과 레퍼런스 변수는 다른 형식이다.
변수 선언시 타입과 저장 공간을 직접 지정하는 것과, 이미 존재하는 테이블의 형식을 그대로 따오는 것.
3. 선택문
1) IF ~ THEN ~ END IF
TRUE : THEN 이하의 문장 실행
FLASE or NULL : END IF 다음 문장 실행
사원 테이블에서 SCOTT 사원의 연봉을 구하는 PL/SQL문을 작성해보는 실습
set SERVEROUTPUT ON
DECLARE
vemp emp%rowtype;
vsal number(7,2);
BEGIN
SELECT * INTO vemp FROM emp WHERE ename='SCOTT';
IF vemp.comm is null THEN vemp.comm := 0;
END IF;
vsal := vemp.sal * 12 + vemp.comm;
DBMS_OUTPUT.PUT_line('사번 / 이름 / 연봉');
DBMS_OUTPUT.PUT_line(vemp.empno || ' / ' || vemp.ename || ' / ' || vsal);
END;
/
scott 사원의 일부 컬럼만 뽑아 쓰는 것보다는 rowtype으로 전체 데이터를 참조하는 것이 더 간단하므로 %rowtype
2) IF - THEN - ELSE - END IF
SET SERVEROUTPUT ON
DECLARE
vemp emp%rowtype;
vsal number(7,2);
BEGIN
SELECT * INTO vemp FROM emp WHERE ename = 'SCOTT';
IF vemp.comm is null THEN
vsal := vemp.sal*12;
ELSE
vsal := vemp.sal+12+vemp.comm;
END IF;
DBMS_OUTPUT.PUT_line('사번 / 이름 / 연봉');
DBMS_OUTPUT.PUT_line(vemp.empno || ' / ' || vemp.ename || ' / ' || vsal);
END;
/
위의 IF ~ THEN ~ END IF; 문과 다른 점은
1)은 comm 컬럼의 null 값을 0으로 치환하여 계산했다는 것이고,
2)는 comm 컬럼이 null 인 경우 계산 방식과 null 이 아닌 경우의 계산 방식을 각각 지정하여 처리했다는 점이다.
3) IF ~ THEN ~ ELS IF ~ ELSE ~ END IF
SCOTT 사원의 부서번호를 이용해서 부서명을 구하는 PL/SQL문 작성해보기
SET SERVEROUTPUT ON
DECLARE
vemp emp%rowtype;
vdname varchar2(14);
BEGIN
SELECT * INTO vemp FROM emp WHERE ename = 'SCOTT';
IF vemp.deptno = 10 THEN vdname = 'ACCOUNTING';
ELSIF vemp.deptno = 20 THEN vdname = 'RESEARCH';
ELSIF vemp.deptno = 30 THEN vdname = 'SALES';
ELSE vemp.deptno = 40 THEN vdname = 'OPERATIONS';
END IF;
DBMS_OUTPUT.PUT_line('사번 / 이름 / 부서');
DBMS_OUTPUT.PUT_line(vemp.empno || ' / ' || vemp.ename || ' / ' || vdname);
END;
/