코딩 펭귄 2022. 8. 13. 15:44
📅 2021.09.27 ~ 2021.09.28

PL/SQL

  • Procedural Language extension to SQL의 약자로 오라클 자체에 내장되어 있는 절차적 언어이다. SQL의 단점을 보완하여 SQL 문장 내에서 변수의 정의, 조건 처리, 반복 처리 등을 지원한다.
  • 구조
    • 선언부(DECLARE SECTION): DECLARE로 시작하며, 변수나 상수를 선언하는 부분이다.
    • 실행부(EXECUTABLE SECTION): BEGIN으로 시작하며, 제어문, 반복문, 함수 정의 등 로직을 기술한다.
    • 예외부(EXCEPTION SECTION): EXCEPTION으로 시작하며, 예외 사항 발생 시 해결하기 위한 문장을 기술한다.
  • 화면 출력
    BEGIN 
        DBMS_OUTPUT.PUT_LINE('HELLO WORLD');
    END;
    /

    화면 출력을 하기 위해서는 출력을 화면에 보여주도록 하는 설정을 먼저 진행하여야 한다. 해당 설정을 하지 않는 경우 위의 문장을 실행하여도 출력되지 않는다. 설정은 다음과 같이 한다.
    SET SERVEROUTPUT ON;
    END는 PL/SQL의 종료를 알리기 위해 작성하며, /는 구분을 위해 작성한다.
  • PL/SQL에서 대입할 경우에는 =를 사용하지 않고, :=을 사용한다. 또한 연결하는 경우에도 +가 아닌 ||을 사용한다.

 

선언부

  • 선언 시에는 변수명 자료형 순서로 선언을 하며, SELECT를 이용하여 가지고 온 값을 변수에 대입 가능하다.
    DECLARE
        EMP_ID NUMBER;
        EMP_NAME VARCHAR2(30);
    BEGIN
        SELECT EMP_ID, EMP_NAME
        INTO EMP_ID, EMP_NAME 
        FROM EMPLOYEE
        WHERE EMP_ID = 200;
        
        DBMS_OUTPUT.PUT_LINE('EMP_ID : ' || EMP_ID);
        DBMS_OUTPUT.PUT_LINE('EMP_NAME : ' || EMP_NAME);
    END;
    /

    이때 SELECT문에서 사용되는 EMP_ID, EMP_NAME과 변수로 선언된 EMP_ID, EMP_NAME은 이름만 같을 뿐 별개이기 때문에 자동으로 값이 해당 변수에 들어가지 않는다. 따라서 INTO를 이용하여 변수에 값을 대입해 주어야 하고, 이때 순서는 SELECT 순서와 동일하게 맞춰 주어야 원하는 값이 원하는 변수에 대입된다.
  • 가지고 오는 컬럼의 타입과 동일한 타입이어야 하는 경우에는 각 컬럼의 타입을 참조해서 쓰는 레퍼런스 변수를 사용한다.
    DECLARE
        EMP_ID EMPLOYEE.EMP_ID%TYPE; 
        EMP_NAME EMPLOYEE.EMP_NAME%TYPE;
  • 사용자에게 값을 입력받기 위해서는 '& '를 사용한다. & 뒤에는 입력받을 창에 출력할 문구를 작성한다. (ex. '&사번' ⇒ 사번 값 입력)
    DECLARE
        EMP_ID EMPLOYEE.EMP_ID%TYPE; 
        EMP_NAME EMPLOYEE.EMP_NAME%TYPE;
    BEGIN
        SELECT EMP_ID, EMP_NAME
        INTO EMP_ID, EMP_NAME 
        FROM EMPLOYEE
        WHERE EMP_ID = '&사번';
        
        DBMS_OUTPUT.PUT_LINE('EMP_ID : ' || EMP_ID);
        DBMS_OUTPUT.PUT_LINE('EMP_NAME : ' || EMP_NAME);
    END;
    /
  • 레퍼런스 변수를 여러 번 쓰는 경우 번거롭기 때문에 '%ROWTYPE'을 이용하여 한 행을 한 번에 참조할 수 있다. 행에 대한 데이터 형, 크기, 속성을 모두 참조한다. 하지만, 출력 시에는 변수명.컬럼명 형식으로 지정하여 출력하여야 한다.
    DECLARE
        E EMPLOYEE%ROWTYPE;
    BEGIN
        SELECT * 
        INTO E
        FROM EMPLOYEE
        WHERE EMP_ID = '&사번';
        
        DBMS_OUTPUT.PUT_LINE('EMP_ID : ' || E.EMP_ID);
        DBMS_OUTPUT.PUT_LINE('EMP_NAME : ' || E.EMP_NAME);  
    END;
    /

 

선택문

  • IF ~ THEN ~ END IF (단일 if문)
    DECLARE
        EMP_ID EMPLOYEE.EMP_ID%TYPE;
        EMP_NAME EMPLOYEE.EMP_NAME%TYPE;
        SALARY EMPLOYEE.SALARY%TYPE;
        BONUS EMPLOYEE.BONUS%TYPE;
    BEGIN 
        SELECT EMP_ID, EMP_NAME, SALARY, NVL(BONUS, 0)
        INTO EMP_ID, EMP_NAME, SALARY, BONUS
        FROM EMPLOYEE
        WHERE EMP_ID = '&사번';
        
        DBMS_OUTPUT.PUT_LINE('EMP_ID : ' || EMP_ID);
        DBMS_OUTPUT.PUT_LINE('EMP_NAME : ' || EMP_NAME);
        DBMS_OUTPUT.PUT_LINE('SALARY : ' || SALARY);
        
        IF(BONUS = 0)
            THEN DBMS_OUTPUT.PUT_LINE('보너스를 지급받지 않는 사원입니다');
        END IF;
        DBMS_OUTPUT.PUT_LINE('BONUS : ' || BONUS * 100 || '%');
    END;
    /
    NVL을 사용하지 않는 경우에는 IF 조건문을 (BONUS IS NULL)로 설정하면 된다.
  • IF ~ THEN ~ ELSE ~ END IF (if ~ else문)
    DECLARE
        EMP_ID EMPLOYEE.EMP_ID%TYPE;
        EMP_NAME EMPLOYEE.EMP_NAME%TYPE;
        DEPT_TITLE DEPARTMENT.DEPT_TITLE%TYPE;
        NATIONAL_CODE LOCATION.NATIONAL_CODE%TYPE;
        TEAM VARCHAR2(20);
    BEGIN
        SELECT EMP_ID, EMP_NAME, DEPT_TITLE, NATIONAL_CODE
        INTO EMP_ID, EMP_NAME, DEPT_TITLE, NATIONAL_CODE
        FROM EMPLOYEE
             LEFT JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID)
             LEFT JOIN LOCATION ON (LOCAL_CODE = LOCATION_ID)
        WHERE EMP_ID = '&사번';
        
        IF(NATIONAL_CODE = 'KO')
            THEN TEAM := '국내팀';
        ELSE
            TEAM := '해외팀';
        END IF;
        
        DBMS_OUTPUT.PUT_LINE('사번 : ' || EMP_ID);
        DBMS_OUTPUT.PUT_LINE('이름 : ' || EMP_NAME);
        DBMS_OUTPUT.PUT_LINE('부서 : ' || DEPT_TITLE);
        DBMS_OUTPUT.PUT_LINE('소속 : ' || TEAM);
    END;
    /
    IF 뒤에만 THEN이 붙는다. ELSE 뒤에는 붙지 않는다.
  • IF ~ THEN ~ ELSIF ~ THEN ~ ELSE ~ END IF (if ~ else if ~ else문)
    DECLARE
        SCORE NUMBER;
        GRADE CHAR(1);
    BEGIN
        SCORE := '&점수';
    
        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 || '학점입니다');
    END;
    /
    이때 IF문과 ELSIF문은 소괄호를 이용해 묶지 않고 IF SCORE >= 90 형태로 사용해도 된다.
  • CASE ~ WHEN ~ THEN ~ END (switch ~ case문)
    DECLARE 
    	E EMPLOYEE%ROWTTYPE;
    	DNAME DEPARTMENT.DEPT_TITLE%TYPE;
    BEGIN
        SELECT * INTO E
        FROM EMPLOYEE
        WHERE EMP_ID = '&EMP_ID';
        
            DNAME := CASE 
                    WHEN E.DEPT_CODE = 'D1' THEN '인사관리부'
                    WHEN E.DEPT_CODE = 'D2' THEN '회계관리부'
                    WHEN E.DEPT_CODE = 'D3' THEN '마케팅부'
                    WHEN E.DEPT_CODE = 'D4' THEN '국내영업부'
                    WHEN E.DEPT_CODE = 'D5' THEN '해외영업1부'
                    WHEN E.DEPT_CODE = 'D6' THEN '해외영업2부'
                    WHEN E.DEPT_CODE = 'D7' THEN '해외영업3부'
                    WHEN E.DEPT_CODE = 'D8' THEN '기술지원부'
                    WHEN E.DEPT_CODE = 'D9' THEN '총무부'
                    ELSE '배정X'
                END;
    	DBMS_OUTPUT.PUT_LINE(E.EMP_NAME || ' ' || E.EMP_ID || ' ' || DNAME);
    END;
    /
    비교할 부분(E.DEPT_CODE)을 CASE로 따로 빼서 아래와 같이 적어도 된다.
    DECLARE 
    	E EMPLOYEE%ROWTTYPE;
    	DNAME DEPARTMENT.DEPT_TITLE%TYPE;
    BEGIN
        SELECT * INTO E
        FROM EMPLOYEE
        WHERE EMP_ID = '&EMP_ID';
        
    			DNAME := CASE E.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 '배정X'
                    END;
    
    	DBMS_OUTPUT.PUT_LINE(E.EMP_NAME || ' ' || E.EMP_ID || ' ' || DNAME);
    END;
    /

 

반복문

  • BASIC LOOP: 내부에 처리문을 작성하며, 마지막에 LOOP를 벗어날 조건을 명시한다. 만약 LOOP 탈출 조건이 존재하지 않는 경우에는 계속 반복하다가 오버플로우가 발생하고 종료된다.
    LOOP
       처리문
       조건문
    END LOOP;
    반복문을 종료할 때는 IF문을 이용하거나 EXIT WHEN을 이용한다.
    DECLARE 
    	N NUMBER := 1;
    BEGIN
    	LOOP
    		DBMS_OUTPUT.PUT_LINE(N);
    		N := N + 1;
    		
    		-- 1. IF문 이용
    		IF N > 5
    			THEN EXIT;
    		END IF;
    	END LOOP;
    END;
    /
    
    DECLARE 
    	N NUMBER := 1;
    BEGIN
    	LOOP
    		DBMS_OUTPUT.PUT_LINE(N);
    		N := N + 1;
    		
    		-- 2. EXIT WHEN 이용
    		EXIT WHEN N > 5;
    	END LOOP;
    END;
    /
  • FOR LOOP: 인덱스를 사용하여 반복하기 때문에 DECLARE를 이용한 변수 선언이 필요없다.
    FOR 인덱스 IN 초기값..최종값
    LOOP
       처리문
    END LOOP;
    BEGIN
        FOR N IN 1..5
        LOOP
            DBMS_OUTPUT.PUT_LINE(N);
            END LOOP;
    END;
    /

    위 코드는 1부터 5까지 출력된다. 5부터 1까지 출력하려고 하는 경우에 5..1 형태로 작성하면 아무것도 출력되지 않는다. 인덱스는 초기값에서 시작해 최종 값까지 증가하는 형태이기 때문에 초기값에 더 큰 값을 대입하면 반복문에 들어가지 않기 때문이다. 따라서 숫자를 거꾸로 출력하는 경우에는 빼기를 이용하여 출력하거나, REVERSE를 이용하여야 한다.
    -- 빼기를 이용한 출력
    BEGIN
        FOR N IN 1..5
        LOOP
            DBMS_OUTPUT.PUT_LINE(6-N);
            END LOOP;
    END;
    /
    
    -- REVERSE를 이용한 출력
    BEGIN
        FOR N IN REVERSE 1..5
        LOOP
            DBMS_OUTPUT.PUT_LINE(N);
            END LOOP;
    END;
    /
     PL/SQL도 자바처럼 반복문 안에 조건문/반복문이 중첩될 수 있다.
  • WHILE LOOP
    WHILE 조건
    LOOP
       처리문
    END LOOP;
    WHILE문의 경우 인덱스가 존재하지 않기 때문에 변수를 직접 선언해 주어야 하며, 해당 변수를 증가/감소하는 문장도 존재하여야 한다. 반복문을 중첩하는 경우 해당 변수의 값 초기화도 같이 진행되어야 한다.
    DECLARE 
        N NUMBER := 1;
    BEGIN
        WHILE N <= 5
        LOOP
            DBMS_OUTPUT.PUT_LINE(N);
            N := N+1;
        END LOOP;
    END;
    /
    
    WHILE문에서 숫자를 거꾸로 출력하는 경우에는 REVERSE를 사용하지 않아도 되며, 변수의 값과 조건문만 변경해주면 된다.
    DECLARE 
        N NUMBER := 5;
    BEGIN
        WHILE N >= 1
        LOOP
            DBMS_OUTPUT.PUT_LINE(N);
            N := N-1;
        END LOOP;
    END;
    /

 

예외처리

  • 예외에는 미리 정의되어 있는 예외와 사용자 지정 예외 두 가지 종류가 존재한다.
  • 미리 정의되어 있는 예외
    • NO_DATA_FOUND: SELECT문이 아무 행도 반환하지 못할 경우 처리한다.
      DECLARE
          NAME VARCHAR2(30);
      BEGIN
          SELECT EMP_NAME INTO NAME
          FROM EMPLOYEE
          WHERE EMP_ID = '&사번';
          
          DBMS_OUTPUT.PUT_LINE(NAME);
          
      EXCEPTION
          WHEN NO_DATA_FOUND
              THEN DBMS_OUTPUT.PUT_LINE('없는 사번입니다.');
      END;
      /
      위 문장 실행 시 없는 사번을 넣을 경우 '데이터를 찾을 수 없습니다'라는 오류가 발생한다. 해당 오류가 발생하지 않고, '없는 사번입니다.'라는 문구가 출력되도록 하기 위해서 NO_DATA_FOUND 예외 처리를 이용한다.
    • DUP_VAL_ON_INDEX: UNIQUE 제약 조건이 있는 컬럼에 중복되는 데이터가 들어갈 경우 처리한다.
      BEGIN
          UPDATE EMPLOYEE
          SET EMP_ID = '&사번'
          WHERE EMP_ID = 200;
      EXCEPTION
          WHEN DUP_VAL_ON_INDEX
              THEN DBMS_OUTPUT.PUT_LINE('이미 존재하는 사번입니다');
      END;
      /
      위 문장 실행 시 이미 존재하는 사번으로 변경하는 경우 EMP_ID 컬럼에 UNIQUE 제약 조건이 있기 때문에 '무결성 제약 조건에 위배됩니다'라는 오류가 발생한다. 해당 오류가 발생하지 않고 '이미 존재하는 사번입니다'라는 문구가 출력되도록 하기 위해서 DUP_VAL_ON_INDEX 예외 처리를 이용한다.
    • ZERO_DEVIDE: 0으로 나눌 경우 처리한다.

 

PROCEDURE(프로시저)

  • PL/SQL 문장을 저장하여 변수처럼 사용할 수 있도록 하는 객체이다. 간단하게 호출하여 사용 가능하다.
    CREATE OR REPLACE PROCEDURE 프로시저명[(매개변수, ...)]
    IS
      선언부 (DECLARE는 작성하지 않는다)
    BEGIN
       실행부
    EXCEPTION
       예외처리부
    END;
    /
  • 프로시저의 경우 생성만으로 프로시저 내부의 PL/SQL 문장이 실행되지 않으며, 호출하여야 해당 문장을 실행 가능하다.
    CREATE OR REPLACE PROCEDURE DEL_ALL_EMP
    IS
    BEGIN
        DELETE FROM EMP_01;
    END;
    /
    
    EXEC DEL_ALL_EMP;
  • 프로시저를 관리하는 데이터 딕셔너리
    SELECT * FROM USER_SOURCE;
    해당 딕셔너리를 확인하면 PL/SQL 문장이 라인 별로 구분되어 저장돼 있다. VIEW에서처럼 이것을 하나씩 불러와서 실행하는 방식이다.
  • 프로시저를 호출할 때마다 특정 값을 입력받아 사용하여야 하는 경우에는 매개변수를 이용하여야 한다. 만약 프로시저를 생성하는 문장에 입력이 있는 경우 해당 프로시저를 생성할 때만 입력을 받고, 그 값으로 고정된다. 따라서 호출 시에는 입력받을 수 없으며 의도된대로 작동하지 않게 된다.
    매개변수 사용 시 프로시저 명 뒤 소괄호 안에 매개변수 이름과 타입을 작성하며, 이때 타입은 레퍼런스 타입을 사용할 수 있다.
    매개변수 사용 시 호출할 때도 매개변수를 작성해 주어야 하며, 이때 매개변수에 입력을 넣을 수 있다.
    CREATE OR REPLACE PROCEDURE DEL_EMP_ID(INPUT_EMP_ID EMPLOYEE.EMP_ID%TYPE)
    IS
    BEGIN
        DELETE FROM EMPLOYEE WHERE EMP_ID = INPUT_EMP_ID;
    END;
    /
    
    EXEC DEL_EMP_ID('&사번');

FUNCTION

  • PROCEDURE와 동일하나 반환값이 존재한다
    CREATE OR REPLACE FUNCTION 함수명[(매개변수, ...)]
    RETURN 데이터타입
    IS
       선언부
    BEGIN
       실행부
       RETURN 반환값;
    EXCEPTION
       예외처리부
    END;
    /
  • 함수 실행 방법
    EXECUTE 함수명;
    EXEC 함수명;
    함수는 프로시저와 달리 반환 값이 존재하기 때문에 반환 값을 받아 줄 바인드 변수가 있어야 실행 가능하다.
    • 바인드 변수: PL/SQL 외부에서도 사용할 수 있는 변수로 호스트 환경에서 생성되어 데이터를 저장하기 때문에 호스트 변수라고도 한다.
      키워드 VARIABLE을 이용하여 생성하며, SQL문이나 PL/SQL 블록에서도 사용 가능하다.
      비 PL/SQL 변수이기 때문에 PL/SQL 블록이 실행된 후에도 엑세스가 가능하다.
      PRINT 명령을 이용하여 출력이 가능하며, :(콜론)을 붙여 사용한다.
    CREATE OR REPLACE FUNCTION SELECT212
    -- RETURN VARCHAR2
    RETURN EMPLOYEE.EMP_NAME%TYPE 
    IS
        NAME EMPLOYEE.EMP_NAME%TYPE;
    BEGIN
        SELECT EMP_NAME
        INTO NAME
        FROM EMPLOYEE
        WHERE EMP_ID = 212;
        
        RETURN NAME;
    END;
    /
    
    VARIABLE EX_NAME VARCHAR2(20); 
    EXEC :EX_NAME := SELECT212;
    PRINT EX_NAME;
    
    함수의 RETURN에 타입을 적을 때 레퍼런스 변수를 사용 가능하며, 데이터 타입을 적는 경우 크기를 지정하지 않아야 한다. 바인드 변수를 생성하는 경우에는 레퍼런스 변수를 사용한 데이터 타입 설정이 불가능하다.
  • 함수 또한 프로시저와 동일하게 생성 시 값을 입력받게 되면 해당 값으로 고정되기 때문에 호출 시마다 값을 입력받아야 하는 경우에는 매개변수를 사용한다.

 

TRIGGER(트리거)

  • 테이블/뷰가 DML문에 의해 변경이 된 경우 자동으로 실행할 내용을 저장해 두는 객체로 쪽지를 받았을 때(INSERT) 자동으로 NEW 알림 배지가 뜨게 하는 등의 경우에 사용된다.
    CREATE OR REPLACE TRIGGER 트리거명
    BEFORE | AFTER
    INSERT | UPDATE | DELETE (어떤 DML이 작동했을 때 동작할 것인지 설정)
    ON 테이블명
    [FOR EACH ROW]
    [WHEN 조건]
    DECLARE 선언부
    BEGIN 실행부
    EXCEPTION 예외처리부
    END;
    /
  • 트리거 종류
    1. SQL 문의 실행 시기에 따른 분류
      • BEFORE TRIGGER: SQL문을 실행하기 전에 트리거가 실행된다.
      • AFTER TRIGGER: SQL문을 실행한 이후에 트리거가 실행된다.
    2. SQL문에 의해 영향을 받는 ROW에 따른 분류
      • ROW TRIGGER: SQL문 각 ROW에 대해 한 번씩 실행된다. FOR EACH ROW 옵션을 작성하여야 사용 가능하다.
        • ROW TRIGGER 조건
          :OLD: 참조 전 열의 값을 의미한다. (INSERT: X, UPDATE: 수정 전 자료, DELETE: 삭제 전 자료)
          :NEW: 참조 후 열의 값을 의미한다. (INSERT: 입력한 자료, UPDATE: 수정 후 자료. DELETE: X)
      • STATEMENT TRIGGER: SQL문에 의해 한 번만 실행된다. DEFAULT 트리거로 설정하지 않는 경우 STATEMENT TRIGGER가 된다.
  • 트리거 생성 및 실행
    CREATE OR REPLACE TRIGGER TRG_01
    AFTER INSERT 
    ON EMPLOYEE
    BEGIN
        DBMS_OUTPUT.PUT_LINE('신입사원이 입사했습니다.');
    END;
    /
    
    INSERT INTO EMPLOYEE VALUES (888, '김개발', '222222-2222222', 'gae@bal.or.kr', '01012344321',
                                 'D5', 'J3', 'S3', 3000000, 0.1, 200, SYSDATE, NULL, DEFAULT);
    아래 INSERT문이 실행될 때마다 '신입사원이 입사했습니다.' 문구가 출력된다.
    쇼핑몰의 경우 상품을 입고/출고했을 때 트리거를 사용하여 개수를 자동으로 변경되도록 할 수도 있다. 만약 트리거를 사용하지 않는다면 상품이 입고/출고될 때마다 테이블을 수정해 주어야 하는 번거로움이 있다.방금 입력한 자료를 기준으로 하여 수정하여야 하기 때문에 :NEW를 사용한 조건문을 작성한다. 이후 INSERT를 이용하여 PRO_DETAIL 테이블에 열을 추가하면, PRODUCT 테이블의 AMOUNT 값이 입고/출고 양에 따라 수정되는 것을 확인할 수 있다.
  • -- 상품 정보 테이블 CREATE TABLE PRODUCT( PCODE NUMBER PRIMARY KEY, PNAME VARCHAR2(30), BRAND VARCHAR2(30), PRICE NUMBER, STOCK NUMBER DEFAULT 0 ); -- 상품 입출고 이력 테이블 CREATE TABLE PRO_DETAIL( DCODE NUMBER PRIMARY KEY, PCODE NUMBER, PDATE DATE, AMOUNT NUMBER, STATUS VARCHAR2(10) CHECK(STATUS IN('입고', '출고')), -- 입출고 상태 FOREIGN KEY(PCODE) REFERENCES PRODUCT(PCODE) ); CREATE SEQUENCE SEQ_PCODE; CREATE SEQUENCE SEQ_DCODE; INSERT INTO PRODUCT VALUES(SEQ_PCODE.NEXTVAL, '모니터', '최고', 100, DEFAULT); INSERT INTO PRODUCT VALUES(SEQ_PCODE.NEXTVAL, '마우스', '찍찍', 50, DEFAULT); INSERT INTO PRODUCT VALUES(SEQ_PCODE.NEXTVAL, '키보드', '타닥', 80, DEFAULT); CREATE OR REPLACE TRIGGER TRG_02 AFTER INSERT ON PRO_DETAIL FOR EACH ROW BEGIN -- 상품이 입고된 경우 IF :NEW.STATUS = '입고' THEN UPDATE PRODUCT SET STOCK = STOCK + :NEW.AMOUNT WHERE PCODE = :NEW.PCODE; END IF; -- 상품이 출고된 경우 IF :NEW.STATUS = '출고' THEN UPDATE PRODUCT SET STOCK = STOCK - :NEW.AMOUNT WHERE PCODE = :NEW.PCODE; END IF; END; / INSERT INTO PRO_DETAIL VALUES(SEQ_DCODE.NEXTVAL, 2, SYSDATE, 10, '입고'); INSERT INTO PRO_DETAIL VALUES(SEQ_DCODE.NEXTVAL, 3, SYSDATE, 5, '입고'); INSERT INTO PRO_DETAIL VALUES(SEQ_DCODE.NEXTVAL, 2, SYSDATE, 7, '입고'); INSERT INTO PRO_DETAIL VALUES(SEQ_DCODE.NEXTVAL, 1, SYSDATE, 12, '입고'); INSERT INTO PRO_DETAIL VALUES(SEQ_DCODE.NEXTVAL, 1, SYSDATE, 8, '출고'); INSERT INTO PRO_DETAIL VALUES(SEQ_DCODE.NEXTVAL, 2, SYSDATE, 1, '출고');