: Procedureal Language
: SQL을 기초로 한 절차적 언어
: SET SERVEROUTPUT ON;
: BEGIN ~ END; /
: DECLARE ~ BEGIN ~ END; /
▶ 기본 출력
: DBMS_OUTPUT.PUT_LINE()
-- 서버의 작업 결과 출력 구문
SET SERVEROUTPUT ON;
-- BEGIN ~ END; / : 기본 형태
BEGIN
DBMS_OUTPUT.PUT_LINE('HELLO');
END;
/
출력 결과
HELLO
▶ 변수 선언
: DECLARE [변수 선언 구문]
: 대입 연산자(:=) 사용으로 우항의 값을 좌항에 대입 가능
DECLARE
ENO NUMBER(4);
ENM VARCHAR2(10);
BEGIN
ENO := 9999;
ENM := 'CHARLES';
DBMS_OUTPUT.PUT_LINE(ENO || ' ' || ENM);
END;
/
출력 결과
9999 CHARLES
▶ USER DEFINED DATA TYPE
: TABLE_NAME.COLUMN_NAME%TYPE 을 통해 해당 COLUMN의 DATA TYPE 지정
DECLARE
ENO EMP.EMPNO%TYPE;
ENM EMP.ENAME%TYPE;
BEGIN
ENO := 9999;
ENM := 'CHARLES';
DBMS_OUTPUT.PUT_LINE(ENO || ' ' || ENM);
END;
/
출력 결과
9999 CHALRES
▶ SELECT를 통해 DATA 입력
: SELECT ~ INTO ~ FROM ~
: SELECT 로 선택한 COLUMN 순서에 맞게 INTO 뒤에 변수 입력
DECLARE
ENO EMP.EMPNO%TYPE;
ENM EMP.ENAME%TYPE;
BEGIN
SELECT EMPNO, ENAME INTO ENO, ENM FROM EMP WHERE ENAME = 'SMITH';
DBMS_OUTPUT.PUT_LINE(ENO || ' ' || ENM);
END;
/
출력 결과
7369 SMITH
▶ 다수의 SELECT 사용
DECLARE
ENM EMP.ENAME%TYPE;
DNM DEPT.DNAME%TYPE;
BEGIN
SELECT ENAME INTO ENM FROM EMP WHERE EMPNO = 7499;
SELECT DNAME INTO DNM FROM DEPT WHERE DEPTNO = 20;
DBMS_OUTPUT.PUT_LINE(ENM || ' ' || DNM);
END;
/
출력 결과
ALLEN RESERACH
▶ SELECT 결과를 이용해 다시 SELECT
DECLARE
DNO EMP.DEPTNO%TYPE;
DNM DEPT.DNAME%TYPE;
BEGIN
SELECT DEPTNO INTO DNO FROM EMP WHERE ENAME = 'SMITH';
SELECT DNAME INTO DNM FROM DEPT WHERE DEPTNO = DNO;
DBMS_OUTPUT.PUT_LINE('SMITH : ' || DNO || ' : ' || DNM);
END;
/
출력 결과
SMITH : 20 : RESEARCH
▶ IF문
: IF(조건) THEN ~ ELSIF(조건) THEN ~ ELSE ~ END IF;
DECLARE
DNO EMP.DEPTNO%TYPE;
DNM DEPT.DNAME%TYPE;
BEGIN
SELECT DEPTNO INTO DNO FROM EMP WHERE ENAME = 'MILLER';
IF(DNO = 10) THEN DNM := '회계';
ELSIF(DNO = 20) THEN DNM := '기획';
ELSE DNM := '영업';
END IF;
DBMS_OUTPUT.PUT_LINE('MILLER : ' || DNO || ' : ' || DNM);
END;
/
출력 결과
MILLER : 10 : 회계
▶ 반복문
: LOOP / FOR / WHILE
▷ LOOP
: LOOP ~ END LOOP;
: 탈출 조건 - IF() THEN EXIT; END IF;
DECLARE
LOOPCNT NUMBER(2) := 1; -- 변수 선언 및 초기화
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(LOOPCNT);
LOOPCNT := LOOPCNT + 1;
IF(LOOPCNT > 10) THEN EXIT;
END IF;
END LOOP;
END;
/
출력 결과
1 2 3 4 5 6 7 8 9 10
▷ FOR
: FOR ~ IN ~ LOOP ~ END LOOP;
: N..M - N부터 M까지 1씩 증가
DECLARE
LOOPCNT NUMBER(2) := 1; -- 변수 선언 및 초기화
BEGIN
-- FOR 문에서의 LOOPCNT는 새로 선언되는 지역 변수
FOR LOOPCNT IN 1..10 LOOP
DBMS_OUTPUT.PUT_LINE(LOOPCNT);
END LOOP;
END;
/
출력 결과
1 2 3 4 5 6 7 8 9 10
▷ WHILE
: WHILE() ~ LOOP ~ END LOOP;
DECLARE
LOOPCNT NUMBER(2) := 1; -- 변수 선언 및 초기화
BEGIN
WHILE LOOPCNT < 11 LOOP
DBMS_OUTPUT.PUT_LINE(LOOPCNT);
LOOPCNT := LOOPCNT + 1;
END LOOP;
END;
/
출력 결과
1 2 3 4 5 6 7 8 9 10
▶ 외부에서 DATA 입력
: FUNCTION과 PROCEDURE에서 유용
: 외부 입력키(&) 사용
DECLARE
TMPINT NUMBER(5);
TMPSTR VARCHAR2(10);
BEGIN
TMPINT := &NUM;
TMPSTR := '&STR';
DBMS_OUTPUT.PUT_LINE(TMPINT);
DBMS_OUTPUT.PUT_LINE(TMPSTR);
END;
/
출력 결과
29
STRING
DECLARE
STR EMP.ENAME%TYPE;
DNO EMP.DEPTNO%TYPE;
DNM DEPT.DNAME%TYPE;
BEGIN
STR := '&NAME';
SELECT DEPTNO INTO DNO FROM EMP WHERE ENAME = STR;
SELECT DNAME INTO DNM FROM DEPT WHERE DEPTNO = DNO;
DBMS_OUTPUT.PUT_LINE(STR || ' ' || DNO || ' ' || DNM);
END;
/
출력 결과
KING 10 ACCOUNTING
DECLARE
STR EMP.ENAME%TYPE;
DNM DEPT.DNAME%TYPE;
BEGIN
STR := '&NAME';
SELECT DNAME INTO DNM FROM DEPT
WHERE DEPTNO = (SELECT DEPTNO FROM EMP WHERE ENAME = STR);
DBMS_OUTPUT.PUT_LINE(STR || ' ' || DNM);
END;
/
출력 결과
SMITH RESEARCH
DECLARE
STR EMP.ENAME%TYPE;
DNM DEPT.DNAME%TYPE;
BEGIN
STR := '&NAME';
SELECT DNAME INTO DNM FROM EMP, DEPT
WHERE EMP.DEPTNO = DEPT.DEPTNO
AND ENAME = STR;
DBMS_OUTPUT.PUT_LINE(STR || ' ' || DNM);
END;
/
출력 결과
WARD SALES
▶ PROCEDURE
: PL/SQL을 DBMS에 저장했다가 사용하는 객체
: 복잡한 업무용
-- PROC_TEST1 컴파일
CREATE OR REPLACE PROCEDURE PROC_TEST1(INPUTSTR IN VARCHAR2)
IS BEGIN
DBMS_OUTPUT.PUT_LINE(INPUTSTR);
END;
/
-- PROC_TEST1 실행
EXECUTE PROC_TEST1('HELLO WORLD');
출력 결과
HELLO WORLD
CREATE OR REPLACE PROCEDURE PROC_TEST2 (NUM1 IN NUMBER, NUM2 IN NUMBER)
-- PROCEDURE 내에서 사용할 변수 선언
IS RESNUM NUMBER;
BEGIN
RESNUM := NUM1 + NUM2;
DBMS_OUTPUT.PUT_LINE('NUM1 + NUM2 = ' || RESNUM);
END;
/
EXECUTE PROC_TEST2(3, 5);
출력 결과
NUM1 + NUM2 = 8
CREATE OR REPLACE PROCEDURE CALC_TEST (NUM1 IN NUMBER, GIHO IN VARCHAR2, NUM2 IN NUMBER)
IS RESNUM NUMBER;
BEGIN
IF(GIHO = '+') THEN RESNUM := NUM1 + NUM2;
ELSIF (GIHO = '-') THEN RESNUM := NUM1 - NUM2;
ELSIF (GIHO = 'X') THEN RESNUM := NUM1 * NUM2;
ELSIF (GIHO = '/') THEN RESNUM := NUM1 / NUM2;
ELSIF (GIHO = '%') THEN RESNUM := MOD(NUM1, NUM2);
ELSE DBMS_OUTPUT.PUT_LINE('WRONG INPUT');
END IF;
DBMS_OUTPUT.PUT_LINE(NUM1 || ' ' || GIHO || ' ' || NUM2 || ' = ' || RESNUM);
END;
/
EXECUTE CALC_TEST(3, '%', 4);
출력 결과
3 % 4 = 3
▶ FUNCTION
: PL/SQL을 DBMS에 저장했다가 사용하는 객체 (기능적으로 PROCEDURE와 동일)
: 간단한 기능용
CREATE OR REPLACE FUNCTION FUNC_TEST1 RETURN NUMBER
IS intToOut NUMBER;
BEGIN
INTTOOUT := 213;
RETURN INTTOOUT;
END;
/
VARIABLE IVAR NUMBER;
EXECUTE : IVAR := FUNC_TEST1;
PRINT IVAR;
SELECT FUNC_TEST1 FROM DUAL;
출력 결과
IVAR FUNC_TEST1 ---------- ---------- 213 213
CREATE OR REPLACE FUNCTION SEARCHDNAME(ENM IN VARCHAR2) RETURN VARCHAR2
IS DNM DEPT.DNAME%TYPE;
BEGIN
SELECT DNAME INTO DNM FROM DEPT
WHERE DEPTNO = (SELECT DEPTNO FROM EMP WHERE ENAME = ENM);
RETURN DNM;
END;
/
SELECT SEARCHDNAME('ADAMS') FROM DUAL;
출력 결과
SEARCHDNAME('ADAMS') -------------------- RESEARCH
: 숫자 2개와 기호 1개를 입력받아 계산하는 FUNCTION 작성
CREATE OR REPLACE FUNCTION FUNC_CALC(NUM1 IN NUMBER, GIHO IN VARCHAR2, NUM2 IN NUMBER) RETURN NUMBER
IS RESNUM NUMBER;
BEGIN
IF (GIHO = '+') THEN RESNUM := NUM1 + NUM2;
ELSIF (GIHO = '-') THEN RESNUM := NUM1 - NUM2;
ELSIF (GIHO = 'X') THEN RESNUM := NUM1 * NUM2;
ELSIF (GIHO = '/') THEN RESNUM := NUM1 / NUM2;
ELSIF (GIHO = '%') THEN RESNUM := MOD(NUM1, NUM2);
ELSE DBMS_OUTPUT.PUT_LINE('WRONG INPUT'); -- NULL 출력
END IF;
RETURN RESNUM;
END;
/
SELECT FUNC_CALC(4, '/', 4) RESULT FROM DUAL;
출력 결과
RESULT ---------- 1
: java의 ResultSet과 같은 역할
CREATE OR REPLACE PROCEDURE TEST_CURSOR1
IS
DEPTROW DEPT%ROWTYPE;
CURSOR CSR IS SELECT * FROM DEPT;
BEGIN
OPEN CSR;
FETCH CSR INTO DEPTROW;
DBMS_OUTPUT.PUT_LINE(DEPTROW.DEPTNO || ' ' || DEPTROW.DNAME || ' ' || DEPTROW.LOC);
END;
/
EXECUTE TEST_CURSOR1;
출력 결과
10 ACCOUNTING NEW YORK
CREATE OR REPLACE PROCEDURE TEST_CURSOR2
IS
DEPTROW DEPT%ROWTYPE;
CURSOR CSR IS SELECT * FROM DEPT;
BEGIN
OPEN CSR;
LOOP
FETCH CSR INTO DEPTROW;
EXIT WHEN CSR%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(DEPTROW.DEPTNO || ' ' || DEPTROW.DNAME || ' ' || DEPTROW.LOC);
END LOOP;
CLOSE CSR;
END;
/
EXECUTE TEST_CURSOR2;
출력 결과
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
: 하나의 테이블에서 변경이 생겼을 때, 관련 있는 다른 테이블에 변경을 적용하는 기술
: INSERT, UPDATE, DELETE 등의 DML 문이 수행될 때, 자동(DB)으로 추가적 변경
: 새롭게 추가, 변경된 값(:NEW.COLUMN_NAME)과 변경 이전의 값(:OLD.COLUMN_NAME) 활용
▷ SETTING
CREATE TABLE TRI_SALES ( SALENO NUMBER(3)
, PRODNO NUMBER(3)
, SALEQTY NUMBER(5));
CREATE TABLE TRI_STOCK ( PRODNO NUMBER(3)
, PRODNM VARCHAR2(60)
, PRODQTY NUMBER(5));
INSERT INTO TRI_STOCK VALUES(1, '새우깡', 100);
INSERT INTO TRI_STOCK VALUES(2, '포카칩', 100);
INSERT INTO TRI_STOCK VALUES(3, '감자깡', 100);
COMMIT;
▷ INSERT TRIGGER
: 판매된 수량만큼 재고량을 변경
→ (변경된 재고량) = (기존 재고량) - (판매 수량)
CREATE OR REPLACE TRIGGER TRI_INS
-- TRIGGER 수행 시점 지정 : TRI_SALES 각 행에서 INSERT 수행 이후
AFTER INSERT ON TRI_SALES FOR EACH ROW
BEGIN
UPDATE TRI_STOCK SET PRODQTY = PRODQTY - :NEW.SALEQTY
WHERE PRODNO = :NEW.PRODNO;
END;
/
INSERT INTO TRI_SALES VALUES(1, 2, 5);
실행 결과
PRODNO PRODNM PRODQTY ---------- --------- ---------- 1 새우깡 100 2 포카칩 95 3 감자깡 100
▷ UPDATE TRIGGER
: 부분 반품된 수량만큼 재고량을 변경
→ (변경된 재고량) = (기존 재고량) + (기존 판매 수량) - (최종 판매 수량)
CREATE OR REPLACE TRIGGER TRI_UPD
AFTER UPDATE ON TRI_SALES FOR EACH ROW
BEGIN
-- :OLD.COLUMN_NAME - 수정 전 데이터 값 :NEW.COLUMN_NAME - 수정 후 데이터 값
UPDATE TRI_STOCK SET PRODQTY = PRODQTY + :OLD.SALEQTY - :NEW.SALEQTY
WHERE PRODNO = :OLD.PRODNO;
END;
/
UPDATE TRI_SALES SET SALEQTY = 3 WHERE SALENO = 1;
실행 결과
PRODNO PRODNM PRODQTY ---------- --------- ---------- 1 새우깡 100 2 포카칩 97 3 감자깡 100
▷ DELETE TRIGGER
: 반품된 수량만큼 재고량을 변경
→ (변경된 재고량) = (기존 재고량) + (기존 판매 수량)
CREATE OR REPLACE TRIGGER TRI_DEL
AFTER DELETE ON TRI_SALES FOR EACH ROW
BEGIN
UPDATE TRI_STOCK SET PRODQTY = PRODQTY + :OLD.SALEQTY
WHERE PRODNO = :OLD.PRODNO;
END;
/
DELETE FROM TRI_SALES WHERE SALENO = 1;
실행 결과
PRODNO PRODNM PRODQTY ---------- --------- ---------- 1 새우깡 100 2 포카칩 100 3 감자깡 100
: 예외 처리
: EXCEPTION WHEN ERROR_NAME THEN ~
DECLARE
DNM DEPT.DNAME%TYPE;
BEGIN
SELECT DNAME INTO DNM FROM DEPT
WHERE DEPTNO = (SELECT DEPTNO FROM EMP);
DBMS_OUTPUT.PUT_LINE(DNM);
EXCEPTION WHEN OTHERS
THEN DBMS_OUTPUT.PUT_LINE('CHECK EXCEPTION');
END;
/
출력 결과
CHECK EXCEPTION