SQL

2022_11_25 PLSQL

0304호 2022. 11. 25. 18:43

 

--2022_11_25

--PLSQL(프로그래밍SQL)
/*
오라클에서 제공되는 프로그래밍 기능이다.
일반적인 프로그래밍과는 차이가 있지만, 오라클 내부에서 아주 적절한 방식을 적용해준다
쿼리문의 집합으로 어떠한 동작을 일괄처리하기 위한 용도로 사용됨

절차형 SQL(PLSQL)은 코드부분만 선택하여 F5로 컴파일하야 실행함
*/

--출력을 허용함
SET SERVEROUTPUT ON;
-----------------------------------------------

DECLARE 
    vi_num NUMBER;  -- 변수선언
BEGIN 
    vi_num := 100;  --대입
    DBMS_OUTPUT.PUT_LINE(vi_num);   --출력
END;
---------------------------------------------

/*
연산자
일반 sql문의 모든 연산자의 사용이 가능하고
특별하게 **는 제곱을 의미합니다.
*/
DECLARE
    a NUMBER := 2**2*3**2;      --4*9
BEGIN 
    DBMS_OUTPUT.PUT_LINE('a = ' || TO_CHAR(a));
END;
---------------------------------------------

/*
DML문
DML문은 사용이 불가능하고, 일반적인 SQL문의 SELECT절을 사용하는데
특이한점은 SELECT절 아래에 INTO절을 사용해 변수에 할당한다.
*/
DECLARE
--    v_emp_name VARCHAR2(50);  --사원병(문자열 변수는 길이 제약이 필수)
--    v_dep_name VARCHAR2(50);  --부서명
--해당 테이블과 같은 타입의 컬럼변수를 선언하려면 테이블명.컬럼명%TYPE을 사용하면됨
    v_emp_name employees.first_name%TYPE;
    v_dep_name departments.department_name%TYPE;
BEGIN
    SELECT a.first_name, b.department_name
    INTO v_emp_name, v_dep_name     --대입을 의미
    FROM employees a
    LEFT OUTER JOIN departments b
    ON a.department_id=b.department_id
    WHERE employee_id = 100;
    
    DBMS_OUTPUT.PUT_LINE(v_emp_name || '-' || v_dep_name);
END;
---------------------------------------------------------------

--SELECT 문과 INSERT문 DML문을 같이 사용할 수 있다
CREATE TABLE EMP_SAL(
    EMP_YEARS VARCHAR2(50),
    EMP_SALARY NUMBER(10)
    );
--년도별 사원의 급여합을 구해서 새로운 테이블 INSERT
DECLARE
    EMP_SUM EMPLOYEES.SALARY%TYPE;
    EMP_YEARS EMP_SAL.EMP_YEARS%TYPE :=2008;
BEGIN
--SELECT
    SELECT SUM(SALARY) 
    INTO EMP_SUM
    FROM EMPLOYEES
    WHERE TO_CHAR(HIRE_DATE,'YYYY') = EMP_YEARS;

--INSERT
    INSERT INTO EMP_SAL VALUES(EMP_YEARS,EMP_SUM);
--COMMIT
    COMMIT;
    
    DBMS_OUTPUT.PUT_LINE(EMP_SUM);
END;

SELECT * FROM EMP_SAL;
----------------------------------------------------------
--1. 구구단중 3단을 출력하는 익명 블록을 만들어 보자.
DECLARE
    X NUMBER := 3;
    Y1 NUMBER := 1;
    Y2 NUMBER := 2;
    Y3 NUMBER := 3;
    Y4 NUMBER := 4;
    Y5 NUMBER := 5;
    Y6 NUMBER := 6;
    Y7 NUMBER := 7;
    Y8 NUMBER := 8;
    Y9 NUMBER := 9;
        
    
    ANS1 NUMBER := X*Y1;     
    ANS2 NUMBER := X*Y2;     
    ANS3 NUMBER := X*Y3;     
    ANS4 NUMBER := X*Y4;     
    ANS5 NUMBER := X*Y5;     
    ANS6 NUMBER := X*Y6;     
    ANS7 NUMBER := X*Y7;     
    ANS8 NUMBER := X*Y8;     
    ANS9 NUMBER := X*Y9;     
   
BEGIN 
    
    DBMS_OUTPUT.PUT_LINE(X || 'x' || Y1 || '=' || ANS1);
    DBMS_OUTPUT.PUT_LINE(X || 'x' || Y2 || '=' || ANS2);
    DBMS_OUTPUT.PUT_LINE(X || 'x' || Y3 || '=' || ANS3);
    DBMS_OUTPUT.PUT_LINE(X || 'x' || Y4 || '=' || ANS4);
    DBMS_OUTPUT.PUT_LINE(X || 'x' || Y5 || '=' || ANS5);
    DBMS_OUTPUT.PUT_LINE(X || 'x' || Y6 || '=' || ANS6);
    DBMS_OUTPUT.PUT_LINE(X || 'x' || Y7 || '=' || ANS7);
    DBMS_OUTPUT.PUT_LINE(X || 'x' || Y8 || '=' || ANS8);
    DBMS_OUTPUT.PUT_LINE(X || 'x' || Y9 || '=' || ANS9);
    
END;




--2. 사원테이블에서 201번 사원과 이름과 이메일 주소를 출력하는 익명 블록을 만들어 보자
--FIRST_NAME  /  EMAIL

DECLARE
    EMP_NAME EMPLOYEES.FIRST_NAME%TYPE;
    EMP_EMAIL EMPLOYEES.EMAIL%TYPE;
BEGIN   
    SELECT FIRST_NAME, EMAIL 
    INTO emp_name, emp_email
    FROM EMPLOYEES
    where employee_id = 201;
    
    DBMS_OUTPUT.PUT_LINE(emp_name||' - ' || emp_email);

END;



--3.  사원 테이블에서 사원번호가 제일 큰 사원을 찾아낸 뒤,
--이 번호 +1번으로 아래의 사원을 EMPS에 INSERT하는 문장
--EMPLOYEE_ID, LAST_NAME, EMAIL, HIRE_dATE, JOB_ID를 신규 입력하는 익명 블록을 만들어 보자
--HINT :SELECT절 이후에 INSERT문의 사용이 가능합니다.
<사원명 > : STEVEN
<이메일> : STEVENJOBS
<입사일자> : 오늘날짜
<JOB_ID>  : CEO

DECLARE
    EMP_max_empno EMPLOYEES.employee_id%TYPE;   --최대값 저장
BEGIN
    SELECT max(employee_id)
    into EMP_max_empno
    FROM employees;
    
    INSERT INTO emps( employee_id, LAST_NAME, EMAIL,  HIRE_DATE, JOB_ID )
    VALUES(EMP_max_empno+1, 'steven', 'stevenjobs',sysdate,'ceo'); 
    
    commit;
    
END;
SELECT SEQ_INFO.NEXTVAL FROM DUAL;
select*from INFO;
DELETE FROM INFO WHERE ID<=10;

CREATE TABLE EMPS AS (SELECT * FROM EMPLOYEES WHERE 1=2);​
--PLSQL제어문
-----------------------------------------------------
--랜덤수 출력
DECLARE
    V_NUM NUMBER := ROUND(DBMS_RANDOM.value(0,10));
BEGIN
    DBMS_OUTPUT.PUT_LINE(V_NUM);
END;
------------------------------------------------------
--IF문장
--IF문 (IF (조건) THEN (구문)  ELSE END IF);
DECLARE
    NUM1 NUMBER := 5;
    NUM2 NUMBER := TRUNC(DBMS_RANDOM.VALUE(1,11));      --RANDOM수 저장
BEGIN
    DBMS_OUTPUT.PUT_LINE('랜덤수 : ' || NUM2);
    IF ( NUM1 >= NUM2 ) THEN 
            DBMS_OUTPUT.PUT_LINE ( NUM1 || '이 큰 수 입니다');       --단독 IF문
    ELSE
            DBMS_OUTPUT.PUT_LINE ( NUM2 || '이 큰 수 입니다');      --ELSE문
    END IF;
    
END;

--------------------------------------------------------------

--ELSEIF문

DECLARE
    RAN_NUM NUMBER:=TRUNC(DBMS_RANDOM.VALUE(1,101));
BEGIN
    
    IF RAN_NUM>=90 THEN
        DBMS_OUTPUT.PUT_LINE('A학점입니다');
    ELSIF RAN_NUM>=80 THEN
        DBMS_OUTPUT.PUT_LINE('B학점입니다');
    ELSIF RAN_NUM>=70 THEN
        DBMS_OUTPUT.PUT_LINE('C학점입니다');
    ELSE
        DBMS_OUTPUT.PUT_LINE('D학점입니다');
    END IF;
END;

--실습
/*첫번째 값은 ROWNUM을 이용하면 됩니다.
10~120 사이의 10단위 랜덤한 번호를 이용해서 랜덤DEPARTMENT_ID 의 첫번째 행만 SELECT합니다.
뽑은 사람의 SALARYRK 9000이상이면 높음, 5000이상이면 중간, 나머지는 낮음으로 출력
*/
 
 

DECLARE
    SCORE EMPLOYEES.SALARY%TYPE;
    RAN NUMBER :=ROUND(DBMS_RANDOM.VALUE(10,120),-1);

BEGIN
   SELECT  SALARY
   INTO SCORE
   FROM EMPLOYEES
    WHERE DEPARTMENT_ID = ROUND(DBMS_RANDOM.VALUE(10,120),-1) AND ROWNUM=1;
    
    DBMS_OUTPUT.PUT_LINE(SCORE);
    IF SCORE>=9000 THEN
    DBMS_OUTPUT.PUT_LINE('높음');
    ELSIF SCORE>=5000 THEN
    DBMS_OUTPUT.PUT_LINE('중간');
    ELSE  
    DBMS_OUTPUT.PUT_LINE('낮음');
    END IF;

END;

---------------------------------------------------------------------------------
DECLARE
    SCORE EMPLOYEES.SALARY%TYPE;
    RAN NUMBER :=ROUND(DBMS_RANDOM.VALUE(10,120),-1);

BEGIN
   SELECT  SALARY
   INTO SCORE
   FROM EMPLOYEES
    WHERE DEPARTMENT_ID = ROUND(DBMS_RANDOM.VALUE(10,120),-1) AND ROWNUM=1;
    
    DBMS_OUTPUT.PUT_LINE(SCORE);
    CASE WHEN SCORE>=9000 THEN
    DBMS_OUTPUT.PUT_LINE('높음');
    WHEN SCORE>=5000 THEN
    DBMS_OUTPUT.PUT_LINE('중간');
    ELSE  
    DBMS_OUTPUT.PUT_LINE('낮음');
    END CASE;

END;

---------------------------------------------------------------------------
--반복문 WHILE

DECLARE
    a NUMBER := 3;
    b NUMBER := 1;

BEGIN
    
    WHILE b <= 9
    LOOP
    DBMS_OUTPUT.PUT_LINE(a || ' x ' || b || ' = ' || a*b);
      b := b + 1;
    END LOOP;
END;
---------------------------------------------------------------------
--탈출문 EXIT WHEN 조건

DECLARE
    a NUMBER := 3;
    b NUMBER := 1;

BEGIN
    
    WHILE b <= 9
    LOOP
    DBMS_OUTPUT.PUT_LINE(a || ' x ' || b || ' = ' || a*b);
    EXIT WHEN b=5;
      b := b + 1;
    END LOOP;
    
END;
---------------------------------------------------------------------
--CONTINUE WHEN 조건

DECLARE
    a NUMBER := 3;
    b NUMBER := 1;

BEGIN
    
    WHILE b <= 9
    LOOP
    DBMS_OUTPUT.PUT_LINE(a || ' x ' || b || ' = ' || a*b);
    b := b + 1;
    CONTINUE WHEN b=5;
    END LOOP;
    
END;
----------------------------------
DECLARE
    A NUMBER :=3;
BEGIN
    FOR I IN 1..9
    LOOP
        CONTINUE WHEN I=5;
        DBMS_OUTPUT.PUT_LINE(A || ' x ' || I || ' = ' || A*I);
        
    END LOOP;
END;

--------------------------------------------------------------
--실습

--1. 모든 구구단을 출력하는 익명 블록

DECLARE
    A NUMBER:=2;
BEGIN
    FOR I IN 2..9
    LOOP 
        FOR J IN 1..9
        LOOP
        DBMS_OUTPUT.PUT_LINE(I || ' x ' || J || ' = ' || I*J);
        END LOOP;
    END LOOP;
END;

--2. INFO테이블(시퀀스사용)에 INSERT를 300번 실행하는 익명블록을 처리하세요

CREATE SEQUENCE INFO_SEQ NOCACHE;
ALTER


DECLARE

BEGIN
    
    FOR I IN 1..300
    LOOP
        INSERT INTO INFO( ID, TITLE, CONTENT,  REGDATE, AUTH_ID )
        VALUES(SEQ_INFO.NEXTVAL, 'TITLE','CONTENT',SYSDATE,I); 
    END LOOP;
END;

SELECT * FROM INFO;

 

/*
저장 프로시저 - 하나의 함수처럼 실행하기 위한 쿼리의 집합
만드는 과정과, 실행하는 구문을 나누어 작성합니다
*/

--프로시저 생성
/*
CREATE OR REPLACE PROCEDURE NEW_JOB_PROC --매개변수
IS      --변수의 선언 영역
BEGIN   --실행영역
    DBMS_OUTPUT.PUT_LINE('HELLO WORLD!');
END;
*/
--------------------------------------------------
--프로시져 실행
EXECUTE NEW_JOB_PROC;

--프로시저 매개변수

CREATE OR REPLACE PROCEDURE NEW_JOB_PROC
    (
    P_JOB_ID IN JOBS.JOB_ID%TYPE,
    P_JOB_TITLE IN JOBS.JOB_TITLE%TYPE,
    P_MIN_SAL IN JOBS.MIN_SALARY%TYPE,
    P_MAX_SAL IN JOBS.MAX_SALARY%TYPE
    )
IS
BEGIN
    INSERT INTO JOBS
    VALUES (P_JOB_ID, P_JOB_TITLE, P_MIN_SAL, P_MAX_SAL);


END;

------실행
EXECUTE NEW_JOB_PROC('SM_MAN1','SAMPLE TEST',1000,5000);
------조회
SELECT * FROM JOBS;

-------------------------------------------------
CREATE OR REPLACE PROCEDURE NEW_JOB_PROC
    (
    P_JOB_ID IN JOBS.JOB_ID%TYPE,
    P_JOB_TITLE IN JOBS.JOB_TITLE%TYPE,
    P_MIN_SAL IN JOBS.MIN_SALARY%TYPE,
    P_MAX_SAL IN JOBS.MAX_SALARY%TYPE
    )
IS
    V_COUNT NUMBER := 0;        --지역변수
BEGIN
    SELECT COUNT(*)
    INTO V_COUNT
    FROM JOBS 
    WHERE JOB_ID = P_JOB_ID;        --V_COUNT라는 지역변수에 데이터 개수 할당
    IF V_COUNT=0 THEN
        INSERT INTO JOBS
        VALUES (P_JOB_ID, P_JOB_TITLE, P_MIN_SAL, P_MAX_SAL);
    ELSE 
        UPDATE JOBS
        SET JOB_TITLE = P_JOB_TITLE, 
            MIN_SALARY = P_MIN_SAL, 
            MAX_SALARY = P_MAX_SAL
        WHERE JOB_ID = P_JOB_ID;
    END IF;
END;

------실행
EXECUTE NEW_JOB_PROC('SM_MAN1','SAMPLE TEST22',1000,5000);
------조회
SELECT * FROM JOBS;

----------------------------------------------------------------------
--프로시저의 디폴트 매개변수
EXECUTE NEW_JOB_PROC('SM_MAN1','SAMPLE TEST2222');      --매개변수가 일치하지 않기 때문에 에러

CREATE OR REPLACE PROCEDURE NEW_JOB_PROC
    (
    P_JOB_ID IN JOBS.JOB_ID%TYPE,
    P_JOB_TITLE IN JOBS.JOB_TITLE%TYPE,
    P_MIN_SAL IN JOBS.MIN_SALARY%TYPE :=0,
    P_MAX_SAL IN JOBS.MAX_SALARY%TYPE :=1000
    )
IS
    V_COUNT NUMBER := 0;        --지역변수
BEGIN
    SELECT COUNT(*)
    INTO V_COUNT
    FROM JOBS 
    WHERE JOB_ID = P_JOB_ID;        --V_COUNT라는 지역변수에 데이터 개수 할당
    IF V_COUNT=0 THEN
        INSERT INTO JOBS
        VALUES (P_JOB_ID, P_JOB_TITLE, P_MIN_SAL, P_MAX_SAL);
    ELSE 
        UPDATE JOBS
        SET JOB_TITLE = P_JOB_TITLE, 
            MIN_SALARY = P_MIN_SAL, 
            MAX_SALARY = P_MAX_SAL
        WHERE JOB_ID = P_JOB_ID;
    END IF;
END;
COMMIT;

--디폴트 매개변수를 추가하면 
EXECUTE NEW_JOB_PROC('SM_MAN1','SAMPLE TEST2222');      --디폴트 매개변수를 가지고있기 때문에 문제없음
-------------------------------------------------------------------------
CREATE OR REPLACE PROCEDURE NEW_JOB_PROC --매개변수
    ( P_JOB_ID IN JOBS.JOB_ID%TYPE,
      P_JOB_TITLE IN JOBS.JOB_TITLE%TYPE,
      P_MIN_SALARY IN JOBS.MIN_SALARY%TYPE := 0,
      P_MAX_SALARY IN JOBS.MAX_SALARY%TYPE := 1000,
      P_RESULT OUT VARCHAR2 --OUT
    )
IS --변수의 선언영역

    V_COUNT NUMBER := 0;
BEGIN --실행영역

    SELECT COUNT(*)
    INTO V_COUNT
    FROM JOBS
    WHERE JOB_ID = P_JOB_ID;--V_COUNT의 데이터개수 할당
    
    IF V_COUNT = 0 THEN
        INSERT INTO JOBS
        VALUES (P_JOB_ID, P_JOB_TITLE, P_MIN_SALARY, P_MAX_SALARY);
        --OUT변수에 '성공'할당
        P_RESULT := '성공';
    
    ELSE
        UPDATE JOBS
        SET JOB_TITLE = P_JOB_TITLE,
            MIN_SALARY = P_MIN_SALARY,
            MAX_SALARY = P_MAX_SALARY
        WHERE JOB_ID = P_JOB_ID;
        --OUT변수에 '업데이트' 할당
        P_RESULT := '업데이트';
    
    END IF;
    
    --예외 처리

    EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('예외가 발생했습니다');
    
END;

--OUT변수를 활용하려면 익명블록에서 실행
DECLARE 
    STR VARCHAR2(20);  --OUT변수를 돌려받을 변수
BEGIN
    NEW_JOB_PROC('SM_MAN4','SAMPLE',0,1000,STR);
    DBMS_OUTPUT.PUT_LINE(STR);
END;
--------------------------------------------------------------------------------


--실습
--EMPLOYEE_ID 를 받아서 EMPLOYEES 에 존재하면, 근속년수를 출력
--없다면 없습니다를 출력하는 프로시저.
CREATE OR REPLACE PROCEDURE IF_EMPLOY
    (
    P_EMPLOYEE_ID IN EMPLOYEES.EMPLOYEE_ID%TYPE
    )
IS
    P_COUNT NUMBER :=0;
    P_HIRE_DATE EMPLOYEES.HIRE_DATE%TYPE;
  
BEGIN
    SELECT HIRE_DATE 
    INTO P_HIRE_DATE
    FROM EMPLOYEES
    WHERE EMPLOYEE_ID = P_EMPLOYEE_ID;
    
        DBMS_OUTPUT.PUT_LINE(P_HIRE_DATE);
        EXCEPTION WHEN OTHERS THEN 
        DBMS_OUTPUT.PUT_LINE('없습니다');
END;

--실행
EXECUTE IF_EMPLOY(100);