본문 바로가기

oracle10R2

Advanced Product Service 10g Develop PLP 02

7.10 Returining Clause

CREATE or replace PROCEDURE update_salary(emp_id NUMBER) IS

name emp.ename%TYPE;

new_sal number(10);

BEGIN

UPDATE emp

SET sal = sal * 1.1

WHERE empno = emp_id

RETURNING ename, sal INTO name, new_sal;

dbms_output.put_line ('ename: ' || name ||' sal: ' || new_sal);

END update_salary;

/


7.16 Bulk Binding (forall)

à For 문과 forall 문의 속도차이를 경험하자!! (set timing on 설정)

CREATE TABLE TEST_FORALL

(

PROD NUMBER (6),

PROD_DESC VARCHAR2 (30)

);

ALTER TABLE TEST_FORALL ADD PRIMARY KEY (PROD);

1. FOR LOOP 이용

BEGIN

FOR v_Count IN 1..5000 loop

INSERT INTO test_forall VALUES

(v_Count, 'Product Description #' || v_Count);

END LOOP;

END;

/

2. FORALL 이용

DECLARE

TYPE t_Prods IS TABLE OF test_forall.PROD%TYPE;

TYPE t_Descs IS TABLE OF test_forall.PROD_DESC%TYPE;

v_Prods t_Prods := t_Prods(1);

v_Descs t_Descs := t_Descs(1);

BEGIN

v_Prods.EXTEND(500000);

v_Descs.EXTEND(500000);

FOR v_Count IN 1..5000 LOOP

v_Prods(v_Count) := v_Count;

v_Descs(v_Count) := 'Product Description #' || v_Count;

END LOOP;

FORALL v_Count IN 1..5000

INSERT INTO test_forall VALUES

(v_Prods(v_Count), v_Descs(v_Count));

END;

/


7.18 Bulk collect into

CREATE or replace PROCEDURE get_departments(loc number ) is

TYPE dept_tabtype IS

TABLE OF hr.departments%ROWTYPE;

depts dept_tabtype;

BEGIN

SELECT * BULK COLLECT INTO depts

FROM hr.departments

WHERE location_id = loc;

FOR I IN 1 .. depts.COUNT LOOP

DBMS_OUTPUT.PUT_LINE(depts(i).department_id ||' '||depts(i).department_name);

END LOOP;

END;

/

SQL> exec get_departments(1700);

7.18 Fetch using Bulk collect into

CREATE or replace PROCEDURE get_departments(loc NUMBER) IS

CURSOR dept_csr IS SELECT * FROM hr.departments

WHERE location_id = loc;

TYPE dept_tabtype IS TABLE OF dept_csr%ROWTYPE;

depts dept_tabtype;

BEGIN

OPEN dept_csr;

FETCH dept_csr BULK COLLECT INTO depts;

CLOSE dept_csr;

FOR I IN 1 .. depts.COUNT LOOP

DBMS_OUTPUT.PUT_LINE(depts(i).department_id ||' '||depts(i).department_name);

END LOOP;

END;

/

SQL> exec get_departments(1700);

문제) 7-19 쪽 아래 limit 옵션을 보고 출력되는 row 3개로 제한해서 출력해보시요!

7.18 Fetch using Bulk collect into

declare

type Numlist is table of emp.empno%type;

type Bonlist is table of emp.sal%type;

id Numlist;

bl Numlist;

begin

select empno BULK COLLECT INTO id

from emp

where sal < 2000;

forall i in id.first..id.last

update emp set sal = sal * 1.1

where empno = id (i)

RETURNING sal BULK COLLECT INTO bl;

end;

/


7.21 Nocopy Hint

SQL> create or replace procedure delete_all_rows

(p_tab_name in varchar2, p_rows_del out number)

is

cursor_name integer;

begin

cursor_name := DBMS_SQL.OPEN_CURSOR;

DBMS_SQL.PARSE(cursor_name,'DELETE FROM ' || p_tab_name, DBMS_SQL.NATIVE);

p_rows_del := DBMS_SQL.EXECUTE(cursor_name);

DBMS_SQL.CLOSE_CURSOR(cursor_name);

end;

/

프로시저가 생성되었습니다.

경 과: 00:00:00.32

SQL> variable deleted number

SQL> execute delete_all_rows('EMP', :deleted)

PL/SQL 처리가 정상적으로 완료되었습니다.

경 과: 00:00:00.09

SQL> print deleted

DELETED

----------

14

SQL>

SQL>

SQL> create or replace procedure delete_all_rows

(p_tab_name in varchar2, p_rows_del out nocopy number)

is

cursor_name integer;

begin

cursor_name := DBMS_SQL.OPEN_CURSOR;

DBMS_SQL.PARSE(cursor_name,'DELETE FROM ' || p_tab_name, DBMS_SQL.NATIVE);

p_rows_del := DBMS_SQL.EXECUTE(cursor_name);

DBMS_SQL.CLOSE_CURSOR(cursor_name);

end;

/

프로시저가 생성되었습니다.

경 과: 00:00:00.25

SQL>

SQL>

SQL>

SQL> variable deleted number

SQL> execute delete_all_rows('EMP', :deleted)

PL/SQL 처리가 정상적으로 완료되었습니다.

경 과: 00:00:00.03

SQL>

SQL> rollback;

DECLARE

TYPE EmpTabTyp IS TABLE OF emp%ROWTYPE;

emp_tab EmpTabTyp := EmpTabTyp(NULL); -- initialize

t1 NUMBER;

t2 NUMBER;

t3 NUMBER;

PROCEDURE do_nothing1 (tab IN OUT EmpTabTyp) IS

BEGIN

NULL;

END;

PROCEDURE do_nothing2 (tab IN OUT NOCOPY EmpTabTyp) IS

BEGIN

NULL;

END;

BEGIN

SELECT * INTO emp_tab(1) FROM emp WHERE empno = 7788;

emp_tab.EXTEND(24999, 1); -- copy element 1 into 2..25000

t1 := DBMS_UTILITY.GET_TIME;

do_nothing1(emp_tab); -- pass IN OUT parameter

t2 := DBMS_UTILITY.GET_TIME;

do_nothing2(emp_tab); -- pass IN OUT NOCOPY parameter

t3 := DBMS_UTILITY.GET_TIME;

dbms_output.put_line('Call Duration (secs/100)');

dbms_output.put_line('--------------------');

dbms_output.put_line('Just IN OUT: ' || TO_CHAR(t2 - t1));

dbms_output.put_line('With NOCOPY: ' || TO_CHAR(t3 - t2));

end;

/

DECLARE

TYPE EmpTabTyp IS TABLE OF employees%ROWTYPE;

emp_tab EmpTabTyp := EmpTabTyp(NULL); -- initialize

t1 NUMBER;

t2 NUMBER;

t3 NUMBER;

PROCEDURE get_time (t OUT NUMBER) IS

BEGIN t := dbms_utility.get_time; END;

PROCEDURE do_nothing1 (tab IN OUT EmpTabTyp) IS

BEGIN NULL; END;

PROCEDURE do_nothing2 (tab IN OUT NOCOPY EmpTabTyp) IS

BEGIN NULL; END;

BEGIN

SELECT * INTO emp_tab(1) FROM employees WHERE employee_id = 100;

emp_tab.EXTEND(49999, 1); -- copy element 1 into 2..50000

get_time(t1);

do_nothing1(emp_tab); -- pass IN OUT parameter

get_time(t2);

do_nothing2(emp_tab); -- pass IN OUT NOCOPY parameter

get_time(t3);

dbms_output.put_line('Call Duration (secs)');

dbms_output.put_line('--------------------');

dbms_output.put_line('Just IN OUT: ' || TO_CHAR((t2 - t1)/100.0));

dbms_output.put_line('With NOCOPY: ' || TO_CHAR((t3 - t2))/100.0);

END;

/


8.9 Displaying Dependencies

SQL> @?\rdbms\admin\utldtree.sql

SQL> execute deptree_fill('TABLE','SCOTT','EMPLOYEES');

PL/SQL 처리가 정상적으로 완료되었습니다.

경 과: 00:00:00.17

SQL>

SQL> select nested_level, type, name

from deptree

order by seq#;

8.13 Remote Dependencies

SQL> create view emp05

as

select empno, ename, sal, deptno

from emp;

SQL> CREATE OR REPLACE PROCEDURE pro1

( p_empno IN emp.empno%TYPE )

IS

v_empno emp.empno%TYPE;

v_ename emp.ename%TYPE;

v_sal emp.sal%TYPE;

BEGIN

SELECT empno, ename, sal

INTO v_empno, v_ename, v_sal

FROM emp

WHERE empno = p_empno ;

DBMS_OUTPUT.PUT_LINE( '사원번호 : ' || v_empno );

DBMS_OUTPUT.PUT_LINE( '사원이름 : ' || v_ename );

DBMS_OUTPUT.PUT_LINE( '사원급여 : ' || v_sal );

END;

/

SQL> select object_name,object_type, status

from user_objects

where object_name in ('EMP05','PRO1');

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

EMP05 VIEW VALID

PRO1 PROCEDURE VALID

SQL> drop table emp;

테이블이 삭제되었습니다.

SQL> select object_name,object_type, status

2 from user_objects

3 where object_name in ('EMP05','PRO1');

OBJECT_NAM OBJECT_TYPE STATUS

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

EMP05 VIEW INVALID

PRO1 PROCEDURE INVALID

SQL> flashback table emp to before drop;

SQL> select object_name,object_type, status

from user_objects

where object_name in ('EMP05','PRO1'); ß 확인해보시요 !!!

SQL> select count(*) from emp05;

COUNT(*)

----------

14

SQL> select object_name,object_type, status

from user_objects

where object_name in ('EMP05','PRO1'); ß 확인해보시요 !!!

SQL> exec pro1(7788);

사원번호 : 7788

사원이름 : SCOTT

사원급여 : 3000

PL/SQL 처리가 정상적으로 완료되었습니다.

SQL> select object_name,object_type, status

from user_objects

where object_name in ('EMP05','PRO1'); ß 확인해보시요 !!!

8.18 Remote Dependencies 두번째

SQL> create or replace procedure pro2

is

begin

pro1(7788);

end;

/

SQL> alter procedure pro1 compile;

SQL> select object_name,object_type, status

from user_objects

where object_name in ('EMP05','PRO1','PRO2');

OBJECT_NAM OBJECT_TYPE STATUS

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

EMP05 VIEW VALID

PRO1 PROCEDURE VALID

PRO2 PROCEDURE INVALID

SQL> alter procedure pro2 compile;

SQL> select object_name,object_type, status

from user_objects

where object_name in ('EMP05','PRO1','PRO2');

OBJECT_NAM OBJECT_TYPE STATUS

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

EMP05 VIEW VALID

PRO1 PROCEDURE VALID

PRO2 PROCEDURE VALID

SQL> alter procedure pro2 compile;

SQL> select object_name,object_type, status

from user_objects

where object_name in ('EMP05','PRO1','PRO2');

OBJECT_NAM OBJECT_TYPE STATUS

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

EMP05 VIEW VALID

PRO1 PROCEDURE VALID

PRO2 PROCEDURE VALID

ß 문제) 만약 pro2 가 원격 서버에있었다면 pro1 을 컴파일했을 때 혹은 pro1

참조 객체인 emp05 view drop 했을때 status 가 어떻게 나오겠는가 ?

 
9.13 Managing Bfile

-- bfile 컬럼을 추가한다.

SQL> alter table emp add emp_video bfile;

-- directory 를 생성한다.


SQL> create or replace directory log_file as 'c:\log_files';

-- directory 를 사용할 권한을 부여한다.

SQL> grant read on directory log_file to public;

-- bfilename 함수를 사용하여 테이블에 행을 삽입한다.

SQL> update emp
set emp_video=bfilename('LOG_FILE','KING.AVI')
where ename='KING';

SQL> create or replace procedure load_emp_bfile
( p_file_loc in varchar2 )
is
v_file bfile;
v_filename varchar2(16);
cursor emp_cursor is
select ename from emp
where deptno =10 for update ;
begin
for emp_record in emp_cursor loop
v_filename := emp_record.ename || '.bmp';
v_file := BFILENAME(p_file_loc, v_filename);
DBMS_LOB.FILEOPEN(v_file);
update emp
set emp_video=v_file
where current of emp_cursor;
DBMS_OUTPUT.PUT_LINE('LOAD FILE: '|| v_filename || 'size: '|| -
DBMS_LOB.GETLENGTH(v_file));
DBMS_LOB.FILECLOSE(v_file);
end loop;
end load_emp_bfile;
/

SQL> SET SERVEROUTPUT ON

SQL> execute load_emp_bfile('LOG_FILE');
LOAD FILE: KING.bmpsize: -71189
LOAD FILE: CLARK.bmpsize: -71189
LOAD FILE: MILLER.bmpsize: -71189

PL/SQL 처리가 정상적으로 완료되었습니다.

9.24 Add LOB columns

SQL> alter table emp add
(resume clob,
picture blob);

SQL> insert into emp(empno,ename, resume,picture)
values(1111,'aaa',empty_clob(), null);

SQL> update emp
set resume='date of birth: 8 February 1951',
picture=EMPTY_BLOB()
where empno=1111;

SQL> update emp
set resume='Date of Birth : 1 june 1956'
where empno=1111;


SQL> select empno, ename, resume
from emp
where empno =1111;


SQL> select dbms_lob.substr(resume,5,18)
from emp;

SQL> declare
text varchar2(4001);
begin
select resume into text
from emp
where empno=1111;
dbms_output.put_line('text is: '|| text);
end;
/
text is: Date of Birth : 1 june 1956

PL/SQL 처리가 정상적으로 완료되었습니다.

10.5 DML Trigger
SQL> create or replace trigger secure_emp
before insert on emp
begin
if (to_char(sysdate,'DY') in ('
','')) OR
(to_char(sysdate,'HH24:MI')
not between '08:00' and '18:00')
then raise_application_error(-20500,'You may insert into emp table -
onlye during business hours.');
end if;
end;

/

SQL> insert into emp(empno ,ename, sal)
2 values(9391,'scott',3000);
insert into emp(empno ,ename, sal)
*
1
행에 오류:
ORA-20500: You may insert into emp table -
onlye during business hours.
ORA-06512: "SCOTT.SECURE_EMP",
5에서
ORA-04088:
트리거 'SCOTT.SECURE_EMP'의 수행시 오류

문제) 위의 트리거를 수정하여라 월요일 부터 금요일까지 저녘 6시에서 아침 9
사이에는 데이타를 입력 못하겠금하는 트리거를 작성하여라


10.13 DML Trigger

Create or replace trigger secure_emp BEFORE

INSERT OR UPDATE OR DELETE ON emp

BEGIN

IF (TO_CHAR(SYSDATE,'DY') IN ('','')) OR (TO_CHAR(SYSDATE,'HH24') NOT BETWEEN '08' AND '18') THEN

IF DELETING THEN RAISE_APPLICATION_ERROR(-20502,'You may delete from EMPLOYEES table'|| 'only during business hours.');

ELSIF INSERTING THEN RAISE_APPLICATION_ERROR(-20500,'You may insert into EMPLOYEES table'|| 'only during business hours.');

ELSIF UPDATING('SAL') THEN RAISE_APPLICATION_ERROR(-20503, 'You may '|| 'update SALARY only during business hours.');

ELSE RAISE_APPLICATION_ERROR(-20504,'You may'||' update EMPLOYEES table only during'|| ' normal hours.');

END IF;

END IF;

END;

/

문제) 현재 시점으로 시간을 고쳐서 입력해보시요 !!

10.14 DML Row Trigger

SQL> create or replace trigger restrict_salary
before insert or update of sal on emp
for each row
begin
if not (:new.job in ('SALESMAN','ANALYST'))
and :new.sal > 15000
then
raise_application_error(-20202,'Employee cannot earn this amount');
end if;
end;
/

문제) 다음 부서번호가 10 20번인 사원들만 월급을 5000 이상으로 수정할수있게

하고 나머지 부서번호는 수정못하게하라

10.15 After Trigger

SQL> create table audit_emp_values
( user_name varchar2(15),
timestamp date,
id number(4),
old_last_name varchar2(10),
new_last_name varchar2(10),
old_title varchar2(10),
new_title varchar2(10),
old_salary number(7,2),
new_salary number(7,2) );

SQL> create or replace trigger audit_emp_values
after delete or insert or update on emp
for each row
begin
insert into audit_emp_values
values(user,sysdate,:old.empno, :old.ename, :new.ename,
:old.job, :new.job, :old.sal, :new.sal );
end;
/

문제) 10-17 을 보시고 직업이 SALESMAN 인 사원의 월급을 갱신했을때만

audit_emp_values 에 값이 들어가겠금 위의 코드를 수정하시요 !!

10.15 After Trigger

SQL> alter table dept

add constraint dept_deptno_pk primary key(deptno);

테이블이 변경되었습니다.

SQL> alter table emp

add constraint emp_deptno_fk foreign key(deptno)

references dept(deptno);

테이블이 변경되었습니다.

SQL> update emp

set deptno=90

where empno=7788; ß 확인해보시요 !!


SQL> CREATE OR REPLACE TRIGGER emp_dept_fk_trg

AFTER UPDATE OF deptno

ON emp FOR EACH ROW

BEGIN

INSERT INTO dept VALUES(:new.deptno, NULL, NULL);

EXCEPTION

WHEN DUP_VAL_ON_INDEX THEN

NULL;

END;

/

SQL> update emp

set deptno=90

where empno=7788;

문제) delete from dept where deptno=10; ß 이 문장이 에러가
나지않고 수행될수 있도록 트리거를 작성하시요 !!

)

CREATE OR REPLACE TRIGGER Dept_del_cascade

AFTER DELETE ON Dept

FOR EACH ROW

BEGIN

DELETE FROM Emp

WHERE Deptno = :old.Deptno;

END;

/

10.21 Instead of Trigger


SQL> create view emp_dept
as select empno, ename, sal, emp.deptno, dname
from emp, dept where emp.deptno=dept.deptno;

SQL>create or replace trigger emp_dept_insert
instead of insert on emp_dept
referencing new as n
for each row
declare
dept_cnt number;
begin
if :n.empno is not null then
insert into emp(empno, ename, sal)
values (:n.empno, :n.ename, :n.sal);
end if;

if :n.deptno is not null then
select count(*) into dept_cnt
from dept where deptno = :n.deptno;

if dept_cnt > 0 and (:n.dname is not null) then
update dept set dname = :n.dname where deptno = :n.deptno;
else insert into dept(deptno, dname) values(:n.deptno, :n.dname);
end if;
end if;
end;
/

insert into emp_dept values (5000, 'EYKIM', 100, 10, 'SALES');
insert into emp_dept(empno, ename, deptno) values (6000, 'YOUNKIM', 20);
insert into emp_dept (empno, deptno, dname) values (7000, 50, 'NEW_DEPT');

11.4 DDL Trigger

create table trigger_log

(action varchar2(90),

action_date date );

CREATE OR REPLACE TRIGGER audit_object_create AFTER CREATE ON SCHEMA -- 현재 SCHEMA 안에서만 추적하라 ( 전체라면 DATABASE )

BEGIN

INSERT INTO trigger_log

VALUES ('CREATE ' || sys.dictionary_obj_type || ' ' || sys.dictionary_obj_name || ' by ' || sys.login_terminal, sysdate);

END;

/

CREATE OR REPLACE TRIGGER audit_object_alter AFTER ALTER ON SCHEMA

BEGIN

INSERT INTO trigger_log

VALUES ('ALTER ' || sys.dictionary_obj_type || ' ' || sys.dictionary_obj_name || ' by ' || sys.login_terminal, sysdate);

END;

/

CREATE OR REPLACE TRIGGER audit_object_drop AFTER DROP ON SCHEMA

BEGIN

INSERT INTO trigger_log

VALUES ('DROP ' || sys.dictionary_obj_type || ' ' || sys.dictionary_obj_name || ' by ' ||sys.login_terminal, sysdate);

END;

/

문제) 아예 drop 이나 truncate 를 수행하지 못하게 트리거를 생성하여라!!

create or replace trigger no_ddl

before drop or truncate on scott.schema

begin

raise_application_error(-20001,'You cannot drop or truncate');

end;

/

11.6 Logon Trigger

SQL> create table log_trig_table
(user_id varchar2(10),
log_date date,
action varchar2(20) );

SQL> create or replace trigger logon_trig
after logon on database
begin
insert into log_trig_table(user_id,log_date,action)
values(user,sysdate,'logging on');
end;
/

문제) 특정 유져로 접속을 시도해보고 테이블에 잘 입력이 되는지 확인하라!!

11.6 Logon Trigger 사용예

CREATE OR REPLACE TRIGGER LOGON_TRACE

AFTER LOGON ON DATABASE

DECLARE

v_sid v$session.sid%TYPE;

v_serial v$session.serial#%TYPE;

v_username v$session.username%TYPE;

v_terminal v$session.terminal%TYPE;

BEGIN

SELECT sid, serial#, username, terminal

         into v_sid, v_serial, v_username, v_terminal

FROM v$session

WHERE AUDSID = USERENV('SESSIONID')

         AND ROWNUM <= 1 ;

IF v_username = 'SPC' AND v_terminal = 'LJH' THEN

execute immediate 'alter session set cursor_sharing = force';

SYS.DBMS_SESSION.SET_SQL_TRACE(TRUE);

END IF;

END ;

/

 

'oracle10R2' 카테고리의 다른 글

Oracle 10g XML Fundamentals  (0) 2011.07.20
SQL Loader  (0) 2011.07.10
Advanced Product Service 10g Develop PLP 01  (0) 2011.07.02
ASM  (0) 2011.06.22
Oracle DICTIONARY  (0) 2011.06.20