[문제] 사원번호를 입력값으로 받아서 그 사원의 급여를 출력하는 프로그램을 작성. 또한 급여 1000당 별(*) 하나를 출력.
<화면출력>
사원번호 : 200
급여 : 4400
별점 : ****

DECLARE
    emp_id hr.employees.employee_id%type := 200;
    sal hr.employees.salary%type;
    star varchar2(10);
BEGIN
    SELECT salary
    INTO sal
    FROM hr.employees
    WHERE employee_id = emp_id;
    
    FOR i IN 1..trunc(sal/1000) LOOP
        star := star||'*';
    END LOOP;
    
    DBMS_OUTPUT.PUT_LINE('사원번호 : '||emp_id);
    DBMS_OUTPUT.PUT_LINE('급   여 : '||sal);
    DBMS_OUTPUT.PUT_LINE('별   점 : '||star);

    FOR i IN 1..trunc(sal/1000) LOOP
        DBMS_OUTPUT.PUT('*');   -- 가로로 메모리에만 누적
    END LOOP;
    
    DBMS_OUTPUT.NEW_LINE;   -- 줄바꿈
END;
/
>> NEW_LINE을 사용하여 줄바꿈으로서  PUT 메모리에 누적된 데이터가 출력 됨

 

[문제] 사원들의 근속 연수가 20년 이상이면 10% 인상급여로 수정하는 프로그램을 생성
<화면결과>
100 사원의 입사일은 03/06/17 근속연수는 20년입니다.
100 사원의 급여가 24000 -> 26400 수정했습니다.

DECLARE
    v_id hr.employees.employee_id%type := 100;
    v_day hr.employees.hire_date%type;
    v_sal_before hr.employees.salary%type;
    v_sal_after hr.employees.salary%type;
    v_years number;
    
BEGIN
    SELECT hire_date, salary
    INTO v_day, v_sal_before
    FROM hr.employees
    WHERE employee_id = v_id;

    v_years := trunc(months_between(sysdate,v_day)/12);
    DBMS_OUTPUT.PUT_LINE(v_id||' 사원의 입사일은 '||to_char(v_day,'yy/mm/dd')||' 근속연수는 '||v_years||'년입니다.');
    
    IF v_years >= 20 THEN               -- 조건에 맞으면 UPDATE
        UPDATE hr.employees
        SET salary = salary * 1.1
        WHERE employee_id = v_id;
        
        IF sql%found THEN               -- DML작업 데이터가 있다면 조회
            SELECT salary
            INTO v_sal_after           -- 수정 후 데이터 값
            FROM hr.employees
            WHERE employee_id = v_id;
            
            DBMS_OUTPUT.PUT_LINE(v_id||' 사원의 급여가 '||v_sal_before||' -> '||v_sal_after||' 수정했습니다.');
            
            COMMIT;                     -- 저장
        ELSE
            ROLLBACK;
        END IF;
    ELSE
        DBMS_OUTPUT.PUT_LINE(v_id||' 사원은 급여를 수정할 수 없습니다.');
    END IF;
END;
/

 



■ 조합데이터 유형

- 다중값을 보유할 수 있다.
레코드 : 서로 다른 유형의 값을 저장
배열 : 동일한 데이터 유형의 값을 저장
    - index by table(연관배열)
    - nested table(중첩테이블)
    - varray

DECLARE
    v_dept_id hr.departments.department_id%type;
    v_dept_name hr.departments.department_name%type;
    v_mgr_id hr.departments.manager_id%type;
    v_loc_id hr.departments.location_id%type;

BEGIN
    SELECT *
    INTO v_dept_id, v_dept_name, v_mgr_id, v_loc_id --> 테이블 칼럼 개수만큼 계속 생성 필요
    FROM hr.departments
    WHERE department_id = 10;
    
    DBMS_OUTPUT.PUT_LINE(v_dept_id);
    DBMS_OUTPUT.PUT_LINE(v_dept_name);
    DBMS_OUTPUT.PUT_LINE(v_mgr_id);
    DBMS_OUTPUT.PUT_LINE(v_loc_id);
    
END;
/
DECLARE
    TYPE dept_record_type IS RECORD
       (dept_id hr.departments.department_id%type
        dept_name varchar2(30),
        dept_mgr number,
        dept_loc number);                -- 필드 구성 : 테이블 내 타입과 사이즈에 맞게 작성
    
    v_rec dept_record_type;

BEGIN
    SELECT *
    INTO v_rec
    FROM hr.departments
    WHERE department_id = 10;
    
    DBMS_OUTPUT.PUT_LINE(v_rec.dept_id);
    DBMS_OUTPUT.PUT_LINE(v_rec.dept_name);
    DBMS_OUTPUT.PUT_LINE(v_rec.dept_mgr);
    DBMS_OUTPUT.PUT_LINE(v_rec.dept_loc);
END;
/

 

%rowtype : 테이블 또는 뷰의 열이름, 데이터 유형을 가져다가 사용하는 속성이다.
# %rowtype 이점
    - 테이블의 컬럼수, 데이터 유형을 알 필요가 없으며 실제 런타임에 변경된 속성을 그대로 사용할 수 있다.
    - SELECT * INTO 레코드 변수...
    - 행 레벨의 INSERT 및 UPDATE

DECLARE
    v_rec hr.departments%rowtype;

BEGIN
    SELECT *
    INTO v_rec
    FROM hr.departments
    WHERE department_id = 10;
    
    DBMS_OUTPUT.PUT_LINE(v_rec.department_id); -- 레코드변수.필드명(=칼럼명)
    DBMS_OUTPUT.PUT_LINE(v_rec.department_name);
    DBMS_OUTPUT.PUT_LINE(v_rec.manager_id);
    DBMS_OUTPUT.PUT_LINE(v_rec.location_id);
END;
/

 

DECLARE
    TYPE rec_type IS RECORD        -- 레코드 선언 시 한번에 선언
        (v_sal number(8),
        v_minsal number(8) default 1000,
        v_hire_date hr.employees.hire_date%type,
        v_rec hr.employees%rowtype);

    v_myrec rec_type;

BEGIN
    v_myrec.v_sal := v_myrec.v_minsal + 500;
    v_myrec.v_hire_date := sysdate;

    SELECT *
    INTO v_myrec.v_rec
    FROM hr.employees
    WHERE employee_id = 100;

    DBMS_OUTPUT.PUT_LINE(v_myrec.v_rec.first_name);
    DBMS_OUTPUT.PUT_LINE('근무연수 : '||trunc(months_between(v_myrec.v_hire_date, v_myrec.v_rec.hire_date)/12));

END;
/

 

 

CREATE TABLE hr.retired_emp
    (empno number(4),
    ename varchar2(20),
    job varchar2(10),
    mgr number(4),
    hiredate date,
    leavedate date,
    sal number(10),
    comm number(7,2),
    deptno number(2));

desc hr.retired_emp

 

DECLARE
    v_id number := 115;
    v_emp_rec hr.employees%rowtype;

BEGIN
    SELECT *
    INTO v_emp_rec
    FROM hr.employees
    WHERE employee_id = v_id;
    
    INSERT INTO hr.retired_emp(empno,ename,job,mgr,hiredate,leavedate,sal,comm,deptno)
    VALUES(v_emp_rec.employee_id, v_emp_rec.first_name, v_emp_rec.job_id, v_emp_rec.manager_id,
        v_emp_rec.hire_date, sysdate, v_emp_rec.salary, v_emp_rec.commission_pct, v_emp_rec.department_id);
END;
/

SELECT * FROM hr.retired_emp;
ROLLBACK;
DECLARE
    v_id number := 115;
    v_rec hr.retired_emp%rowtype;

BEGIN
    SELECT employee_id, first_name, job_id, manager_id, hire_date, sysdate, salary, commission_pct, department_id
    INTO v_rec
    FROM hr.employees
    WHERE employee_id = v_id;

    INSERT INTO hr.retired_emp VALUES v_rec;    -- 행레벨의 INSERT. 데이터타입 동일
END;
/

SELECT * FROM hr.retired_emp;
COMMIT;

 

DECLARE
    v_id number := 115;
    v_rec hr.retired_emp%rowtype;
BEGIN

    SELECT employee_id, first_name, job_id, manager_id, hire_date, sysdate, salary, commission_pct, department_id
    INTO v_rec
    FROM hr.employees
    WHERE employee_id = v_id;

    -- 레코드변수안에 있는 필드 값 수정
    v_rec.ename := upper(v_rec.ename);
    v_rec.leavedate := to_date('2003-10-01','yyyy-mm-dd');
    v_rec.comm := 0;

    -- 행레벨의 UPDATE
    UPDATE hr.retired_emp
    SET ROW = v_rec
    WHERE empno = v_id;
END;
/

SELECT * FROM hr.retired_emp;
ROLLBACK;

 



■ 1차원 배열

DECLARE
    TYPE tab_char_type IS TABLE OF varchar2(10) INDEX BY pls_integer;
                             --    ㄴ 데이터 형식           ㄴ 방번호 형식
    v_city tab_char_type;  
BEGIN
    v_city(1) := '서울';
    v_city(2) := '경기';
    v_city(3) := '부산';
    v_city(4) := '광주';

    DBMS_OUTPUT.PUT_LINE(v_city.count);     -- 배열에 저장된 값의 개수
    DBMS_OUTPUT.PUT_LINE(v_city.first);     -- 배열의 첫번째 인덱스 번호 (가장 작은)
    DBMS_OUTPUT.PUT_LINE(v_city.last);      -- 배열의 마지막 인덱스 번호 (가장 큰)
    DBMS_OUTPUT.PUT_LINE(v_city.next(3));   -- 배열의 인덱스 n번 뒤에 오는 인덱스 번호
    DBMS_OUTPUT.PUT_LINE(v_city.prior(3));  -- 배열의 인덱스 n번 앞에 오는 인덱스 번호
END;
/


- pls_integer : PL/SQL 전용 데이터 타입. 정수, -2^31~2^31-1(-2G~2G-1)
                보편적 많이 사용. number보다 속도가 빠름

DECLARE
    TYPE tab_char_type IS TABLE OF varchar2(10) INDEX BY pls_integer;
    v_city tab_char_type;  
BEGIN
    v_city(1) := '서울';
    v_city(2) := '경기';
    v_city(3) := '부산';
    v_city(4) := '광주';

    DBMS_OUTPUT.PUT_LINE(v_city(1));        -- 방번호 내 값 출력
    DBMS_OUTPUT.PUT_LINE(v_city(2));
    DBMS_OUTPUT.PUT_LINE(v_city(3));
    DBMS_OUTPUT.PUT_LINE(v_city(4));

    FOR i IN v_city.first..v_city.last LOOP     -- first ~ last 내 값 출력
        DBMS_OUTPUT.PUT_LINE(v_city(i));
    END LOOP;

    v_city(4) := '전주';      -- 수정
    v_city(5) := '인천';      -- 추가

    FOR i IN v_city.first..v_city.last LOOP
        DBMS_OUTPUT.PUT_LINE(v_city(i));
    END LOOP;
END;
/

 

DECLARE
    TYPE tab_char_type IS TABLE OF varchar2(10) INDEX BY pls_integer;
    v_city tab_char_type;  
BEGIN
    v_city(1) := '서울';
    v_city(2) := '경기';
    v_city(3) := '부산';
    v_city(4) := '광주';

    v_city.delete(3);   -- 배열의 n번 요소를 삭제

    FOR i IN v_city.first..v_city.last LOOP        -- 오류 : first : 1, last : 4인데 3번을 찾으려고 하니 없어서 오류가 남
        DBMS_OUTPUT.PUT_LINE(v_city(i));
    END LOOP;

END;
/
>> ORA-01403: no data found
DECLARE
    TYPE tab_char_type IS TABLE OF varchar2(10) INDEX BY pls_integer;
    v_city tab_char_type;  
BEGIN
    v_city(1) := '서울';
    v_city(2) := '경기';
    v_city(3) := '부산';
    v_city(4) := '광주';

    v_city.delete(3);

    FOR i IN v_city.first..v_city.last LOOP
        IF v_city.EXISTS(i) THEN         -- 존재여부체크
            DBMS_OUTPUT.PUT_LINE(v_city(i));
        ELSE
            DBMS_OUTPUT.PUT_LINE(i||' 요소는 존재하지 않습니다.');
        END IF;
    END LOOP;

END;
/


# 배열 요소 중에 없는 요소를 참조하게 되면 NO_DATA_FOUND 오류 발생한다.
배열변수.EXISTS(n) : n번 요소가 존재하면 TRUE, 존재하지 않으면 FALSE 리턴한다.

# 배열.delete : 배열의 모든 요소 삭제
  배열.delete(n) : 배열의 n번 요소를 삭제
  배열.delete(m,n) : 배열의 m~n까지 요소 삭제

DECLARE
    TYPE table_id_type IS TABLE OF number INDEX BY pls_integer;
    v_tab table_id_type;
BEGIN
    v_tab(1) := 100;
    v_tab(2) := 200;
    
    UPDATE hr.employees
    SET salary = salary * 1.1
    WHERE employee_id = v_tab(1);   -- 실행계획 1

    UPDATE hr.employees
    SET salary = salary * 1.1
    WHERE employee_id = v_tab(2);   -- 실행계획 2
END;
/
ROLLBACK;
↓ 비슷한 실행계획을 묶어보자 >> 배열 + 반복문 사용
DECLARE
    TYPE table_id_type IS TABLE OF number INDEX BY pls_integer;
    v_tab table_id_type;
BEGIN
    v_tab(1) := 100;
    v_tab(2) := 200;
    
    FOR i IN v_tab.first..v_tab.last LOOP
        UPDATE hr.employees
        SET salary = salary * 1.1
        WHERE employee_id = v_tab(i);
    END LOOP;

END;
/

 

-- 인덱스 번호가 순차적이지 않을 경우

DECLARE
    TYPE table_id_type IS TABLE OF number INDEX BY pls_integer;
    v_tab table_id_type;
BEGIN
    v_tab(1) := 100;
    v_tab(3) := 200;
    
    FOR i IN v_tab.first..v_tab.last LOOP
        IF v_tab.EXISTS(i) THEN         -- 존재여부체크
            UPDATE hr.employees
            SET salary = salary * 1.1
            WHERE employee_id = v_tab(i);
        ELSE
            DBMS_OUTPUT.PUT_LINE(i||' 요소는 존재하지 않습니다.');
        END IF;
    END LOOP;
END;
/

# 배열 반복 사용 시 EXISTS 습관적으로 사용하자.

 



SCALOR 변수

employee_id

 

레코드 변수 v_rec  -> 테이블 칼럼 순서대로 작성

detp_id dept_name  dept_mgr  dept_loc 

 

배열 1차원

employee_id(1)
employee_id(2)
employee_id(3)
employee_id(4)

 

배열 2차원

detp_id dept_name  dept_mgr dept_loc
. . . .
. . . .
. . . .

 

'Oracle SQL' 카테고리의 다른 글

18 PL/SQL EXCEPTION  (1) 2023.10.31
17 PL/SQL Nested Table, VARRAY, Explicit Cursor  (0) 2023.10.30
15 PL/SQL WHILE LOOP, FOR, Implicit Cursor  (1) 2023.10.26
14 PL/SQL Variable, CASE, LOOP  (0) 2023.10.25
13-1 SAVEPOINT  (0) 2023.10.24