Oracle 22강 - PL/SQL 익명블록

PL/SQL (Procedual Language SQL)

  • 표준 SQL의 기능을 확장한 문법제공(분기,반복,변수 등)
  • 미리 컴파일되어 저장되기 때문에 처리속도와 효율성 증가
  • 모듈화 및 캡슐화 기능 제공
  • 표준 문법이 없음
  • 각 DBMS에 종속적 --내가 사용하는 DBMS에 따라 문법이 다름
  • 익명블록(Anonymous block),Procedure,Function,Trigger,Package 등 제공됨
  1. 익명블록 -- 주로 검증할 때 사용, 저장되지 않음
  • 단순 스크립트에서 실행되는 블록(이름이 없음)
  • 저장불가
  • PL/SQL의 기본 구조
    (사용형식)
    DECLARE --여기에 CREATE,PROCEDURE,FUNCTION이 나오면 각각의 PL/SQL이 됨
    선언부;--변수,상수,커서 선언
    BEGIN
    실행부;--문제해결을 위한 비지니스로직 처리 SQL문
    [EXCEPTION --모든 예외를 처리할 수 있음
    예외처리부;]
    END;

사용예)키보드로 부서번호를 입력 받아 해당 부서의 주소,인원수,평균급여를 출력하는 익명블록을 작성하시오

  ACCEPT P_DEPT_NO PROMPT '부서코드 입력(10-110) : ' --P_DEPT_NO:변수(P:파라미터라는 뜻)
  DECLARE
    V_DEPT_NO HR.DEPT.DEPARTMENT_ID%TYPE := TO_NUMBER('&P_DEPT_NO'); 
    --&:참조하는 방식(포인터 안의 내용을 참조하라는 기호
                    ,실행할 때 &안에 있는 자료를 꺼내서 참조하라)
    V_CNT NUMBER:=0;--V_DEPT_NO:(V:변수라는 뜻)
    V_ASAL NUMBER:=0;
    V_ADDR VARCHAR2(200);
  BEGIN
    SELECT A.STREET_ADDRESS||' '||A.CITY||' '||A.STATE_PROVINCE --주소를 얻기위한 SELECT문
      INTO V_ADDR --주소를 할당
      FROM HR.LOCATIONS A, HR.DEPT B
     WHERE A.LOCATION_ID=B.LOCATION_ID
       AND B.DEPARTMENT_ID=V_DEPT_NO;
       
    SELECT COUNT(EMPLOYEE_ID), --사원의 수
           ROUND(AVG(SALARY)) --그 부서의 평균급여
      INTO V_CNT,V_ASAL     --두 개의 변수에 각각 할당해줌
      FROM HR.EMP
     WHERE DEPARTMENT_ID=V_DEPT_NO; 
     
    DBMS_OUTPUT.PUT_LINE('부서번호 : '||V_DEPT_NO); --SYSTEM.OUT.PRINTLN과 같음
    DBMS_OUTPUT.PUT_LINE('주소 : '||V_ADDR);
    DBMS_OUTPUT.PUT_LINE('인원수 : '||V_CNT);
    DBMS_OUTPUT.PUT_LINE('평균급여 : '||V_ASAL);
  END;  

TO_NUMBER('&P_DEPT_NO') - 문자열타입을 NUMBER타입으로
':=' : 자바의 대입연산자와 같음

V_DEPT_NO HR.DEPT.DEPARTMENT_ID%TYPE := TO_NUMBER('&P_DEPT_NO');
-> HR.DEPT.DEPARTMENT_ID과 같은 타입으로 변수를 선언하라는 뜻(참조타입)

지역변수는 사용하기 전에 반드시 초기화해야함.

V_CNT NUMBER:=0;
-> NUMBER타입은 반드시 초기화시켜야 함(초기화하지 않으면 NULL값이 들어감)
초기화시키지 않으면 SCRIPTRUNNER가 왔다갔다하기만 함. - 중지해서 NUMBER에 0 대입해줘야 함.

PL/SQL의 BEGIN블록 안에서 사용되는 SELECT문은 SELECT~INTO로 사용됨
SELECT에 두 컬럼이 사용되면 INTO에도 두 변수가 할당되어야 함.
선언문(DECLARE)에서는 SELECT~INTO문이 안 쓰임,
BEGIN블록 안에서만 SELECT~INTO절이 사용됨, 이외의 구간에는 SELECT FROM절 사용

SELECT 위치를 골라서
INTO 변수에 할당

DBMS_OUTPUT.PUT_LINE('부서번호 : '||V_DEPT_NO); --자바에서 PRINTLN과 같은 역할
스크립트 출력창에는 결과가 나오지 않음, DBMS출력창에 결과가 나옴
결과가 지워지지 않음, 지우개 툴을 눌러서 직접 지워줘야 함.

SCALR 변수 : 한 순간에 하나의 값만 들어올 수 있는 변수
같은 타입의 변수를 여러 개 동시에 저장할 수 있는 변수 : 배열
참조타입의 행참조타입은 자바의 ArrayList와 비슷하고 C언어의 구조체와 똑같음
구조체 : 다양한 타입의 데이터들을 한번에 저장할 수 있음 -> 데이터는 저장하지만 데이터를 조작하는 행위가 없음.
(자바의) class : 데이터를 저장하고 조작을 할 수있음

1)변수와 상수

  • SCALAR 변수, REFERENCE 변수제공
    (선언 형식)
    변수(상수)명 [CONSTANT]데이터타입|참조타입 [:=초기값]; --상수인 경우 CONSTANT쓰고 변수면 생략
  • 데이터타입
    . 표준 오라클에서 사용하는 데이터타입
    . PLS_INTEGER, BINARY_INTEGER : 4 BYTE 정수(-2147483648~2147483647)
    . BOOLEAN 타입(true,false,null)
  • 참조타입
    . 열참조타입 : 테이블명.컬럼명%TYPE
    . 행참조타입 : 테이블명%ROWTYPE --자바의 ArrayList와 비슷, 구조체와 똑같음

사용예)임의의 부서코드를 발생시켜 해당 부서에 속한 사원 중 한 직원의 --난수발생
사원번호,사원명,입사일,직책코드,급여를 출력하는 익명블록을 작성하시오

  DECLARE
    V_EMP_ID HR.EMP.EMPLOYEE_ID%TYPE;
    V_ENAME HR.EMP.EMP_NAME%TYPE;
    V_JOB HR.EMP.JOB_ID%TYPE;
    V_SAL HR.EMP.SALARY%TYPE;
    V_DEPT_ID HR.DEPT.DEPARTMENT_ID%TYPE;
  BEGIN
    V_DEPT_ID:=TRUNC(DBMS_RANDOM.VALUE(10,110),-1); 
    --난수발생 VALUE(하한값,상한값) : 하한값~상한값 사이의 정수형 난수발생
    SELECT EMPLOYEE_ID,EMP_NAME,JOB_ID,SALARY
      INTO V_EMP_ID,V_ENAME,V_JOB,V_SAL
      FROM HR.EMP
     WHERE DEPARTMENT_ID = V_DEPT_ID 
       AND ROWNUM=1;
       
     DBMS_OUTPUT.PUT_LINE('사원번호 : '||V_EMP_ID);   
     DBMS_OUTPUT.PUT_LINE('사원명 : '||V_ENAME);
     DBMS_OUTPUT.PUT_LINE('직책 : '||V_JOB);
     DBMS_OUTPUT.PUT_LINE('급여 : '||V_SAL);
  END;

가장많은

  DECLARE
    V_EMP_ID HR.EMP.EMPLOYEE_ID%TYPE;
    V_ENAME HR.EMP.EMP_NAME%TYPE;
    V_JOB HR.EMP.JOB_ID%TYPE;
    V_SAL HR.EMP.SALARY%TYPE;
    V_DEPT_ID HR.DEPT.DEPARTMENT_ID%TYPE;
  BEGIN
    V_DEPT_ID:=TRUNC(DBMS_RANDOM.VALUE(10,110),-1); 
  SELECT A.EMPLOYEE_ID, A.EMP_NAME,A.JOB_ID,A.SALARY 
    INTO V_EMP_ID,V_ENAME,V_JOB,V_SAL
    FROM (SELECT EMPLOYEE_ID,EMP_NAME,JOB_ID,SALARY
            FROM HR.EMP
           WHERE DEPARTMENT_ID = V_DEPT_ID
           ORDER BY 4 DESC) A
   WHERE ROWNUM=1;
       
     DBMS_OUTPUT.PUT_LINE('사원번호 : '||V_EMP_ID);   
     DBMS_OUTPUT.PUT_LINE('사원명 : '||V_ENAME);
     DBMS_OUTPUT.PUT_LINE('부서코드 : '||V_DEPT_ID);
     DBMS_OUTPUT.PUT_LINE('직책 : '||V_JOB);
     DBMS_OUTPUT.PUT_LINE('급여 : '||V_SAL);
  END;

 COMMIT;

사용예)2005년 7월 29일 'h001'회원이 'P202000012' 제품을 2개 구입했을 경우 이 정보를 CART테이블에 저장하시오.
--해야할 것 1.2005년7월29일에 매출이 발생되었나 확인
데이터 개수를 세서 COUNT를 써서 1일경우 -> 구매한 경우
0인경우 -> 구매하지 않은 경우

 DECLARE
    V_CNT NUMBER:=0; --V_CNT: 레코드 수(2005년7월29일 매출이 있는지 확인하는 변수) 
    V_CART_NO CART.CART_NO%TYPE;
 BEGIN
    SELECT COUNT(*) INTO V_CNT --2005년7월29일 매출 행이 존재하는지 확인
      FROM CART
     WHERE CART_NO LIKE '20050729%;';
     
     IF V_CNT=0 THEN --매출이 없다(0이다)
        V_CART_NO:='2005072900001'; --카트번호 부여         
     ELSE --매출이 있다. V_CNT값이 0이 아니다(이미 데이터가 존재한다)
        SELECT CART_NO INTO V_CART_NO --1번이 부여됨
          FROM CART
         WHERE CART_MEMBER='h001' --이 회원이
           AND SUBSTR(CART_NO,1,8)='20050729' --이 날
           AND CART_PROD='P202000012';
           --이 상품을 구매한 이력이 있는지 없는지 판별 -있으면 수를 추가, 없으면 INSERT
     END IF;      --여기까지 V_CART_NO가 NULL인 경우, NULL이 아닌 경우, V_CNT가 0인 경우
     IF V_CART_NO IS NULL AND V_CNT !=0 THEN  --안샀으면 NULL
        INSERT INTO CART --V_CART_NO가 NULL인 경우 : 매출은 있지만 그 회원이 그 날 그 제품을 사지는 않음
          SELECT 'h001',MAX(CART_NO)+1,'P202000012',2 --구매한적없음, 카트번호중 제일 큰 번호에 다음번호
            FROM CART
           WHERE CART_NO LIKE '20050729%';
     ELSIF V_CART_NO IS NOT NULL AND V_CNT !=0 THEN  --V_CART_NO가 NULL이 아닌 경우, 사간 경우     
          UPDATE CART
             SET CART_QTY=CART_QTY+2 --그 회원이 이미 그 물건을 샀기 때문에 CART_QTY에 숫자만 2개 더해줌
           WHERE CART_MEMBER='h001'
             AND SUBSTR(CART_NO,1,8)='20050729'
             AND CART_PROD='P202000012';
     ELSIF V_CART_NO IS NOT NULL AND V_CNT =0 THEN --아무도 안 사간 경우 : 신규로 구매를 등록
        INSERT INTO CART
          VALUES('h001',V_CART_NO,'P202000012',2);  
     END IF;     
 END;

좋은 웹페이지 즐겨찾기