제약 조건 조회
DROP TABLE HR.TEST PURGE;
CREATE TABLE hr.test(id number(1) PRIMARY KEY);
SELECT * FROM user_constraints WHERE table_name = 'TEST';
배열 값의 데이터를 입력하는 도중 예외사항 발생하더라도 끝까지 실행하고 싶으면?
DECLARE
TYPE t_id IS TABLE OF NUMBER;
v_id t_id := t_id(1,2,3,4,5,1,6,10,7);
BEGIN
FOR i IN v_id.first..v_id.last LOOP
BEGIN
INSERT INTO hr.test(id) VALUES (v_id(i));
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm); --오류 내용 출력
END;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm); --오류 내용 출력
END;
/
ROLLBACK;
DECLARE
TYPE t_id IS TABLE OF NUMBER;
v_id t_id := t_id(1,2,3,4,5,21,1,1,6,10,7);
dml_err EXCEPTION;
PRAGMA EXCEPTION_INIT(dml_err, -24381);
BEGIN
FORALL i IN v_id.first..v_id.last SAVE EXCEPTIONS
INSERT INTO hr.test(id) VALUES (v_id(i));
EXCEPTION
WHEN dml_err THEN--오류 내용 출력
FOR i in 1..sql%bulk_exceptions.count loop
dbms_output.put_line(sql%bulk_exceptions(i).error_index);
DBMS_OUTPUT.PUT_LINE(sqlerrm(-sql%bulk_exceptions(i).error_code));-- 오류코드의 오름차 순으로 출력
end LOOP;
END;
/
배열의 중간에 값이 없다. 즉 갭이 있는 경우 forall 이용해서 대량의 데이터 입력시 오류 발생. -> 비정상적인 종료 발생(auto rollback)

DECLARE
TYPE t_tab IS TABLE OF test%rowtype index by pls_integer;
v_tab t_tab;
dml_err exception;
pragma exception_init(dml_err,-24381);
BEGIN
SELECT object_id, object_name
BULK COLLECT INTO v_tab
FROM all_objects
WHERE rownum <= 10;
dbms_output.put_line(v_tab.count);
v_tab.delete(2);
v_tab.delete(4);
v_tab.delete(6);
dbms_output.put_line(v_tab.count);
FORALL i in v_tab.first..v_tab.last
INSERT INTO hr.test values v_tab(i);
rollback;
EXCEPTION
WHEN dml_err THEN--오류 내용 출력
for i in v_tab.first..v_tab.last loop
dbms_output.put_line(sql%bulk_rowcount(i));
end loop;
FOR i in 1..sql%bulk_exceptions.count loop
dbms_output.put_line(sql%bulk_exceptions(i).error_index);
DBMS_OUTPUT.PUT_LINE(sqlerrm(-sql%bulk_exceptions(i).error_code));-- 오류코드의 오름차 순으로 출력
end LOOP;
END;
/
예외처리가 너무 길다.
예외처리를 하지 않고 forall 문에서 한번에 처리 가능. 즉 오류를 발생시키지 않음.
DECLARE
TYPE t_tab IS TABLE OF test%rowtype index by pls_integer;
v_tab t_tab;
BEGIN
SELECT object_id, object_name
BULK COLLECT INTO v_tab
FROM all_objects
WHERE rownum <= 10;
v_tab.delete(2);
v_tab.delete(4);
v_tab.delete(6);
FORALL i IN INDICES OF v_tab
INSERT INTO hr.test values v_tab(i);
END;
/
하지만 오류를 발생시키지 않기 때문에 어떤 배열에서 오류를 발생시켰는지 확인하기 위해서는 기존의 방식을 활용해야 한다.
자주 사용하는 익명 블록구조를 다시 불러올 때 마다 PARSE과정을 거쳐 컴파일 해야한다.
즉 데이터베이스에 저장되어 있지 않는다. 소스코드를 우리가 가져가야 한다.
다른 응용프로그램에서도 해당 프로그램을 호출할 수 없다.
입력값, RETURN값 처리를 바인드 변수를 이용해야 한다. (PARAMETER 사용 불가)
DECLARE
v_rec hr.employees%rowtype;
BEGIN
SELECT *
INTO v_rec
FROM hr.employees
WHERE employee_id = :b_id;
dbms_output.put_line(v_rec.employee_id || ' ' || v_rec.salary);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE(:b_id || '에 해당하는 사원은 존재하지 않는다.');
END;
/
이러한 문제를 SUB PROGRAM을 이용해 극복하고자 한다.
특정 작업을 수행하는 서브 프로그램으로 데이터베이스에 객체로 저장된다.
DB에 객체로 저장되기 떄문에 재사용성과 유지관리에 대한 편리성이 보장된다.
이름이 있는 PL/SQL 구조이다.
데이터베이스에 저장되어 한 번만 컴파일 한 후 호출시에는 컴파일된 코드를 사용한다.
이름도 존재하기 때문에 다른 프로그램에서도 호출할 수 있다.
PARAMETER를 이용해 입력값, RETURN값을 처리할 수 있다.
-내가 받은 system권한
select * from session_privs;
에서 CREATE PROCEDURE가 있어야지만 PROCEDURE, FUNCTION, PACKAGE를 생성할 수 있다.

CREATE OR REPLACE PROCEDURE emp_proc(p_id IN number) --procedure with formal parameter
IS -- DECLARE 대신에 사용
v_rec hr.employees%rowtype;
BEGIN
SELECT *
INTO v_rec
FROM hr.employees
WHERE employee_id = p_id;
dbms_output.put_line(v_rec.employee_id || ' ' || v_rec.salary);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE(p_id || '에 해당하는 사원은 존재하지 않는다.');
END;
/
select *
from user_source
where name = 'EMP_PROC'
ORDER BY LINE;
또는

또는
select * from user_objects where object_name = 'EMP_PROC';
(변수 모드 데이터타입)
파라미터 모드 (in, out, in out)
in : 입력값 받겠다. 입력값을 처리하는 기능. 호출환경에서 프로시저로 값을 전달한다. 입력된 실제 파라미터 값은 반드시 상수값으로 동작한다. 임의로 수정 X ( 생략 가능 - 기본값 in모드)
out : 결과값 주겠다. 프로그램 안에서 값을 호출환경으로 전달한다. return. 호출 환경에서 받아올 변수가 필요하다. 변수로 동작한다.
in out : 변수로 동작한다. 입력된 초기 value를 수정하여 갱신 , return 할 수 있다.
호출 서브프로그램의 파라미터 리스트에 사용되는 실제 값.
파라미터 안에 들어간 실제 매개변수로 입력된 파라미터.
생성한 프로시저를 사용하는 방법은 execute를 이용해서 해당 프로시저를 실행할 수 있다.
EXEC emp_proc(100) -- procedure with actual parameter
또는 다른 프로그램에서 호출하여 사용할 수 있다.
BEGIN
emp_proc(100); -- procedure with actual parameter
END;
/
CREATE OR REPLACE PROCEDURE emp_proc(p_id number, p_name out varchar2, p_sal out number)
is
-- 선언할 거 없더라도 is절 반드시 선언해야 함
begin
select last_name, salary
into p_name, p_sal -- out모드의 변수에 값 할당
from hr.employees
where employee_id = p_id;
dbms_output.put_line(p_name || ' ' || p_sal);
exception
when no_data_found then
dbms_output.put_line(p_id ||'번 사원은 존재하지 않는다.');
end;
/
DECLARE
v_name varchar2(30);
v_sal number;
BEGIN
emp_proc(100,v_name,v_sal);
dbms_output.put_line(v_name);
dbms_output.put_line(v_sal);
END;
/
또는 바인드 변수 사용 가능
var b_name varchar2(30);
var b_sal number;
exec emp_proc(100,:b_name,:b_sal)
print :b_name, b_sal
CREATE OR REPLACE PROCEDURE p_time(id in out number)
IS
BEGIN
id := id * 10; -- 입력 값을 * 10 하여 수정함.
END;
/
또는
CREATE OR REPLACE PROCEDURE phone(p_phone in out varchar2)
is
begin
p_phone := substr(p_phone,1,3)||'-'
||substr(p_phone, 4,4)||'-'
||substr(p_phone, 8);
end;
/


프로시저에서 오류가 발생할 때 컴파일러에서 오류를 보여주는 명령어.
프로시저가 컴파일 실패하더라도 코드소스 객체정보는 딕셔너리에 저장되어 user_object, user_source에서 확인할 수 있다.
다음 프로세서에 대하여...
CREATE OR REPLACE PROCEDURE insert_proc(id in number, name in varchar2, day in date default sysdate, deptno in number default 10)
is
begin
insert into hr.test values (id, name,day,deptno);
end insert_proc; --사용 가능 / 트랜잭션 살아있음
/
exec insert_proc(1,'scott',to_date('2022-01-01','yyyy-mm-dd'), 10);
exec insert_proc(id => 1,name => 'scott', deptno => 10);
특정 매개변수에 대해 defualt 값을 넣고 싶을 때 사용할 수 있다.
exec insert_proc(3,name => 'khazan', deptno => 30);
이름 지정방식 사용 이후에는 위치지정방식을 사용할 수 없다.
exec insert_proc(3,name => 'khazan', sysdate, deptno => 30); -- 오류 발생
일지 작업 하는 습관.