오라클 데이터베이스

SQL 9일차(0720)

피아마수 2023. 7. 20. 11:37

커서 실행 후 반환 값

속성
SQL%FOUND sql문 실행후 반환된 행수가 1개 이상이다 (TRUE)
SQL%NOTFOUND sql문 실행후 반환된 행수가 없다 (TRUE)
SQL%ISOPEN 커서가 열려잇으면 TRUE - > 묵시적을 닫아서 항상 FALSE
SQL%ROWCOUNT sql문 실행후 반영된 행의 수 (정수로 반환)

자바에서 JDBC를 통해서 sql문을 실행하면 무조건 commit이 되는 데 이걸 막기위해서 java코드로 commit(false)해줘야함

conn.setAutoCommit(false);

작업이 다끝나고 원하는 시점에 commit 하는 함수

conn.commit();

자바에서 procedure에서 out 파라미터로 설정한거는 뭘 전달하는지 모르기때문에 getObject를 통해서 받아와서 형변환을 해주어야한다.

cstmt.registerOutParameter(4, Types.VARCHAR);

Types.~ : 다른 데이터베이스의 파라미터도 받아올수 있는 범용 데이터타입

OracleTypes.~ : 오라클 데이터베이스에서만 사용할수 있는 오라클 전용 테이터 타입

 

DB에서 테스트하기

--developer 에서 테스트 
    var out_cursor REFCURSOR
    exec usp_getEmpList('1980-01-01','1983-12-30',:out_cursor)
    print out_cursor;

 

프로시저 장점

1. 보안상 좋다( 개발자가 sql문을 갖고 있지 않기때문)

2. 영속적인 데이터를갖고 있으면서 CRUD가 가능하다

 

 

프로시저는 단독 존재

function은 select 문법에 select ,where등등에 사용

 

function

create or replace function f_max_sal
(s_deptno emp.deptno%TYPE)--파라미터
return number   -- public int f_max_sal(int deptno) {  return 10}
--리턴타입 정의
is
  max_sal emp.sal%TYPE;
BEGIN
      select max(sal)
          into max_sal
      from emp
      where deptno = s_deptno;
      return max_sal;--실제 리턴되는 값
END;

생성된 함수

 

트리거 

어떤 사건(insert,delete, update)이 발생했을때 자동으로 실행되는 트랜잭션

하나의 테이블에 트리거 3개씩 등록가능하다

insert,delete, update 3개

 

내부적으로 트랜잭션을 가지고 있다

입고 재고 출고 가 있는게 출고 실패하면 재고도 입고도 롤백됌

 

"트리거란 특정 테이블의 데이터에 변경이 가해졌을 때 자동으로 수행되는
[저장 프로시저]라고 할 수 있다."

 

{}무조건쓰기

create or replace trigger tri_01
after insert on tri_emp --tri_emp 테이블에  insert  가 되고 나서
BEGIN -- 자동 동작할 내용
    DBMS_OUTPUT.PUT_LINE('신입사원 입사');
END;

트랜잭션 때문에 lock을 걸어서 트리거를 실행 중에는 다른 사람이 접근하지 못한다.

 

트리거 타입, 이벤트, 대상테이블, 활성화상태 를 확인하자

https://www.mockaroo.com/

대보 1기 주제

데이터베이스 들어갈 랜덤 데이터를 원하는 갯수만큼 만들어주는 더미데이터 사이트

깃허브 연동해서 이벤트를 처리하는 프로젝트;;;

 

테이블에 DML할때마다 테이블에 기록 , 문장도 기록가능(해보기)

 

create or replace trigger emp_audit_tr
 after insert or update or delete on emp2
 for each row -- 5행 추가 되면 5번실행하도록함 ,원래는 한번만함 몇번수행이든
begin
 if inserting then
      insert into emp_audit
      values(emp_audit_tr.nextval, user, 'inserting', sysdate);
 elsif updating then
      insert into emp_audit
      values(emp_audit_tr.nextval, user, 'updating', sysdate);
 elsif deleting then --어떤 이벤트인지 구분
      insert into emp_audit
      values(emp_audit_tr.nextval, user, 'deleting', sysdate);
 end if;
end;

 

 

--update
--기존의 데이터를 삭제하고 새 데이터를 insert하는 작업이다.

이런걸 하려고 insert가상테이블과 delete가상테이블이 존재한다

 

 

들어가는 데이터를 기록하는 트리거

create or replace trigger emp_audit_tr
 after insert or update or delete on emp2
 for each row
begin
 if inserting then
      insert into emp_audit --insert는 new 만 사용하면된다.
      values(emp_audit_tr.nextval, user, 'inserting', sysdate, :old.deptno, :new.deptno);
                                                                --old 가상테이블(기존데이터)--new 가상테이블(새로운 데이터)
 elsif updating then--update는 old, new를 둘다사용
    insert into emp_audit
    values(emp_audit_tr.nextval, user, 'updating', sysdate, :old.deptno, :new.deptno);
 elsif deleting then -- old 만 사용한다.
    insert into emp_audit
    values(emp_audit_tr.nextval, user, 'deleting', sysdate, :old.deptno, :new.deptno);
 end if;
end;


select* from emp2;

--insert
insert into emp2(empno,ename,deptno) values (9999,'홍길동',100);
--deptno만 삽입했으니 이것만 들어감
select * from emp_audit;

--update
update emp2 set deptno=200
where empno=9999;
select * from emp_audit;

--delete
delete from emp2 where empno=9999;
select * from emp_audit;

 

 

 

insert , update, delete 트리거는 before와 update는 인지하기 못하기 때문에 상관없이 3개임

 

데이터를 넣을때 유효성 검사 

예외를 강제 발생

아래는 시간 대에 삽입을 불가능하게 한다

create or replace trigger trigger_order
before insert on tri_order
BEGIN
  IF(to_char(sysdate,'HH24:MM') not between '12:00' and '18:00') THEN
     RAISE_APPLICATION_ERROR(-20002, '허용시간 오류 쉬세요');
  END IF;
END;

다른 테이블에 같은 데이터를 입력하고 싶다

--입고 데이터 들어오면 같은 데이터를 재고 입력
create or replace trigger insert_t_01
after insert on t_01
for each row
BEGIN
  insert into t_02(no, pname)
  values(:NEW.no ,:NEW.pname);
END;

--입고
insert into t_01 values(1,'notebook');

 

 

ps-sql 0720 공부한거

--emp 테이블 
--CURD 프로시져 만들기

CREATE OR REPLACE PROCEDURE pc_update_deptno
(
    e_empno    IN number,
    e_deptno IN number
)
IS
BEGIN
    UPDATE c_emp
    SET deptno = e_deptno
    WHERE empno  = e_empno;
    commit;
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        DBMS_OUTPUT.PUT_LINE('SQL ERROR MESSAGE: ' || SQLERRM);
END;

select * from c_emp;
select * from c_dept;
select * from user_constraints where table_name='C_EMP';
select * from user_constraints where table_name='C_DEPT';


 exec pc_update_deptno(1,500);




--exec pc_update_deptno('','')
--여러건의 데이터 조회시 JDBC 작업....
create or replace procedure usp_getEmpList
(
    s_hiredate in varchar2,
    e_hiredate in varchar2,
    list_cursor out SYS_REFCURSOR --약속된 ...
)
is
    begin
        open list_cursor FOR
        select empno,ename,job,sal , hiredate
        from emp
        where hiredate  between to_date(s_hiredate,'YYYY-MM-DD') and to_date(e_hiredate,'YYYY-MM-DD')
        --자바에서 문자로 받아온 날짜를 dB에서 날짜로 변환해서 사용한다.
        order by hiredate desc;
        
        exception
        when others then
            DBMS_OUTPUT.PUT_LINE('SQL ERROR MESSAGE: ' || SQLERRM);
    end;
    --developer 에서 테스트 
    var out_cursor REFCURSOR
    exec usp_getEmpList('1980-01-01','1983-12-30',:out_cursor)
    print out_cursor;
/*
                String proc_call = "{? = call emptest(?)}";
                
                // create callable statement
                cstmt = conn.prepareCall(proc_call);

                // key here is to register the output parameter
                // of type cursor, execute, then cast it as a ResultSet.
                cstmt.registerOutParameter(1, OracleTypes.CURSOR);
                cstmt.setInt(2, 10);  //10번 부서의 데이터만 얻기 위해

                cstmt.executeQuery();
                rs = (ResultSet)cstmt.getObject(1);

                while(rs.next()) {
                        out.println(rs.getString("ename") + "<br>");
                }

*/


---------------------기본 procedure END-----------------------------------------
--[사용자 정의 함수]
--to_char() , sum() 오라클에서 제공
--사용자가 직접 필요한 함수를 만들어 사용가능
--사용방법은 다른 함수사용법과 동일
--사용자 정의 함수 paramter  정의 , return 값
create or replace function f_max_sal
(s_deptno emp.deptno%TYPE)--파라미터
return number   -- public int f_max_sal(int deptno) {  return 10}
--리턴타입 정의
is
  max_sal emp.sal%TYPE;
BEGIN
      select max(sal)
          into max_sal
      from emp
      where deptno = s_deptno;
      return max_sal;--실제 리턴되는 값
END;

---
select * from emp where sal = f_max_sal(10);

select max(sal) , f_max_sal(30) from emp;
--
create or replace function f_callname
(vempno emp.empno%TYPE)
return varchar2 -- public String f_callname() {  String  v_name; return "홍길동"}
is
  v_name emp.ename%TYPE;
BEGIN
    select ename || '님'
      into v_name
    from emp
    where empno=vempno;
    return v_name;
END;

select f_callname(7788) from dual;

select empno, ename , f_callname(7788) , sal
from emp
where empno=7788;

select empno, ename , f_callname(empno) , sal
from emp
where empno=7788;
select * from emp;

--함수 
--parmater  사번을 입력받아서 사번에 해당되는 부서이름을 리턴하는 함수
create or replace function f_get_dname
(vempno emp.empno%TYPE)
return varchar2
is
    v_dname dept.dname%TYPE;
  BEGIN
    select dname
      into v_dname
    from dept
    where deptno = (select deptno from emp where empno=vempno);
    return v_dname;
  END;

select empno , ename ,deptno, f_get_dname(empno)
from emp 
where empno=7788;
--------------------------function END------------------------------------------

--[트리거 : Trigger]
--트리거(trigger)의 사전적인 의미는 방아쇠나 (방아쇠를) 쏘다, 발사하다,
--(사건을) 유발시키다라는 의미가 있다.
 
--[입고]    [재고]     [출고]
 
--입고 INSERT (내부적으로 [트랜잭션]이 동작)
--재고 INSERT
--위험부담 : lock
 
 
--PL/SQL에서의 트리거 역시 방아쇠가 당겨지면 자동으로 총알이 발사되듯이
--어떠한 이벤트가 발생하면 그에 따라 다른 작업이 자동으로 처리되는 것을 의미한다.
/*
트리거란 특정 테이블의 데이터에 변경이 가해졌을 때 자동으로 수행되는
[저장 프로시저]라고 할 수 있다.
앞서 배운 저장 프로시저는 필요할 때마다 사용자가 직접
 EXECUTE 명령어로 호출해야 했다.
하지만 트리거는 이와 달리 테이블의
데이터가 INSERT, UPDATE, DELETE 문에 의해 변경되어질 때
[ 자동으로 수행되므로 이 기능을 이용하며 여러 가지 작업 ] 을 할 수 있다.
이런 이유로 트리거를 사용자가 직접 실행시킬 수는 없다.
 
 --자동(insert, update ,delete)이벤트가 발생하면  자동으로  실행되는 procedure ...
 
 
 
--BEFORE : 테이블에서 DML 실행되기 전에 트리거가 동작
--AFTER :  테이블에서 DML 실행후에 트리거 동작
 
Syntax
CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE | AFTER} triggering_event [OF column1, . . .] ON table_name
[FOR EACH ROW [WHEN trigger_condition]
trigger_body;
 
trigger_name TRIGGER 의 식별자
  BEFORE | AFTER DML 문장이 실행되기 전에 TRIGGER 를 실행할 것인지 실행된
  후에 TRIGGER 를 실행할 것인지를 정의
triggering_event 
TRIGGER 를 실행하는 DML(INSERT,UPDATE,DELETE)문을 기술한다.
 
OF column TRIGGER 가 실행되는 테이블에서 COLUMN 명을 기술한다.
 
table_name TRIGGER 가 실행되는 테이블 이름
 
FOR EACH ROW 이 옵션을 사용하면 
행 레벨 트리거가 되어 triggering 문장
에 의해 영향받은 행에 대해 각각 한번씩 실행하고 사용하지
않으면 문장 레벨 트리거가 되어 DML 문장 당 한번만 실행된다.
 
 
  TRIGGER 에서 OLD 와 NEW
    행 레벨 TRIGGER 에서만 사용할 수 있는 예약어로 트리거 내에서 현재 처리되고 있는 행
    을 액세스할 수 있다. 즉 두개의 의사 레코드를 통하여 이 작업을 수행할 수 있다. :OLD
    는 INSERT 문에 의해 정의되지 않고 :NEW 는 DELETE 에 대해 정의되지 않는다. 그러나
    UPDATE 는 :OLD 와 :NEW 를 모두 정의한다. 아래의 표는 OLD 와 NEW 값을 정의한 표이다. 
    문장 :OLD :NEW
    INSERT 모든 필드는 NULL 로 정의 문장이 완전할 때 삽입된 새로운 값
    UPDATE 갱신하기 전의 원래 값 문장이 완전할 때 갱신된 새로운 값
    DELETE 행이 삭제되기 전의 원래 값 모든 필드는 NULL 이다.
 
DROP TRIGGER 명령어로 트리거를 삭제할 수 있고 TRIGGER 를 잠시 disable 할 수 있다.
DROP TRIGGER trigger_name;
ALTER TRIGGER trigger_name {DISABLE | ENABLE};
TRIGGER 와 DATA DICTIONARY
TRIGGER 가 생성될 때 소스 코드는 데이터 사전 VIEW 인 user_triggers 에 저장된다. 이
VIEW 는 TRIGGER_BODY, WHERE 절, 트리거링 테이블, TRIGGER 타입을 포함 한다.
 
*/
create table tri_emp
as
  select empno , ename from emp where 1=2;


select * from tri_emp;

create or replace trigger tri_01
after insert on tri_emp --tri_emp 테이블에  insert  가 되고 나서
BEGIN -- 자동 동작할 내용
    DBMS_OUTPUT.PUT_LINE('신입사원 입사');
END;

insert into tri_emp(empno,ename) values(100,'홍길동');
select * from tri_emp;



create or replace trigger tri_02
after update on tri_emp
BEGIN
  DBMS_OUTPUT.PUT_LINE('신입사원 수정');
END;


select * from user_jobs;


--테이블에 trigger 정보
select * from user_triggers where table_name='TRI_EMP';


insert into tri_emp values(100,'김유신');

update tri_emp
set ename='아하'
where empno=100;


--delete 트리거 : tri_emp
--사원테이블 삭제 (화면 출력)
create or replace trigger tri_03
after delete on tri_emp
BEGIN
  DBMS_OUTPUT.PUT_LINE('신입사원 삭제');
END;

select * from user_triggers where table_name='TRI_EMP';



insert into tri_emp values(200,'홍길동');
update tri_emp set ename='변경' where empno= 200;
delete from tri_emp where empno=200;
-------------------------------------------
----------------------------------
--예제1) 테이블에 INSERT, UPDATE, DELETE 를 할 때 user, 구분(I,U,D), sysdate 를 기록하는 
--테이블(emp_audit)에 내용을 저장한다.
--FOR EACH ROW 이 옵션을 사용하면 
--행 레벨 트리거가 되어 triggering 문장
--에 의해 영향받은 행에 대해 각각 한번씩 실행하고 사용하지
--않으면 문장 레벨 트리거가 되어 DML 문장 당 한번만 실행된다.


drop sequence emp_audit_tr;
drop table emp_audit;


create sequence emp_audit_tr
 increment by 1
 start with 1
 maxvalue 999999
 minvalue 1
 nocycle
 nocache;

create table emp_audit(
 e_id number(6) constraint emp_audit_pk primary key,
 e_name varchar2(30),
 e_gubun varchar2(10),
 e_date date);


drop table emp2;
create table emp2
as
    select * from emp;

desc emp2;
select * from emp2;

create or replace trigger emp_audit_tr
 after insert or update or delete on emp2
 for each row -- 5행 추가 되면 5번실행하도록함 ,원래는 한번만함 몇번수행이든
begin
 if inserting then
      insert into emp_audit
      values(emp_audit_tr.nextval, user, 'inserting', sysdate);
 elsif updating then
      insert into emp_audit
      values(emp_audit_tr.nextval, user, 'updating', sysdate);
 elsif deleting then --어떤 이벤트인지 구분
      insert into emp_audit
      values(emp_audit_tr.nextval, user, 'deleting', sysdate);
 end if;
end;

-- for each row 선언 안했을 때 (명령어 한 번에 대하여 한 건으로 기록된다.)
select * from emp2;
rollback;

update emp2 
set deptno = 200
where deptno = 10;

select * from emp_audit;

delete from emp2 where deptno = 20;

select * from emp_audit;

rollback;

-- for each row 생략하고  선언 했을 때(명령어 한 번에 변경된 행만큼 기록된다.)
create or replace trigger emp_audit_tr
 after insert or update or delete on emp2
begin
 if inserting then
      insert into emp_audit
      values(emp_audit_tr.nextval, user, 'inserting', sysdate);
 elsif updating then
      insert into emp_audit
      values(emp_audit_tr.nextval, user, 'updating', sysdate);
 elsif deleting then
      insert into emp_audit
      values(emp_audit_tr.nextval, user, 'deleting', sysdate);
 end if;
end;

select * from emp2;
update emp2 set deptno = 20 where deptno = 10;

select * from emp_audit;
rollback;


--------------------------------------------------------------------
--INSERT, UPDATE, DELETE로 변경되는 내용에 대하여 전/후 데이터를 기록한다.
drop table emp_audit;

create table emp_audit (
 id number(6) constraint emp_audit_pk primary key,
 name varchar2(30),
 gubun varchar2(10),
 wdate date,
 etc1 varchar2(20),  -- old
 etc2 varchar2(20)   -- new
);
--update
--기존의 데이터를 삭제하고 새 데이터를 insert하는 작업이다.
--이런걸 하려고 insert가상테이블과 delete가상테이블이 존재한다

create or replace trigger emp_audit_tr
 after insert or update or delete on emp2
 for each row
begin
 if inserting then
      insert into emp_audit --insert는 new 만 사용하면된다.
      values(emp_audit_tr.nextval, user, 'inserting', sysdate, :old.deptno, :new.deptno);
                                                                --old 가상테이블(기존데이터)--new 가상테이블(새로운 데이터)
 elsif updating then--update는 old, new를 둘다사용
    insert into emp_audit
    values(emp_audit_tr.nextval, user, 'updating', sysdate, :old.deptno, :new.deptno);
 elsif deleting then -- old 만 사용한다.
    insert into emp_audit
    values(emp_audit_tr.nextval, user, 'deleting', sysdate, :old.deptno, :new.deptno);
 end if;
end;


select* from emp2;

--insert
insert into emp2(empno,ename,deptno) values (9999,'홍길동',100);
--deptno만 삽입했으니 이것만 들어감
select * from emp_audit;

--update
update emp2 set deptno=200
where empno=9999;
select * from emp_audit;

--delete
delete from emp2 where empno=9999;
select * from emp_audit;

--------------------------------------------------------------------------------
--트리거의 활용
create table tri_order
(
  no number,
  ord_code varchar2(10),
  ord_date date
);

--before 트리거의 동작시점이 실제 tri_order 테이블 insert 되기 전에
--트리거 먼저 동작 그 이후 insert 작업
create or replace trigger trigger_order
before insert on tri_order
BEGIN
  IF(to_char(sysdate,'HH24:MM') not between '12:00' and '18:00') THEN
     RAISE_APPLICATION_ERROR(-20002, '허용시간 오류 쉬세요');
  END IF;
END;

insert into tri_order values(1,'notebook',sysdate);
select * from tri_order;
commit;
rollback;
--트리거 삭제
drop trigger trigger_order;




--POINT
--PL_SQL 두개의 가상데이터(테이블) 제공
--:OLD > 트리거가 처리한 레코드의 원래 값을 저장    (ms-sql (deleted)
--:NEW > 새값을 포함                             (ms-sql (inserted)

create or replace trigger tri_order2
before insert on tri_order
for each row
BEGIN
  IF(:NEW.ord_code) not in('desktop') THEN
     RAISE_APPLICATION_ERROR(-20002, '제품코드 오류');
  END IF;
END;

select * from tri_order;

--오류 (desktop)
insert into tri_order values(200,'notebook',sysdate);

insert into tri_order values(200,'desktop',sysdate);

select * from tri_order;
commit;

--------------------------------------------------------------------------------
--입고 , 재고

create table t_01 --입고
(
  no number,
  pname varchar2(20)
);

create table t_02 --재고
(
  no number,
  pname varchar2(20)
);

--입고 데이터 들어오면 같은 데이터를 재고 입력
create or replace trigger insert_t_01
after insert on t_01
for each row
BEGIN
  insert into t_02(no, pname)
  values(:NEW.no ,:NEW.pname);
END;

--입고
insert into t_01 values(1,'notebook');

select * from t_01;
select * from t_02;

-- 입고 제품이 변경 (재고 변경)
create or replace trigger update_t_01
after update on t_01
for each row
BEGIN
  update t_02
  set pname = :NEW.pname
  where no = :OLD.no;
END;

update t_01
set pname = 'notebook2'
where no = 1;

select * from t_01;

select * from t_02;

--delete 트리거 만들어 보세요 
--입고 데이터 delete from t_01 where no =1 삭제 되면 재고 삭제
create or replace trigger delete_tri_01
after delete on t_01
for each row
BEGIN
  delete from t_02
  where no=:OLD.no;
END;

delete from t_01 where no=1;

select* from t_01;
select* from t_02;

commit;
--------------------------------------------------------------------------------
-- 고생했다 토닥 토닥 ---------------------------------------------------------------------