<FETCH 절>
DECLARE
CURSOR emp_cur(p_id number) IS
SELECT *
FROM hr.employees
WHERE department_id = p_id;
TYPE tab_type IS TABLE OF emp_cur%rowtype;
v_rec tab_type;
TYPE v_array_type IS TABLE OF number;
v_array v_array_type := v_array_type(30,40);
BEGIN
FOR i IN v_array.first..v_array.last LOOP
dbms_output.put_line('----------------');
OPEN emp_cur(v_array(i));
FETCH emp_cur BULK COLLECT INTO v_rec;
CLOSE emp_cur;
FOR i in v_rec.first..v_rec.last LOOP
dbms_output.put_line(v_rec(i).last_name);
END LOOP;
END LOOP;
END;
/
<RETURNING 절>
BEGIN
UPDATE hr.employees
SET salary = salary*1.1
WHERE department_id = 30
RETURNING last_name,salary BULK COLLECT INTO v_array;
FOR i IN v_array.first..v_array.last LOOP
dbms_output.put_line(v_array(i).a ||' ' || v_array(i).b);
END LOOP;
ROLLBACK;
END;
/
- PL/SQL엔진에서 SQL 엔진사이의 문맥전환을 줄이는 문이다.
- 배열변수에 있는 값을 이용해서 DML문장을 반복해서 수행할 경우 문맥전환이 발생할때 성능상 문제가 발생해서 이를 해결하고자 FORALL문을 이용하면 PLSQL엔진이 DML문을 바인드 하여 SQL엔진한테 보내면 문맥전환이 한번으로 끝난다.
- FORALL 문 안에는 DML 문만 가능하다.
개별로 DML문에 영향을 받은 행 수 반환
DECLARE
TYPE id_type IS TABLE OF number;
v_num id_type := id_type(10,20,30);
BEGIN
FORALL i IN v_num.first..v_num.last -- 배열변수에 있는 값을 한번에 전달
-- 암시적 커서가 돌아가고 있다.
DELETE FROM hr.emp WHERE department_id = v_num(i);
dbms_output.put_line(sql%rowcount); -- DML문으로 영향을 받은 전체 건수가 나옴
FOR j IN v_num.first..v_num.last LOOP
dbms_output.put_line(sql%bulk_rowcount(j) || 'row count'); --bulk_rowcout 개별 처리된 row건수 확인
END LOOP;
ROLLBACK;
END;
/
- 실행중에 발생한 PL/SQL 오류이다.
- 오라클에 의해 암시적으로 발생한다.
- 프로그램에 의해 명시적으로 발생할 수 있다.
DECLARE
v_id number :=300 ;
v_rec hr.employees%rowtype;
BEGIN
SELECT *
INTO v_rec
FROM hr.employees
WHERE employee_id = v_id;
dbms_output.put_line(v_rec.employee_id || ' ' ||v_rec.last_name);
EXCEPTION
WHEN no_data_found THEN -- 오류번호로는 예외처리를 할 수 없고 오류 이름으로만 처리할 수 있다.
dbms_output.put_line(v_id || '사원은 존재하지 않습니다.');
END;
/

DECLARE
v_id number :=20 ;
v_rec hr.employees%rowtype;
BEGIN
SELECT *
INTO v_rec
FROM hr.employees
WHERE department_id = v_id;
dbms_output.put_line(v_rec.employee_id || ' ' ||v_rec.last_name);
EXCEPTION
WHEN no_data_found THEN -- 오류번호로는 예외처리를 할 수 없고 오류 이름으로만 처리할 수 있다.
dbms_output.put_line(v_id || '사원은 존재하지 않습니다.');
WHEN others THEN
dbms_output.put_line(sqlcode);
dbms_output.put_line(sqlerrm);
END;
/
DECLARE
pk_error EXCEPTION; -- EXCEPTION 이름 선언
PRAGMA EXCEPTION_INIT(pk_error,-2292); -- 오라클의 오류 번호와 내가 선언한 exception 이름을 붙이는 작업
BEGIN
DELETE FROM hr.departments WHERE department_id = 10;
EXCEPTION
WHEN pk_error THEN
dbms_output.put_line('참조무결성 제약조건을 위반하여 삭제할 수 없습니다.');
END;
/
BEGIN
UPDATE hr.employees
SET salary = salary*1.1
WHERE employee_id = 100;
DELETE FROM hr.departments WHERE department_id = 10; -- 오류발생
END;
/
DECLARE
pk_error EXCEPTION;
PRAGMA EXCEPTION_INIT(pk_error,-2292);
BEGIN
UPDATE hr.employees
SET salary = salary*1.1
WHERE employee_id = 100;
DELETE FROM hr.departments WHERE department_id = 10;
EXCEPTION
WHEN pk_error THEN
dbms_output.put_line('참조무결성 제약조건을 위반하여 삭제할 수 없습니다.');
--commit 또는 rollback;
END;
/
DECLARE
pk_error EXCEPTION;
PRAGMA EXCEPTION_INIT(pk_error,-2292);
BEGIN
UPDATE -- transaction 시작
--중첩 BLOCK 수행
BEGIN
DELETE --오류 발생 ORA-2292
EXCEPTION
WHEN pk_error THEN
dbms_output.put_line('참조무결성 제약조건을 위반하여 삭제할 수 없습니다.');
END;
SELECT -- 수행 가능
DML -- 수행 가능
EXCEPTION
WHEN otehrs THEN
...
--commit 또는 rollback;
END;
/
RAISE 예외사항이름 으로 강제 예외처리DECLARE
e_invalid EXCEPTION; -- 사용자가 정의한 예외변수
BEGIN
UPDATE hr.employees
SET salary = salary*1.1
WHERE employee_id = 300;
IF sql%NOTFOUND THEN
RAISE e_invalid; -- 사용자가 발생시킨 예외사항
END IF;
EXCEPTION
WHEN e_invalid THEN
dbms_output.put_line('사원번호를 체크해주세요.');
END;
/
BEGIN
UPDATE hr.employees
SET salary = salary*1.1
WHERE employee_id = 300;
IF sql%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20000,'수정된 데이터가 없습니다.');
END IF;
END;
/

<true 옵션>
DECLARE
v_id number :=300 ;
v_rec hr.employees%rowtype;
BEGIN
SELECT *
INTO v_rec
FROM hr.employees
WHERE employee_id = v_id;
dbms_output.put_line(v_rec.employee_id || ' ' ||v_rec.last_name);
EXCEPTION
WHEN no_data_found THEN -- 오류번호로는 예외처리를 할 수 없고 오류 이름으로만 처리할 수 있다.
RAISE_APPLICATION_ERROR(-20000,'조회 데이터가 없습니다',true);
END;
/

기본 오라클 오류와 내가 만든 오류가 둘다 나온걸 볼 수 있다.
- FORALL 절안에서 오류가 발생했더라도 해당 오류부분 이후도 수행 하고 싶을때 사용
FORALL..IN.. SAVE EXCEPTIONS- FORALL 문안에서 어떤 오류가 발생 할지 모르니깐 오류 선언 할때는 무조건 -24381 번호로 오류 선언을 해야함
DECLARE
TYPE num_type IS TABLE OF number;
v_num num_type := num_type(10,11,0,12,30,0,20,199,2,0);
dml_error EXCEPTION;
PRAGMA EXCEPTION_INIT(dml_error,-24381);
BEGIN
FORALL i IN v_num.first..v_num.last SAVE EXCEPTIONS -- SAVE EXCEPTONS를 해야 오류난 부분 이후를 수행한다.
DELETE FROM hr.emp WHERE salary > 500000/v_num(i); --24381, FORALL 문안에서 고정된 오류번호
EXCEPTION
WHEN dml_error THEN
FOR i IN v_num.first..v_num.last LOOP
dbms_output.put_line(v_num(i) || ' ' ||sql%bulk_rowcount(i));
END LOOP;
ROLLBACK;
END;
/

DECLARE
TYPE num_type IS TABLE OF number;
v_num num_type := num_type(10,11,0,12,30,0,20,199,2,0);
dml_error EXCEPTION;
PRAGMA EXCEPTION_INIT(dml_error,-24381);
BEGIN
FORALL i IN v_num.first..v_num.last SAVE EXCEPTIONS -- SAVE EXCEPTONS를 해야 오류난 부분을 저장
DELETE FROM hr.emp WHERE salary > 500000/v_num(i); --24381, FORALL 문안에서 고정된 오류번호
EXCEPTION
WHEN dml_error THEN
.put_line('number of errors is '||sql%bulk_exceptions.count); -- FORALL문에서 오류난 갯수를 리턴해줌
ROLLBACK;
END;
/

DECLARE
TYPE num_type IS TABLE OF number;
v_num num_type := num_type(10,11,0,12,30,0,20,199,2,0);
dml_error EXCEPTION;
PRAGMA EXCEPTION_INIT(dml_error,-24381);
BEGIN
FORALL i IN v_num.first..v_num.last SAVE EXCEPTIONS -- SAVE EXCEPTONS를 해야 오류난 부분을 저장
DELETE FROM hr.emp WHERE salary > 500000/v_num(i); --24381, FORALL 문안에서 고정된 오류번호
EXCEPTION
WHEN dml_error THEN
dbms_output.put_line('number of errors is '||sql%bulk_exceptions.count); -- FORALL문에서 오류난 갯수를 리턴해줌
FOR j in 1..sql%bulk_exceptions.count LOOP
dbms_output.put_line(sql%bulk_exceptions(j).error_index); -- 배열의 어떤 인덱스 때문에 오류가 발생 한지 파악
dbms_output.put_line(-sql%bulk_exceptions(j).error_code); -- 실제 divisor is equal to zero의 오류번호는 -1476
dbms_output.put_line(sqlerrm(-sql%bulk_exceptions(j).error_code)); -- 오류코드번호의 오류메시지 리턴
END LOOP;
ROLLBACK;
END;
/
