251106 TIL
2025. 11. 6. 18:10ㆍCourses/아이티윌 오라클 DBA 과정
SELECT last_name, salary, hire_date
FROM hr.employees
WHERE employee_id = 100;
BEGIN
SELECT last_name, salary, hire_date
FROM hr.employees
WHERE employee_id = 100;
END;
/
--PLS-00428: an INTO clause is expected in this SELECT statement
PLSQL의 SQL문
- SELECT 문을 사용하여 데이터베이스에 있는 데이터를 조회
- DML문을 사용하여 데이터베이스에 있는 데이터를 입력, 수정, 삭제, 병합
- COMMIT, ROLLBACK, SAVEPOINT 문을 사용하여 트랜잭션을 제어
SQL CURSOR
- CURSOR : SQL 문 실행 메모리 영역(parse, bind, execute, fetch)
- implicit cursor(암시적 커서) : 오라클 서버가 SQL문(SELECT, DML) 처리하기 위해 내부적으로 커서를 생성하고 관리
- explicit cursor(명시적 커서) : 개발자가 명시적으로 커서를 선언하고 관리

implicit cursor(암시적 커서)
- 오라클 서버가 SQL문(SELECT, DML) 처리하기 위해 내부적으로 커서를 생성하고 관리
SELECT … INTO …
- INTO 절이 FETCH절
- 반드시 1개 행만 FETCH 해야 함
- 0개 : NO_DATA_FOUND 오류 발생
- 2개 이상 : TOO_MANY_ROWS 오류 발생
DECLARE
v_name varchar2(30);
v_sal number;
v_date date;
BEGIN
SELECT last_name, salary, hire_date
INTO v_name, v_sal, v_date
FROM hr.employees
WHERE employee_id = 100;
dbms_output.put_line(v_name || ' ' || v_sal || ' ' || v_date);
END;
/
- 1건이 아닌 데이터 fetch 시 오류 발생
-- ORA-01403: no data found
DECLARE
v_name varchar2(30);
v_sal number;
v_date date;
BEGIN
SELECT last_name, salary, hire_date
INTO v_name, v_sal, v_date
FROM hr.employees
WHERE employee_id = 300;
dbms_output.put_line(v_name || ' ' || v_sal || ' ' || v_date);
END;
/
-- ORA-01422: exact fetch returns more than requested number of rows
DECLARE
v_name varchar2(30);
v_sal number;
v_date date;
BEGIN
SELECT last_name, salary, hire_date
INTO v_name, v_sal, v_date
FROM hr.employees
WHERE department_id = 20;
dbms_output.put_line(v_name || ' ' || v_sal || ' ' || v_date);
END;
/
%type
테이블명.컬럼이름%TYPE: 테이블 컬럼의 데이터 타입과 사이즈를 사용하겠다는 의미변수명%TYPE: 변수의 타입과 사이즈를 사용하겠다는 의미- 컬럼의 타입을 몰라도 사용이 가능하고, 타입이 변경되더라도 변경 작업이 필요하지 않기 때문에 유지 관리에 용이
DECLARE
v_lname employees.last_name%type;
v_fname v_lname%type;
v_sal employees.salary%type;
v_date employees.hire_date%type;
BEGIN
SELECT last_name, first_name, salary, hire_date
INTO v_lname, v_fname, v_sal, v_date
FROM hr.employees
WHERE employee_id = 100;
dbms_output.put_line(v_lname || ' ' || ' ' || v_fname || ' ' || v_sal || ' ' || v_date);
END;
/
문제
-- [문제10] 사원 번호를 입력값으로 받아서 last_name, salary, hire_date 정보를 출력하는 프로그램을 작성하세요.
/*
이름 : KING
급여 : ₩24,000.00
입사일 : 2003년 6월 17일
*/
DECLARE
v_last_name employees.last_name%type;
v_salary employees.salary%type;
v_hire_date employees.hire_date%type;
BEGIN
SELECT last_name, salary, hire_date
INTO v_last_name, v_salary, v_hire_date
FROM hr.employees
WHERE employee_id = :b_employee_id;
dbms_output.put_line('이름 : ' || v_last_name);
dbms_output.put_line('급여 : ' || to_char(v_salary, 'FML999G999D99'));
dbms_output.put_line('입사일 : ' || to_char(v_hire_date, 'yyyy"년" fmmm"월" dd"일"'));
dbms_output.put_line('입사일 : ' || to_char(v_hire_date, 'DL'));
dbms_output.put_line('입사일 : ' || to_char(v_hire_date, 'DS'));
END;
/

FM: 공백이나 0을 제거해줌DL: 지역 설정(NLS_TERRITORY, NLS_LANGUAGE)에 따른 긴 날짜 형식 제공DS: 지역 설정(NLS_TERRITORY, NLS_LANGUAGE)에 따른 짧은 날짜 형식 제공
DML(INSERT, UPDATE, DELETE ,MERGE)
BEGIN
INSERT INTO hr.test(id, name, day) VALUES(1, 'ORACLE', SYSDATE);
END;
/
COMMIT; -- or ROLLBACK;
- 프로그램이 끝나더라도 트랜잭션은 진행 중 → COMMIT or ROLLBACK을 프로그램 내 또는 외부에서 처리해줘야 함
BEGIN
INSERT INTO hr.test(id, name, day) VALUES(:b_id, :b_name, to_date(:b_day, 'yyyy-mm-dd'));
END;
/
- 바인드 변수는 number 또는 character 타입만 받을 수 있으므로 날짜 형식의 경우 문자형으로 받은 후 바인드 변수에 to_char()를 적용해 날짜형으로 변환
암시적 커서 속성
sql%rowcount
- DML문에 의해 영향을 받은 행의 수를 리턴하는 속성, 정수값
-- 입력 행 수
BEGIN
INSERT INTO hr.test(id, name, day)
SELECT employee_id, last_name, hire_date
FROM hr.employees;
dbms_output.put_line(sql%rowcount || ' 행이 입력되었습니다.');
END;
/
COMMIT;
-- 수정 행 수
BEGIN
UPDATE hr.test
SET day = sysdate
WHERE id in (100, 200);
dbms_output.put_line(sql%rowcount || ' 행이 수정되었습니다.');
ROLLBACK; -- 테스트 목적이므로 결과만 보고 롤백
END;
/
sql%found
- DML 문에 의해 영향을 받은 행이 있으면 TRUE, 없으면 FALSE로 평가하는 속성, BOOL
sql%notound
- DML 문에 의해 영향을 받은 행이 없으면 TRUE, 있으면 FALSE로 평가하는 속성, BOOL
BEGIN
UPDATE hr.test
SET day = sysdate
WHERE id = 300;
IF sql%found THEN
dbms_output.put_line(sql%rowcount || ' 행이 수정되었습니다.');
ELSE
dbms_output.put_line('사원이 존재하지 않습니다');
END IF;
ROLLBACK;
END;
/
BEGIN
DELETE FROM hr.test WHERE day < to_date('2005-01-01', 'yyyy-mm-dd');
IF sql%found THEN
dbms_output.put_line(sql%rowcount || ' 행이 삭제되었습니다.');
ELSE
dbms_output.put_line('사원이 존재하지 않습니다');
END IF;
ROLLBACK;
END;
/
문제
-- [문제11] 사원번호를 입력값으로 받아서 근속연수가 20년 이상이면 10% 인상급여로 수정하는 프로그램을 생성해주세요.
/*
100사원의 입사일은 2003-06-17 근속연수는 21년입니다.
100사원의 이전 급여는 24000, 수정된 급여는 26400
*/
DECLARE
v_date varchar(30);
v_year number;
v_sal emp.salary%type;
v_new_sal v_sal%type;
BEGIN
SELECT to_char(hire_date, 'yyyy-mm-dd'), trunc(months_between(sysdate, hire_date) / 12), salary
INTO v_date, v_year, v_sal
FROM hr.emp
WHERE employee_id = :b_id;
dbms_output.put_line(:b_id || '사원의 입사일은 ' || v_date || ' 근속연수는 ' || v_year || '년입니다.');
IF v_year >= 20 THEN
UPDATE hr.emp
SET salary = salary * 1.1
WHERE employee_id = :b_id;
SELECT salary
INTO v_new_sal
FROM hr.emp
WHERE employee_id = :b_id;
dbms_output.put_line(:b_id || '사원의 이전 급여는 ' || v_sal || ', 수정된 급여는 ' || v_new_sal || '입니다.');
ELSE
dbms_output.put_line(:b_id || '사원의 급여는 수정할 수 없습니다.');
END IF;
ROLLBACK;
END;
/
조합 데이터 유형
- 스칼라 유형(scalar data type) : 단일 값을 보유하는 변수
- 스칼라 유형과 달리 다중 값을 보유할 수 있는 변수
- 레코드(record) : 서로 다른 데이터 유형의 값을 저장
- 배열(array) : 동일한 데이터 유형의 값을 저장
- index by table
- nested table
- varray
레코드(record)
-- record 사용 전
DECLARE
v_dept_id departments.department_id%type;
v_dept_name departments.department_name%type;
v_dept_mgr departments.manager_id%type;
v_dept_loc departments.location_id%type;
BEGIN
SELECT department_id, department_name, manager_id, location_id
INTO v_dept_id, v_dept_name, v_dept_mgr, v_dept_loc
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_dept_mgr);
dbms_output.put_line('부서위치 : ' || v_dept_loc);
END;
/
-- record 사용 후
DECLARE
/* 레코드 유형 선언, field 구성 */
TYPE dept_record_type IS RECORD(
dept_id departments.department_id%type,
dept_name departments.department_name%type,
dept_mgr departments.manager_id%type,
dept_loc departments.location_id%type
);
v_rec dept_record_type;
BEGIN
SELECT department_id, department_name, manager_id, location_id
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
- 테이블 또는 뷰에서 컬럼 및 데이터 타입을 가지고 레코드 타입을 선언
- 레코드 타입을 손쉽게 사용 가능
- 컬럼의 개수, 타입을 알 필요 없음
- SELECT *
- 행 레벨 INSERT, UPDATE
DECLARE
v_rec 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(
sal number,
minsal number default 1000,
day date,
rec employees%rowtype);
v_rec rec_type;
BEGIN
v_rec.sal := v_rec.minsal + 500;
v_rec.day := sysdate;
SELECT *
INTO v_rec.rec
FROM hr.employees
WHERE employee_id = 100;
dbms_output.put_line(v_rec.sal);
dbms_output.put_line(v_rec.minsal);
dbms_output.put_line(v_rec.day);
dbms_output.put_line(v_rec.rec.last_name);
END;
/
행 레벨 INSERT
DECLARE
v_rec employees%rowtype;
BEGIN
SELECT *
INTO v_rec
FROM hr.employees
WHERE employee_id = 100;
INSERT INTO hr.retired_emp VALUES v_rec;
END;
/
행 레벨 UPDATE
DECLARE
v_rec employees%rowtype;
BEGIN
SELECT *
INTO v_rec
FROM hr.employees
WHERE employee_id = 100;
v_rec.first_name := upper(v_rec.first_name);
v_rec.last_name := upper(v_rec.last_name);
v_rec.commission_pct:= 0.1;
v_rec.hire_date := sysdate;
v_rec.department_id := 10;
UPDATE hr.retired_emp
SET row = v_rec
WHERE employee_id = 100;
END;
/
row키워드 사용
'Courses > 아이티윌 오라클 DBA 과정' 카테고리의 다른 글
| 251105 TIL (0) | 2025.11.05 |
|---|---|
| 251104 TIL (0) | 2025.11.04 |
| 251103 TIL (0) | 2025.11.03 |
| 251031 TIL (1) | 2025.10.31 |
| 251030 TIL (0) | 2025.10.30 |