[Oracle] 34.PL/SQL


PL/SQL 구조



PL/SQL의 구조는 다음과 같습니다.


DECLARE

--PL/SQL에서 사용하는 모든 변수나 상수를 선언하는 부분

BEGIN

--절차적 형식으로 SQL문을 실행할 수 있도록 절차적 언어의 요소인 제어문, 반복문, 함수 정의 등 로직을 기술할 수 있는 부분

EXCEPTION

--예외를 처리하는 부분

END;
/



PL/SQL을 작성하는 요령은 다음과 같습니다.


1.PL / SQL 블록내에서는 한 문장이 종료할 때마다 세미콜론(;)을 사용


2.END뒤에 ;을 사용하여 하나의 블록이 끝났다는 것을 명시


3.PL / SQL 블록의 작성은 편집기를 통해 파일로 작성할 수도 있고, 프롬프트에서 바로 작성


4.SQL*PLUS환경에서는 DELCLARE나 BEGIN이라는 키워드로 PL/SQL블럭이 시작


5.단일행 주석은 --이고 여러행 주석 /* */


6.쿼리문을 수행하기 위해서 /가 반드시 입력되어야 PL/SQL 블록은 행에 / 가 있으면 종결된 것으로 간주



한 번 PL/SQL을 사용해서 간단한 메시지를 출력해볼까요?


-- 오라클의 환경 변수 SERVEROUTPUT는 오라클에서 제공해주는 프로시저를 사용해서 출력

SET SERVEROUTPUT ON 




--PL/SQL

BEGIN
	dbms_output.put_line('Hello world!'); -- 화면 출력 프로시저
END;
/ -- 슬러쉬 안하면 연속적으로 실행됨(끝을 알수 없음) 




image



변수 선언, 할당, 출력




  • 스칼라변수: SQL에서 사용하던 자료형과 유사하게 선언하는 것




DECLARE 
    VEMPNO NUMBER(4);
    VENAME VARCHAR2(10);
BEGIN
    VEMPNO:=7788;
    VENAME:='SCOTT';
    DBMS_OUTPUT.PUT_LINE('사번 / 이름');
    DBMS_OUTPUT.PUT_LINE('==============');
    DBMS_OUTPUT.PUT_LINE(VEMPNO|| '/' ||VENAME);
END;
/




image





  • 레퍼런스 변수: 테이블 속성의 자료형과 크기를 그대로 참조해서 정의: 테이블의 정보를 수정하면 자동으로 수정됨




--PL/SQL 에서 SELECT 문

DECLARE
    ENAME EMP.LAST_NAME%TYPE;
    SAL EMP.SALARY%TYPE;
BEGIN
    SELECT LAST_NAME,SALARY
    INTO ENAME,SAL --대입(데이터형이 알맞아야하고, select 결과가 1행이어야함)
    FROM EMPLOYEES
    WHERE EMPLOYEE_ID=150;
    DBMS_OUTPUT.PUT_LINE(ENAME||'의 급여는'||SAL||'입니다.');
END;
/




image





  • ROWTYPE 변수 : 테이블 전체의 속성타입을 자료형으로 함




DECLARE
    EMP_ROW EMPLOYEES%ROWTYPE; --EMP의 전체 속성타입을 변수로 잡음
BEGIN
    SELECT *
    INTO EMP_ROW
    FROM EMPLOYEES
    WHERE EMPLOYEE_ID=150;
    DBMS_OUTPUT.PUT_LINE(EMP_ROW.EMPLOYEE_ID||'의 이름은 '||EMP_ROW.FIRST_NAME||' '||EMP_ROW.LAST_NAME);
END;
/




image





IF 문




  • 부서명 출력예제(IF문 사용)




DECLARE
    VEMPNO NUMBER(4);
    VENAME VARCHAR2(20);
    VDEPTNO EMP.DEPARTMENT_ID%TYPE;
    VDNAME VARCHAR2(20) :=NULL;
BEGIN
    SELECT EMPLOYEE_ID,FIRST_NAME||' '||LAST_NAME,DEPARTMENT_ID
    INTO VEMPNO, VENAME, VDEPTNO
    FROM EMPLOYEES
    WHERE EMPLOYEE_ID=200;
    IF(VDEPTNO=10) THEN
        VDNAME :='ACCOUNTING';
    ELSIF(VDEPTNO=20)THEN
        VDNAME := 'RESEARCH';
    ELSE
        VDNAME := '부서없음';
    END IF;
    DBMS_OUTPUT.PUT_LINE('사번    이름    부서명');
    DBMS_OUTPUT.PUT_LINE(VEMPNO||'    '||VENAME||'    '||VDNAME);
 END;
 /




image




  • 연봉 구하기 (IF문 사용)




DECLARE 
    VEMP EMPLOYEES%ROWTYPE;
    ANNSAL NUMBER(10,2);
BEGIN
    DBMS_OUTPUT.PUT_LINE('사번 / 이름 / 월급 / 연봉');
    DBMS_OUTPUT.PUT_LINE('-------------------------');
    SELECT *
    INTO VEMP
    FROM EMPLOYEES
    WHERE FIRST_NAME='Steven' and LAST_NAME='King';
    IF(VEMP.COMMISSION_PCT IS NULL) THEN --커미션이 null이면 연봉=월급*12
        ANNSAL := VEMP.SALARY*12;
    ELSE  --커미션이 null이 아니면 연봉=월급*12+커미션
        ANNSAL := VEMP.SALARY*12+VEMP.COMMISSION_PCT;
    END IF;
    DBMS_OUTPUT.PUT_LINE(VEMP.EMPLOYEE_ID||'/'||VEMP.LAST_NAME||'/'||VEMP.SALARY||'/'||ANNSAL);
END;
/




image





LOOP 문




  • 1부터 5까지 출력




DECLARE
     n   NUMBER := 1;
BEGIN
    LOOP
        dbms_output.put_line(n);
        n := n + 1;
        IF
            n > 5
        THEN
            EXIT;
        END IF;
    END LOOP;
 END;
 /




image




  • 구구단 출력




DECLARE
    DAN NUMBER :=2;
    I NUMBER :=1;
BEGIN
    LOOP
        LOOP
            dbms_output.put_line(DAN||'*'||I||'='||(DAN*I));
            I:=I+1;
            IF I>=10 THEN
                EXIT;
            END IF;
        END LOOP;
        I:=1;
        DAN:=DAN+1;
        IF DAN>=10 THEN
            EXIT;
        END IF;
    END LOOP;
END;
/




image





FOR LOOP




  • 1~5 출력(for문 이용)




--1~5 까지 출력

DECLARE
--for문의 인덱스로 사용되는 n을 따로 선언할 필요없다.
BEGIN
    FOR n IN 1..5 LOOP
        dbms_output.put_line(n);
    END LOOP;
END;
/




  • 부서정보 출력(FOR문에 SELECT문 적용)




DECLARE 
BEGIN
    dbms_output.put_line('부서번호/부서명/지역명');
    dbms_output.put_line('====================');
    
    --departments 를 조회해서 한행 씩 cnt에 대입
    FOR cnt IN (SELECT * FROM departments) LOOP  
        dbms_output.put_line(cnt.department_id|| '/'|| cnt.department_name|| '/'|| cnt.location_id);
    END LOOP;

END;
/




image




WHILE LOOP




  • 1부터 5출력




DECLARE
    n   NUMBER := 1;
BEGIN
    WHILE n <= 5 LOOP
        dbms_output.put_line(n);
        n := n + 1;
    END LOOP;
END;
/




  • 별찍기




DECLARE
    v_cnt   NUMBER := 1;
    v_str   VARCHAR2(10) := NULL;
BEGIN
    WHILE v_cnt < 10 LOOP
        v_str := v_str || '*';
        dbms_output.put_line(v_str);
        v_cnt := v_cnt + 1;
    END LOOP;
END;
/




image




배열처럼 사용하는 테이블 변수




  • 테이블 변수를 사용하여 EMP 테이블에서 이름과 업무를 출력




DECLARE
--테이블 타입을 정의

	--employees.last_name 타입을 배열처럼 담을 수 있는 테이블 생성
    TYPE ename_table_type IS
        TABLE OF employees.last_name%TYPE INDEX BY BINARY_INTEGER; 
        
    --employees.job_id 타입을 배열처럼 담을 수 있는 테이블 생성
    TYPE job_table_type IS
        TABLE OF employees.job_id%TYPE INDEX BY BINARY_INTEGER; 

-- 변수 선언
    ename_table   ename_table_type; -- 테이블 변수
    job_table     job_table_type; --테이블 변수
    i             BINARY_INTEGER := 0; --테이블 인덱스(1부터 시작)
BEGIN
    FOR emploop IN (SELECT last_name,job_id FROM employees) LOOP
        i := i + 1;
        ename_table(i) := emploop.last_name;
        job_table(i) := emploop.job_id;
    END LOOP;
    dbms_output.put_line('사원명'|| '/'|| '직책' );
    dbms_output.put_line('======================' );
    FOR j IN 1..i LOOP
        dbms_output.put_line(ename_table(j)|| '  /  '|| job_table(j));
    END LOOP;

END;
/




image





구조체처럼 사용하는 레코드 변수




DECLARE
    -- 레코드 타입 정의
    TYPE emp_record_type IS RECORD ( 
    v_empno      employees.employee_id%TYPE,
    v_ename      employees.first_name%TYPE,
    v_job        employees.job_id%TYPE,
    v_deptno     employees.department_id%TYPE );
    
    --레코드 변수 선언
    emp_record   emp_record_type;
BEGIN
    -- SCOTT 사원의 정보를 레코드 변수에 저장
    SELECT
        employee_id,
        first_name,
        job_id,
        department_id
    INTO emp_record
    FROM
        employees
    WHERE
        employee_id = 100;
    
    -- 레코드 변수에 저장된 사원 정보를 출력

    dbms_output.put_line('사원번호:' || TO_CHAR(emp_record.v_empno) );
    dbms_output.put_line('이   름:' || emp_record.v_ename);
    dbms_output.put_line('담당업무:' || emp_record.v_job);
    dbms_output.put_line('부서번호:' || TO_CHAR(emp_record.v_deptno) );
END;
/




image