PL_SQL(Procedural Language extension to SQL)
- PL/SQL은 오라클에서 제공하는 절차적인 프로그래밍 언어이다.
- SQL 문의 반복적인 실행이나 조건에 따른 분기 등 다양한 기능을 제공한다.
- PL/SQL은 선언부(DECLARE SECTION), 실행부(EXECUTABLE SECTION), 예외 처리부(EXCEPTION SECTION)로 구성된다.
- 오라클에서 내장되어 있는 절차적 언어로 SQL 문장 내에서 변수를 정의, 조건 처리(IF), 반복 처리(LOOP, WHITE, FOR)등을 지원한다.
-- 출력 기능 활성화 (환경 변수를 ON으로 바꿔준다.)
SET SERVEROUTPUT ON;
DECLARE
-- 선언부
...
BEGIN
-- 실행부
...
EXCEPTION
-- 예외 처리부
...
END;
/
- 선언부(DECLARE SECTION) : DECLARE로 시작, 변수나 상수를 선언하는 영역이다.
- 실행부(EXECUTABLE SECTION) : BEGIN으로 시작, 제어문, 반복문, 함수 정의 등 로직을 기술하는 영역이다.
- 예외 처리부(EXCEPTION SECTION) : EXCEPTION으로 시작, 예외사항 발생 시 해결하기 위한 문장을 기술하는 영역이다.
PL/SQL 선언부
변수 선언 및 초기화
- 변수 및 상수는 일반 타입 변수, 레퍼런스 타입 변수, ROW 타입 변수로 선언해서 사용할 수 있다.
일반 타입변수
DECLARE
-- 선언부
변수명 [CONSTANT] 자료형(크기) [:= 값]; -- 상수로 만들고 싶을 때 [CONSTANT] 추가
BEGIN
-- 실행부
...
END;
/
DECLARE
EID NUMBER;
ENAME VARCHAR2(15) := '박현진'; -- 선언과 동시에 초기화
PI CONSTANT NUMBER := 3.14; -- 상수 선언, 선언과 동시에 초기화 해야한다.
BEGIN
EID := 300; -- (오라클에서 =는 동등 연산자, :=는 대입 연산자)
-- PI := 3.15; -- 한번 선언하면 다시 바뀌지 않는다.
DBMS_OUTPUT.PUT_LINE('EID : '|| EID); -- || : 연결 연산자
DBMS_OUTPUT.PUT_LINE('ENAME : '|| ENAME);
DBMS_OUTPUT.PUT_LINE('PI : '|| PI);
END;
/
레퍼런스 타입 변수 : 테이블의 컬럼에 데이터 타입을 참조한다.
DECLARE
-- 선언부
변수명 테이블명.컬럼명%TYPE [:= 값];
BEGIN
-- 실행부
...
END;
/
DECLARE
EID EMPLOYEE.EMP_ID%TYPE; -- EMPLOYEE 테이블에 EMP_ID와 동일한 타입으로 지정하겠다 - VARCHAR2(3)
ENAME EMPLOYEE.EMP_NAME%TYPE;
SAL EMPLOYEE.SALARY%TYPE;
BEGIN
-- 노옹철 사원의 사번, 직원명, 급여 정보를 EID, ENAME, SAL 변수에 대입 후 출력한다.
SELECT EMP_ID, EMP_NAME, SALARY
INTO EID, ENAME, SAL -- 반드시 한 줄에 담아줘야 한다.
FROM EMPLOYEE
-- WHERE EMP_NAME = '노옹철';
WHERE EMP_NAME = '&직원명'; -- &멘트 - 대체 변수 : 사용자에게 입력 받아 검색해서 찾을 수 있다.
DBMS_OUTPUT.PUT_LINE('EID : '|| EID);
DBMS_OUTPUT.PUT_LINE('ENAME : '|| ENAME);
DBMS_OUTPUT.PUT_LINE('SAL : '|| SAL);
END;
/
ROW 타입 변수 : 하나의 테이블의 여러 컴럼의 값을 한꺼번에 저장할 수 있는 변수를 선언할 수 있다.
DECLARE
-- 선언부
변수명 테이블명%ROWTYPE;
BEGIN
-- 실행부
...
END;
/
DECLARE
EMP EMPLOYEE%ROWTYPE;
BEGIN
SELECT *
INTO EMP
FROM EMPLOYEE
WHERE EMP_NAME = '&직원명'; -- 데이터 개수가 같아야 한다.
DBMS_OUTPUT.PUT_LINE('사번 : ' || EMP.EMP_ID);
DBMS_OUTPUT.PUT_LINE('이름 : ' || EMP.EMP_NAME);
DBMS_OUTPUT.PUT_LINE('주민번호 : ' || EMP.EMP_NO);
DBMS_OUTPUT.PUT_LINE('이메일 : ' || EMP.EMAIL);
DBMS_OUTPUT.PUT_LINE('전화번호 : ' || EMP.PHONE);
DBMS_OUTPUT.PUT_LINE('부서 코드 : ' || EMP.DEPT_CODE);
DBMS_OUTPUT.PUT_LINE('직급 코드 : ' || EMP.JOB_CODE);
DBMS_OUTPUT.PUT_LINE('급여 : ' || TO_CHAR(EMP.SALARY, 'FML99,999,999'));
DBMS_OUTPUT.PUT_LINE('입사일 : ' || TO_CHAR(EMP.HIRE_DATE, 'YYYY"년" MM"월" DD"일"'));
END;
/
PL/SQL 실행부
1) 선택문
단일 IF
IF 조건식 THEN
실행 문장
END IF;
-- 사번을 입력받은 후 해당 사원의 사번, 이름, 급여, 보너스를 출력
-- 단, 보너스를 받지 않은 사원은 보너스 출력 전에 '보너스를 지급받지 않은 사원입니다.'라는 문구를 출력한다.
DECLARE
EID EMPLOYEE.EMP_ID%TYPE;
ENAME EMPLOYEE.EMP_NAME%TYPE;
SALARY EMPLOYEE.SALARY%TYPE;
BONUS EMPLOYEE.BONUS%TYPE;
BEGIN
SELECT EMP_ID, EMP_NAME, SALARY, BONUS
INTO EID, ENAME, SALARY, BONUS
FROM EMPLOYEE
WHERE EMP_ID = '&사번';
DBMS_OUTPUT.PUT_LINE('사번 : ' || EID);
DBMS_OUTPUT.PUT_LINE('이름 : ' || ENAME);
DBMS_OUTPUT.PUT_LINE('급여 : ' || SALARY);
IF (BONUS IS NULL) THEN
DBMS_OUTPUT.PUT_LINE('보너스를 지급받지 않는 사원입니다.');
END IF;
DBMS_OUTPUT.PUT_LINE('보너스 : ' || BONUS);
END;
/
IF ~ ELSE
IF 조건식 THEN
실행 문장
ELSE
실행 문장
END IF;
-- 위의 PL/SQL 구문은 IF ~ ELSE 문으로 변경하여 작성
DECLARE
EID EMPLOYEE.EMP_ID%TYPE;
ENAME EMPLOYEE.EMP_NAME%TYPE;
SALARY EMPLOYEE.SALARY%TYPE;
BONUS EMPLOYEE.BONUS%TYPE;
BEGIN
SELECT EMP_ID, EMP_NAME, SALARY, NVL(BONUS, 0)
INTO EID, ENAME, SALARY, BONUS
FROM EMPLOYEE
WHERE EMP_ID = '&사번';
DBMS_OUTPUT.PUT_LINE('사번 : ' || EID);
DBMS_OUTPUT.PUT_LINE('이름 : ' || ENAME);
DBMS_OUTPUT.PUT_LINE('급여 : ' || SALARY);
-- IF (BONUS IS NULL) THEN
IF (BONUS = 0) THEN
DBMS_OUTPUT.PUT_LINE('보너스를 지급받지 않는 사원입니다.');
ELSE
DBMS_OUTPUT.PUT_LINE('보너스율 : ' || BONUS * 100 || '%');
END IF;
END;
-- 사번을 입력 받아서 해당 사원의 사번, 이름, 부서명, 국가 코드를 조회한 후 출력
-- 단, 국가 코드가 'KO'이면 국내팀 그 외는 해외팀으로 출력한다.
DECLARE
EID EMPLOYEE.EMP_ID%TYPE;
ENAME EMPLOYEE.EMP_NAME%TYPE;
DTITLE DEPARTMENT.DEPT_TITLE%TYPE;
NCODE LOCATION.NATIONAL_CODE%TYPE;
BEGIN
SELECT E.EMP_ID,
E.EMP_NAME,
D.DEPT_TITLE,
L.NATIONAL_CODE
INTO EID, ENAME, DTITLE, NCODE
FROM EMPLOYEE E
INNER JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
INNER JOIN LOCATION L ON (D.LOCATION_ID = L.LOCAL_CODE)
WHERE EMP_ID = '203';
DBMS_OUTPUT.PUT_LINE('사번 : ' || EID);
DBMS_OUTPUT.PUT_LINE('이름 : ' || ENAME);
DBMS_OUTPUT.PUT_LINE('부서명 : ' || DTITLE);
IF (NCODE = 'KO') THEN
DBMS_OUTPUT.PUT_LINE('소속 : 국내팀');
ELSE
DBMS_OUTPUT.PUT_LINE('소속 : 해외팀');
END IF;
END;
/
IF ~ ELSIF ~ ELSE
- 개수는 제한이 없다.
IF 조건식 THEN
실행 문장
ELSIF 조건식 THEN
실행 문장
...
ELSE
실행 문장
END IF;
-- 사용자에게 점수를 입력 받아서 SCORE 변수에 저장한 후 학점은 입력된 점수에 따라 GRADE 변수에 저장한다.
-- 90점 이상은 'A'학점
-- 80점 이상은 'B'학점
-- 70점 이상은 'C'학점
-- 60점 이상은 'D'학점
-- 60점 미만은 'F'학점
-- 출력은 '당신의 점수는 95점이고, 학점은 A학점입니다.'와 같이 출력한다.
DECLARE
SCORE NUMBER;
GRADE CHAR(1);
BEGIN
SCORE := '&점수';
IF (SCORE <= 100 AND SCORE >= 0) THEN
IF(SCORE >= 90) THEN
GRADE := 'A';
ELSIF (SCORE >= 80) THEN
GRADE := 'B';
ELSIF (SCORE >= 70) THEN
GRADE := 'C';
ELSIF (SCORE >= 60) THEN
GRADE := 'D';
ELSE
GRADE := 'F';
END IF;
DBMS_OUTPUT.PUT_LINE('당신의 점수는 ' || SCORE || '점이고, 학점은 ' || GRADE || '학점입니다.');
ELSE
DBMS_OUTPUT.PUT_LINE('점수를 잘못 입력하였습니다.');
END IF;
----------------------------------------------------------------------------
-- IF (SCORE > 100 OR SCORE < 0) THEN
-- DBMS_OUTPUT.PUT_LINE('점수를 잘못 입력하였습니다.');
--
-- RETURN;
-- END IF;
--
-- IF(SCORE >= 90) THEN
-- GRADE := 'A';
-- ELSIF (SCORE >= 80) THEN
-- GRADE := 'B';
-- ELSIF (SCORE >= 70) THEN
-- GRADE := 'C';
-- ELSIF (SCORE >= 60) THEN
-- GRADE := 'D';
-- ELSE
-- GRADE := 'F';
-- END IF;
----------------------------------------------------------------------------
-- IF (SCORE > 100 OR SCORE < 0) THEN
-- DBMS_OUTPUT.PUT_LINE('점수를 잘못 입력하였습니다.');
--
-- RETURN;
-- ELSIF(SCORE >= 90) THEN
-- GRADE := 'A';
-- ELSIF (SCORE >= 80) THEN
-- GRADE := 'B';
-- ELSIF (SCORE >= 70) THEN
-- GRADE := 'C';
-- ELSIF (SCORE >= 60) THEN
-- GRADE := 'D';
-- ELSE
-- GRADE := 'F';
-- END IF;
-- DBMS_OUTPUT.PUT_LINE('당신의 점수는 ' || SCORE || '점이고, 학점은 ' || GRADE || '학점입니다.');
END;
/
-- 사용자에게 입력받은 사번과 일치하는 사원의 급여 조회 후 출력한다.
-- 500만원 이상이면 '고급'
-- 300만원 이상이면 '중급'
-- 300만원 미만이면 '초급'
-- 출력은 '해당 사원의 급여 등급은 고급입니다.'와 같이 출력한다.
DECLARE
SALARY EMPLOYEE.SALARY%TYPE;
GRADE VARCHAR2(10);
BEGIN
SELECT SALARY
INTO SALARY
FROM EMPLOYEE
WHERE EMP_ID = 200;
IF (SALARY >= 5000000) THEN
GRADE := '고급';
ELSIF (SALARY >= 3000000) THEN
GRADE := '중급';
ELSE
GRADE := '초급';
END IF;
DBMS_OUTPUT.PUT_LINE('해당 사원의 급여 등급은 ' || GRADE || '입니다.');
END;
/
-- 실행부에 SELECT 구문 여러개 넣기
DECLARE
SAL EMPLOYEE.SALARY%TYPE;
GRADE SAL_GRADE.SAL_LEVEL%TYPE;
BEGIN
SELECT SALARY
INTO SAL
FROM EMPLOYEE
WHERE EMP_ID = '&사번';
SELECT SAL_LEVEL
INTO GRADE
FROM SAL_GRADE
WHERE SAL BETWEEN MIN_SAL AND MAX_SAL;
DBMS_OUTPUT.PUT_LINE('해당 사원의 급여 등급은 ' || GRADE || '입니다.');
END;
/
CASE
CASE 비교 대상
WHEN 비교값 1 THEN 결과값 1
WHEN 비교값 2 THEN 결과값 2
...
[ELSE 결과값]
END;
-- 사용자로부터 사번을 입력받은 후에 사원의 모든 컬럼에 데이터를 EMP에 대입하고 DEPT_CODE에 따라 알맞은 부서를 출력한다.
DECLARE
EMP EMPLOYEE%ROWTYPE;
DTITLE VARCHAR2(30);
BEGIN
SELECT *
INTO EMP
FROM EMPLOYEE
WHERE EMP_ID = '&사번';
DTITLE := CASE EMP.DEPT_CODE
WHEN 'D1' THEN '인사관리부'
WHEN 'D2' THEN '회계관리부'
WHEN 'D3' THEN '마케팅부'
WHEN 'D4' THEN '국내영업부'
WHEN 'D5' THEN '해외영업1부'
WHEN 'D6' THEN '해외영업2부'
WHEN 'D7' THEN '해외영업3부'
WHEN 'D8' THEN '기술지원부'
WHEN 'D9' THEN '총무부'
ELSE '부서없음'
END;
DBMS_OUTPUT.PUT_LINE('사번 : ' || EMP.EMP_ID);
DBMS_OUTPUT.PUT_LINE('이름 : ' || EMP.EMP_NAME);
DBMS_OUTPUT.PUT_LINE('부서 코드 : ' || EMP.DEPT_CODE);
DBMS_OUTPUT.PUT_LINE('부서명 : ' || DTITLE);
END;
/
2) 반복문
BASIC LOOP
LOOP
반복적으로 실행시킬 구문
[반복문을 빠져나갈 조건문 작성]
1) IF 조건식 THEN EXIT; END IF;
2) EIXT WHEN 조건식;
END LOOP;
-- 1 ~ 5까지 순차적으로 1씩 증가하는 값을 출력
DECLARE
NUM NUMBER := 1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(NUM);
NUM := NUM + 1;
-- IF NUM > 5 THEN
-- EXIT;
-- END IF;
EXIT WHEN NUM > 10;
END LOOP;
END;
/
WHILE LOOP
WHILE 조건식
LOOP
반복적으로 실행할 구문;
END LOOP;
-- 1 ~ 5까지 순차적으로 1씩 증가하는 값을 출력
DECLARE
NUM NUMBER := 1;
BEGIN
WHILE NUM <= 5
LOOP
DBMS_OUTPUT.PUT_LINE(NUM);
NUM := NUM + 1;
END LOOP;
END;
/
-- 구구단 출력 (2 ~ 9단)
DECLARE
DAN NUMBER := 2;
SU NUMBER;
RESULT NUMBER;
BEGIN
WHILE DAN <= 9
LOOP
SU := 1;
WHILE SU <= 9
LOOP
RESULT := DAN * SU;
DBMS_OUTPUT.PUT_LINE(DAN || ' X ' || SU || ' = ' || RESULT);
SU := SU + 1;
END LOOP;
DBMS_OUTPUT.PUT_LINE(' ');
DAN := DAN + 1;
END LOOP;
END;
/
FOR LOOP
FOR 변수 IN [REVERSE] 초기값..최종값
LOOP
반복적으로 실행할 구문;
END LOOP;
-- 1 ~ 5까지 순차적으로 1씩 증가하는 값을 출력
BEGIN
FOR NUM IN 1..5
LOOP
DBMS_OUTPUT.PUT_LINE(NUM);
END LOOP;
END;
/
-- 역순으로 출력
BEGIN
FOR NUM IN REVERSE 1..5
LOOP
DBMS_OUTPUT.PUT_LINE(NUM);
END LOOP;
END;
/
-- 구구단(2 ~ 9단) 출력 단, 짝수단만 출력한다.
BEGIN
FOR DAN IN 2..9
LOOP
IF (MOD(DAN, 2) = 0) THEN
FOR SU IN 1..9
LOOP
DBMS_OUTPUT.PUT_LINE(DAN || ' X ' || SU || ' = ' || DAN * SU);
END LOOP;
END IF;
DBMS_OUTPUT.PUT_LINE('');
END LOOP;
END;
/
-- 반복문을 이용한 데이터 삽입
-- 실습 테이블 만들기
DROP TABLE TEST;
CREATE TABLE TEST (
NUM NUMBER,
CREATE_DATE DATE
);
TRUNCATE TABLE TEST;
SELECT * FROM TEST;
ROLLBACK;
-- TEST 테이블에 10개의 행을 INSERT 하는 PL/SQL 작성
BEGIN
FOR NUM IN 1..10
LOOP
INSERT INTO TEST VALUES(NUM, SYSDATE);
IF (MOD(NUM, 2) = 0) THEN
COMMIT;
ELSE
ROLLBACK;
END IF;
END LOOP;
END;
/
SELECT * FROM TEST;
PL/SQL 예외 처리부
- PL/SQL 문에서 발생한 예외를 예외 처리부에서 처리가 가능하다.
DECLARE
...
BEGIN
...
EXCEPTION
WHEN 예외명 1 THEN 예외 처리 구문 1;
WHEN 예외명 2 THEN 예외 처리 구문 2;
...
WHEN OTHERS THEN 예외 처리 구문;
END
오라클에서 미리 정의되어 있는 예외
NO_DATA_FOUND
- SELECT 문의 수행 결과가 한 행도 없을 경우에 발생한다.
TOO_MANY_ROWS
- 한 행이 리턴되어야 하는데 SELECT 문에서 여러 개의 행을 반환할 때 발생한다.
SELECT EMP_ID, MANAGER_ID
FROM EMPLOYEE
WHERE MANAGER_ID = 200;
DECLARE
EID EMPLOYEE.EMP_ID%TYPE;
MID EMPLOYEE.MANAGER_ID%TYPE;
BEGIN
SELECT EMP_ID, MANAGER_ID
INTO EID, MID
FROM EMPLOYEE
WHERE MANAGER_ID = &사수사번;
DBMS_OUTPUT.PUT_LINE('사번 : ' || EID);
DBMS_OUTPUT.PUT_LINE('사수 사번 : ' || MID);
EXCEPTION
WHEN TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE('너무 많은 행이 조회되었습니다. ');
WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('조회 결과가 없습니다.');
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('오류가 발생했습니다.');
END;
/
ZERO_DIVIDE
- 숫자를 0으로 나눌 때 발생한다.
-- 사용자가 입력한 수로 나눗셈 연산 결과를 출력
DECLARE
RESULT NUMBER;
BEGIN
RESULT := 10 / &숫자;
DBMS_OUTPUT.PUT_LINE('결과 : ' || RESULT);
EXCEPTION
WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('나누기 연산시 O으로 나눌 수 없습니다.');
END;
/
DUP_VAL_ON_INDEX
- UNIQUE 제약조건을 가진 컬럼에 중복된 데이터가 INSERT 될 때 발생한다.
BEGIN
UPDATE EMPLOYEE
SET EMP_ID = 200
WHERE EMP_NAME = '&이름';
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN DBMS_OUTPUT.PUT_LINE('이미 존재하는 사번입니다.');
-- WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('오류가 발생했습니다.');
END;
/
'DB > SQL' 카테고리의 다른 글
OBJECT - TRIGGER (0) | 2022.09.14 |
---|---|
OBJECT - CURSOR (0) | 2022.09.13 |
OBJECT - FUNCTION (0) | 2022.09.12 |
OBJECT - PROCEDURE (0) | 2022.09.11 |
OBJECT - SYNONYM (0) | 2022.09.10 |