[PLSQL-2] PL/SQL기본 2 (5~10장)

쏘니's avatar
Mar 31, 2024
[PLSQL-2] PL/SQL기본 2 (5~10장)

5장. PLSQL 내에서의 이중 루프문 (3가지 loop문 모두 이중 루프문 가능)

028. Basic loop문에서 이중 루프문

📖
Basic loop문에서 이중 루프문 반복문 자체를 반복 시키고 싶을 때 이중 반복문을 사용합니다.
  • 문법
    • begin loop loop 실행문; exit when 안쪽 반복문을 종료시킬 조건; end loop; exit when 바깥쪽 반복문을 종료시킬 조건; end loop; end; / ====================================================== 내부 loop문을 반복시키고 싶을 때 외부 loop문 사용 *주의* 각각 exit when을 사용해서 종료시킬 조건 작성해줘야함 그렇지 않으면 무한루프 돌게됨
      notion image
 
예제1) — Basic loop
basic loop문으로 숫자 1부터 9까지 출력하시오
set serveroutput on declare v_count number(10) := 1 ; begin loop dbms_output.put_line( v_count ); #반복해서 실행하고 싶은 실행문 v_count := v_count + 1 ; #반복문을 종료시킬 코드 exit when v_count > 9; #반복문을 종료시킬 코드 end loop; end; / ============================================================= v_count의 값이 exit when의 조건인 v_count > 9 에 해당되지 않으면, 종료하지 못하고 다시 반복문 돌게됨 v_count = 1 + 1 = 2 #9보다 작으므로 2 출력됨 v_count = 2 + 1 = 3 #9보다 작으므로 2 출력됨 v_count = 3 + 1 = 4 #9보다 작으므로 2 출력됨 v_count = 4 + 1 = 5 #9보다 작으므로 2 출력됨 v_count = 5 + 1 = 6 #9보다 작으므로 2 출력됨 v_count = 6 + 1 = 7 #9보다 작으므로 2 출력됨 v_count = 7 + 1 = 8 #9보다 작으므로 2 출력됨 v_count = 8 + 1 = 9 #9보다 작으므로 2 출력됨 v_count = 9 + 1 = 10 #9크기 때문에 exit로 loop문 탈출 end loop; end; /
notion image
 
예제2)
구구단 2단을 출력하는 기본 basic loop문을 수행하시오
set serveroutput on declare v_count number(10) := 1 ; begin loop dbms_output.put_line( '2 x ' || v_count || ' = ' || 2 * v_count ); v_count := v_count + 1 ; exit when v_count > 9; end loop; end; /
notion image
 
예제3) —BASIC LOOP 이중 루프 문
basic loop 문으로 구구단 전체를 출력하시오 !
set serveroutput on declare v_dan number(10) := 2; v_count number(10) := 1 ; begin loop loop dbms_output.put_line( v_dan ||' x ' || v_count || ' = ' || v_dan * v_count ); v_count := v_count + 1 ; exit when v_count > 9; end loop; v_dan := v_dan + 1; -- 단을 증가 시키는 코드 (2단, 3단) v_count := 1; -- v_count 를 다시 1로 변경하는 코드 exit when v_dan > 9; -- 밖의 루프문을 종료시키는 코드 end loop; end; /
notion image

문제풀며 익히기

 
Q1.
예제 3의 코드에 새로운 코드를 추가하여 구구단 짝수단만 출력되게 하시오.
A. set serveroutput on declare v_dan number(10) := 2; v_count number(10) := 1 ; begin loop if mod(v_dan, 2) = 0 then loop dbms_output.put_line( v_dan ||' x ' || v_count || ' = ' || v_dan * v_count ); v_count := v_count + 1 ; exit when v_count > 9; end loop; end if; v_dan := v_dan + 1; -- 단을 증가 시키는 코드 (2단, 3단) v_count := 1; -- v_count 를 다시 1로 변경하는 코드 exit when v_dan > 9; -- 밖의 루프문을 종료시키는 코드 end loop; end; / ================================================================ 내부 loop문을 둘러싸며 if문 넣기
notion image
notion image
 
 

029. While loop문에서 이중 루프문

📖
While loop문에서 이중 루프문 반복문 자체를 반복 시키고 싶을 때 이중 반복문을 사용합니다
  • 문법
    • begin while 조건 loop while 조건 loop 실행문 ; [안쪽 루프문을 종료시킬 코드]; => 안쪽루프문의 조건을 false로 만들 조건 작성 end loop; [바깥쪽 루프문을 종료시킬 코드]; => 바깥쪽루프문의 조건을 false로 만들 조건 작성 end loop; end; /
      notion image
 
예제1) 구구단 2단을 while loop 문으로 구현하시오
declare v_count number(10) := 1; begin while v_count < 10 loop dbms_output.put_line( '2 x ' || v_count || ' = ' || 2 * v_count ); v_count := v_count + 1 ; end loop; end; /
notion image
 
예제2. 구구단 전체를 while loop문으로 출력하시오
set serveroutput on declare v_dan number := 2 ; v_count number(10) := 1; begin while v_dan <= 9 loop while v_count <= 9 loop dbms_output.put_line( v_dan || ' x ' || v_count || ' = ' || v_dan * v_count ); v_count := v_count + 1 ; -- 곱하는 수를 증가시키는 코드 end loop; v_dan := v_dan + 1 ; -- 단을 증가시키는 코드 v_count := 1; -- 단이 증가될 때마다 다시 1로 초기화해줘야 합니다. end loop; end; / ======================================================= v_dan <= 9 일때, 안쪽 루프문 시작 ////////////////////////////////////////////// v_count <= 9 일때, 즉 while조건에 맞을 때 실행. v_count 가 10이되면, 9보다 크기 때문에 안쪽 루프문 탈출하여 v_dan 을 1 증가시키고, v_count를 1로 초기화해줌 ////////////////////////////////////////////// v_dan이 10이되면, 즉 9보다 커지면 바깥쪽 루프문 endloop;
notion image
 

문제풀며 익히기

 
Q1.
구구단의 짝수단만 출력되게 하시오
set serveroutput on declare v_dan number := 2 ; v_count number(10) := 1; begin while v_dan <= 9 loop if mod(v_dan, 2) = 0 then while v_count <= 9 loop dbms_output.put_line( v_dan || ' x ' || v_count || ' = ' || v_dan * v_count ); v_count := v_count + 1 ; -- 곱하는 수를 증가시키는 코드 end loop; end if; v_dan := v_dan + 1 ; -- 단을 증가시키는 코드 v_count := 1; -- 단이 증가될 때마다 다시 1로 초기화해줘야 합니다. end loop; end; /
notion image
 
 

030. For loop문에서 이중 루프문(간단해서 많이 사용)

📖
For loop문에서 이중 루프문 반복문 자체를 반복 시키고 싶을 때 이중 반복문을 사용합니다
  • 문법
    • begin for 카운터 인덱스 in 반복할 범위 loop for 카운터 인덱스 in 반복할 범위 loop 실행문; end loop; end loop; end; / ======================================================= 루프문을 종료시킬 조건을 따로 주지 않아도 됨. 왜? 범위를 주기 때문
      notion image
 
예제1) 이번에는 for loop문으로 구구단 2단을 출력하시오
begin for i in 1 .. 9 loop dbms_output.put_line( '2 x ' || i || ' = ' || 2* i ); end loop; end; /
notion image
 
예제2) 이번에는 for loop문으로 구구단 2단과 3단을 출력하시오
< 이중 루프문 사용 > begin for i in 2 .. 3 loop for j in 1 .. 9 loop dbms_output.put_line( i || ' x ' || j || ' = ' || i * j ); end loop; end loop; end; / ========================================================== 바깥 쪽 루프문 i = 2 내부 루프문 j = 1 j = 2 j = 3 j = 4 j = 5 j = 6 j = 7 j = 8 j = 9 end loop; 바깥 쪽 루프문 i = 3 내부 루프문 j = 1 j = 2 j = 3 j = 4 j = 5 j = 6 j = 7 j = 8 j = 9 end loop; end; / ---------------------------------------------------------- < 이중 루프문을 사용하지 않는다면? > begin for i in 1 .. 9 loop dbms_output.put_line( '2 x ' || i || ' = ' || 2* i ); end loop; for i in 1 .. 9 loop dbms_output.put_line( '3 x ' || i || ' = ' || 2* i ); end loop; end; / =========================================================== 이중 루프문을 사용하지 않으면 비효율적이게 됨 9단까지 한다면 루프문을 9번 써줘야됨
notion image

문제풀며 익히기

 
Q1.
구구단 2단부터 9단까지 전체를 다 출력하시오
  • 출력예시
    • 2 x 1 = 2 2 x 2 = 4 2 x 3 = 6 : : : : 9 x 8 = 72 9 x 9 = 81
set serveroutput on begin for i in 2 .. 9 loop for j in 1 .. 9 loop dbms_output.put_line( i || ' x ' || j || ' = ' || i * j ); end loop; end loop; end; /
notion image
 
Q2.
위의 코드를 수정해서 홀수 단만 출력하시오.
set serveroutput on begin for i in 2 .. 9 loop if mod(i,2) = 1 then for j in 1 .. 9 loop dbms_output.put_line( i || ' x ' || j || ' = ' || i * j ); end loop; end if; end loop; end; /
 
 
 

031. 이중 루프문에서 레이블 사용법

📖
이중 루프문에서 레이블 사용법
이중 루프문에서 레이블을 사용하게 되면 어느 코드까지가 밖의 루프문이고 어느 코드까지가 안쪽 루프문인지를 명확히 구분할 수 있습니다
즉, 코드를 이용해서 구분 가능(가시적이고 코드 수정이 편함) 예제1)
구구단 전체를 출력하는 basic loop문에서 레이블을 사용하시오
declare v_dan number(10) := 2; v_count number(10) := 1 ; begin <<Outer_loop>> #대문자로 안써도 됨 loop <<Inner_loop>> loop dbms_output.put_line( v_dan ||' x ' || v_count || ' = ' || v_dan * v_count ); v_count := v_count + 1 ; exit Inner_loop when v_count > 9; end loop Inner_loop; v_dan := v_dan + 1; -- 단을 증가 시키는 코드 v_count := 1; -- v_count 를 다시 1로 변경하는 코드 exit Outer_loop when v_dan > 9; -- 밖의 루프문을 종료시키는 코드 end loop Outer_loop; end; / -------------------- notepad같은 경우 더블클릭시 같은 단어가 표시되기 때문에 수정 편
notion image
notion image

문제풀며 익히기

 
Q1.
다음의 이중 for loop문에 레이블을 추가하세요.
begin for i in 2 .. 9 loop for j in 1 .. 9 loop dbms_output.put_line( i || ' x ' || j || ' = ' || i * j ); end loop; end loop; end; /
begin <<outer_loop>> for i in 2 .. 9 loop <<inner_loop>> for j in 1 .. 9 loop dbms_output.put_line( i || ' x ' || j || ' = ' || i * j ); end loop inner_loop; end loop outer_loop; end; /

032. 예제32. PL/SQL 코드 디버깅하는 방법

📖
디버깅(debuging) 이란 ? 말 그대로 코드에서 버그(에러의 원인)를 찾아내는것 입니다. cmd 창에서 돌렸을 때, 과정은 알 수 없이 결과만 나오기 때문에 천천히 보는 방법이 필요함. cmd창이 아닌 sqldeveloper에서 수행가능
프로시저를 만들어야 디버깅을 할 수 있음
예제) PL/SQL 코드를 디버깅 하기
1. sqldeveloper 를 실행하고 아래의 설정을 합니다. 도구 -> 환경설정 ->디버거-> DBMS_DEBUG 사용 체크 2. 아래의 pro77 프로시져(구구단 전체를 whileloop로 구성한)를 생성합니다. create or replace procedure pro77 is v_dan number := 2 ; v_count number(10) := 1; begin while v_dan <= 9 loop while v_count <= 9 loop dbms_output.put_line( v_dan || ' x ' || v_count || ' = ' || v_dan * v_count ); v_count := v_count + 1 ; end loop; v_dan := v_dan + 1 ; v_count := 1; end loop; end; / >> Procedure PRO77이(가) 컴파일되었습니다. 3. sqldeveloper 에서 프로시져 pro77을 클릭하면 소스코드가 보이는데 여기서 중단점을 찍어야합니다. 중단점을 찍는 이유는 중단점에서 일시멈춤을하기 위해서 입니다. 4. pro77 을 클릭하고 오른쪽 마우스를 눌러서 디버그를 위한 컴파일을 합니다. 5. 무당벌레 아이콘을 누릅니다. 6. 오른쪽 아래에 텝에 스마트 데이터 텝을 클릭하고 7. F7 을 누르면서 디버깅을 진행합니다.
notion image
notion image
notion image
notion image
notion image
notion image

문제풀며 익히기

 
Q1.
구구단 2단을 출력하는 아래의 코드를 디버깅 하시오
set serveroutput on declare v_count number(10) := 1 ; begin loop dbms_output.put_line( '2 x ' || v_count || ' = ' || 2 * v_count ); v_count := v_count + 1 ; exit when v_count > 9; end loop; end; /
create or replace procedure pro78 as v_count number(10) := 1 ; begin loop dbms_output.put_line( '2 x ' || v_count || ' = ' || 2 * v_count ); v_count := v_count + 1 ; exit when v_count > 9; end loop; end; / set serveroutput on execute pro78;
notion image
notion image
 

6장. PL/SQL 에서의 조합 변수

💡
  • PL/SQL 변수의 종류 3가지 ?
  1. 스칼라 변수 : 단일값 만 저장
  1. 조합변수 : 여러개값을 저장 (레코드 변수, 컬렉션 )
  1. 바인드 변수 : 호스트 변수

033. PL/SQL 레코드 변수란

📖
PL/SQL 레코드 변수란 " 스칼라 변수(값을 한개만 저장 가능)와는 다르게 여러개의 값을 저장하는 변수 “
  • PL/SQL 에서의 조합 변수의 종류 2가지?
      1. PL/SQL 레코드 : 가로로 데이터가 담기는 변수
      1. PL/SQL 컬렉션 : 세로로 데이터가 담기는 변수
      notion image
 
PL/SQL 레코드 생성 예제)
사원번호를 물어보게하고 사원번호를 입력하면 해당 사원의 사원이름, 월급과 직업이 출력되는 PL/SQL 코드를 작성하시오
set serveroutput on set verify off accept p_empno prompt '사원번호를 입력하세요 ~' declare v_empno emp.empno%type := &p_empno; #v_empno는 값을 하나만 담는 스칼라변수 type t_rec is record ( v_ename emp.ename%type, v_sal emp.sal%type, v_job emp.job%type ); v_myrec t_rec ; begin select ename, sal, job into v_myrec from emp where empno = v_empno; v_myrec.v_sal := 9000; #레코드 변수 안의 값을 9000으로 바꿔버릴 수 있는 것 dbms_output.put_line ( v_myrec.v_ename ||' ' || to_char(v_myrec.v_sal) || ' ' || v_myrec.v_job ) ; #문자끼리 연결하기 때문에 명시적으로 to_char해줌 end; / ========================================================= type [레코드 타입 이름] is record ( [첫번째 방에 들어갈 값의 이름] emp.ename%type, [두번째 방에 들어갈 값의 이름] emp.sal%type, [세번째 방에 들어갈 값의 이름] emp.job%type ); [레코드 변수 이름] [레코드 타입 이름] ; V_MGREC ------------------------------- | | | | ------------------------------- v_ename v_sal v_job => 즉, 타입을 먼저 만들어줘야 레코드 변수를 생성할 수 있음 ------------------------ v_myrec.v_sal := 9000; 위와 같이 바꾼 이유는 해당 값을 암호화 하기 위해 원래 값과 다른 값을 출력시켜버린 것
notion image
notion image

문제풀며 익히기

 
Q1.
사원번호를 물어보게 하고 사원번호를 입력하면 해당 사원의 직업, 월급과 커미션이 출력되는 PL/SQL 코드를 작성하는데 커미션은 무조건 0으로 출력되게 하시오
  • 출력예시
    • 사원 번호를 입력하세요 ~ 7788
      직업은 ANALYST 월급은 3000 커미션 0
set serveroutput on set verify off accept p_empno prompt '사원번호를 입력하세요 ~' declare v_empno emp.empno%type := &p_empno; type t_rec is record ( v_job emp.job%type, v_sal emp.sal%type, v_comm emp.comm%type ); v_myrec t_rec ; begin select job, sal, comm into v_myrec from emp where empno = v_empno; v_myrec.v_comm := 0; dbms_output.put_line ( v_myrec.v_job ||' ' || to_char(v_myrec.v_sal) || ' ' || to_char(v_myrec.v_comm) ) ; end; / ================================================================= comm은 노출되면 안되는 값으로 선정되었기 때문에 값을 모두 0으로 출력하게 함 출력시 일관성 있기 문자형태로 연결연산자 사용하기 위해 to_char해줌 -------------------------------------------------------------- gpt의 말 set serveroutput on set verify off accept p_empno prompt '사원번호를 입력하세요 ~' declare v_empno emp.empno%type := &p_empno; type t_rec is record ( v_job emp.job%type, v_sal emp.sal%type, v_comm emp.comm%type ); v_myrec t_rec ; begin select job, sal, comm into v_myrec.v_job, v_myrec.v_sal, v_myrec.v_comm from emp where empno = v_empno; v_myrec.v_comm := 0; dbms_output.put_line ( v_myrec.v_job ||' ' || to_char(v_myrec.v_sal) || ' ' || to_char(v_myrec.v_comm) ) ; end; /
notion image
 
 

034. PL/SQL 레코드 변수의 이점

📖
PL/SQL 레코드 변수의 이점 %rowtype을 이용해서 레코드 변수를 선언하면 다음의 기능을 이용할 수 있습니다.
< %ROWTYPE 사용 안할 때 > type t_rec is record ( v_job emp.job%type, v_sal emp.sal%type, v_comm emp.comm%type ); v_myrec t_rec ; ======================================================================= < %ROWTYPE 사용하여 쉽게 만드는 법 > 1. select * into 레코드 변수명 from emp where empno = v_empno; 2. update emp set row = 레코드 변수명 #해당 row전체를 한번 where empno = v_empno; 3. insert into emp values 레코드 변수명; #한번에 insert
 
예제 )
1. emp 테이블과 똑같은 emp_test 라는 테이블로 생성하는데 데이터는 가져오지 말고 구조만 구성합니다. create table emp_test as select * from emp where 1= 2; 2. 사원번호를 물어보게하고 사원번호를 입력하면 해당 사원의 모든 데이터가 emp_test 테이블에 입력되는 PL/SQL 코드를 작성하시오 (데이터 이행을 쉽게 할 수 있음) 데이터 이행(ETL) emp -------------------------> emp_test accept p_empno prompt '사원번호를 입력하세요 ~' declare v_empno emp.empno%type := &p_empno; #스칼라변수 v_emp emp%rowtype; -- emp 테이블의 컬럼의 모든 데이터 타입을 그대로 따르겠다. #레코드변수 begin select * into v_emp from emp where empno = v_empno; v_emp.sal := 0 ; insert into emp_test values v_emp; end; / select * from emp_test; ================================================ < 설명 > v_emp emp%rowtype; #레코드 변수 => 컬럼 8개를 만들 수 있는 레코드블록들이 만들어짐 => 각 블록 칸들의 이름은 EMP테이블의 컬럼 명들임 => emp 테이블의 컬럼의 모든 데이터 타입을 그대로 따르겠다는 것. select * into v_emp => 레코드 변수로 인해 컬럼명을 하나하나 쓸 필요 없이 * 사용 가능해짐 insert into emp_test values v_emp; => 레코드 변수 v_emp에 있는 값이 한번에 emp_test 테이블로 insert
notion image
notion image
notion image
 

문제풀며 익히기

 
Q1.
dept 테이블의 구조를 가지고 있는 dept_backup 테이블을 다음과 같이 생성하고 부서번호를 물어보게 하고 부서번호를 입력하면 해당 부서번호의 모든 행을 dept 테이블에서 찾아서 dept_backup 테이블에 입력되게 PL/SQL 코드를 작성하세요
< 테이블 구조 생성 > create table dept_backup as select * from dept where 1 = 2; ---------------------------------- < 정답 > accept p_deptno prompt '부서번호를 입력하세요 ~' declare v_deptno dept.deptno%type := &p_deptno; v_dept dept%rowtype; -- emp 테이블의 컬럼의 모든 데이터 타입을 그대로 begin -- 따르겠다. select * into v_dept from dept where deptno = v_deptno; insert into dept_backup values v_dept; end; /
notion image
 
 
 

035. 레코드 변수를 사용하여 DML 작업 쉽게 하기

📖
emp ----------------------> emp_test2 에 update 수행하기
 
예제)
emp 테이블과 똑같은 구조를 갖는 emp_test2 테이블을 생성하시오 ! 그리고 emp 테이블에서 사원번호만 emp_test2 테이블에 입력하세요.
create table emp_test2 as select * from emp where 1 = 2; insert into emp_test2(empno) select empno from emp; SQL> select empno, ename, sal 2 from emp_test2;
notion image
 
예제2)
사원번호를 입력받아 해당 사원번호의 데이터를 emp 테이블에서 불러와서 emp_test2 테이블에 입력되게 PL/SQL을 작성하시오
accept p_empno prompt '사원번호를 입력하세요 !' declare v_empno emp.empno%type := &p_empno; v_emp emp%rowtype ; begin select * into v_emp from emp where empno = v_empno; v_emp.sal := 0 ; update emp_test2 set row = v_emp where empno = v_empno; commit; #sql developer에서 보고싶다면 커밋해줘야함 end; / ======================================================== # SET ROW V_EMPNO의 값을 emp_test2에 한번에 UPDATE row사용해서 레코드변수를 이용해 한번에 update
notion image
notion image
 
 
 
 

문제풀며 익히기

 
Q1.
먼저 salgarde 테이블의 구조를 가지고 salgrade_test 테이블을 생성하세요 그리고 grade 컬럼에 1~5까지 입력하세요
-- salgrade 테이블 생성 스크립트 drop table salgrade; create table salgrade ( grade number(10), losal number(10), hisal number(10) ); insert into salgrade values(1,700,1200); insert into salgrade values(2,1201,1400); insert into salgrade values(3,1401,2000); insert into salgrade values(4,2001,3000); insert into salgrade values(5,3001,9999); commit; -------------------- create table salgrade_test as select * from salgrade where 1 = 2; insert into salgrade_test(grade) select grade from salgrade; =========================== SQL> select * from salgrade_test; GRADE LOSAL HISAL ---------- ---------- ---------- 1 2 3 4 5
notion image
 
Q2.
grade 를 물어보게하고 grade 를 입력하면 해당 등급의 데이터를 salgrade 테이블에서 읽어서 salgrade_test 테이블에 갱신되게하는 PL/SQL 프로그램을 작성하시오 !
  • 출력예시 : grade 를 입력하세요 ~ 2
accept p_grade prompt '등급을 입력하세요 ~ ' declare v_grade salgrade.grade%type := &p_grade; v_salgrade salgrade%rowtype ; begin select * into v_salgrade from salgrade where grade = v_grade; ? <---- 여기에 코드를 답으로 적어주세요. end; /
accept p_grade prompt '등급을 입력하세요 ~ ' declare v_grade salgrade.grade%type := &p_grade; v_salgrade salgrade%rowtype ; begin select * into v_salgrade from salgrade where grade = v_grade; update salgrade_test set row = v_salgrade where grade = v_grade; commit; end; /
notion image
 
 
 

7장. PL/SQL 에서의 명시적 커서

💡
  • 커서 사용 이유 : 여러개의 행을 화면에 출력하고 싶을 때 사용

036. 명시적 커서문 작성하기

📖
  • 커서(cursor)란 ? 메모리의 한 영역
      1. 암시적 커서 : 오라클이 알아서 자체적으로 할당한 메모리. 이 메모리는 PL/SQL에서 DML 문을 처리했을때 몇건의 행을 처리했는지 그 데이터를 저장할 메모리. 예: SQL%rowcount, SQL%found, SQL%notfound
      1. 명시적 커서 : PL/SQL 프로그래머가 프로그램 내에서 사용할 데이터를 미리 SQL 로 SELECT 해서 올려놓은 메모리 영역
        1. (프로그래머가 사용하기 위해 직접 만든것)
 
  • 명시적 커서 사용 순서
      1. 커서를 먼저 선언을 합니다.
      1. 커서를 엽니다.
      1. 커서내의 데이터를 첫행부터 하나씩 fetch 합니다.
      1. 더이상 fetch 할께 없을때까지 fetch 합니다.
      1. 커서를 닫습니다.
        1. *fetch : 데이터를 한 행 씩 읽어오는 것
       
★ 꼭 알아야하는 중요 파라미터
"db 에서 열 수 있는 최대 커서의 갯수”
< 방법 1 > SQL> show parameter cursors NAME TYPE VALUE ------------- ------------------ ------------------ open_cursors integer 300 session_cached_cursors integer 50 SQL> select name, value from v$parameter where name like '%cursor%'; !주의! open_cursor 가 300개 입니다. 300 개를 초과해서 커서를 열려고 하면 더 이상 열 수 가 없게 됩니다. 반드시 커서를 선언하고 열어서 썼으면 커서를 닫는 코드를 써주셔야 합니다.
 
예제)
커서선언 --> 커서오픈 --> 커서패치 --> 커서 닫기
accept p_deptno prompt '부서번호를 입력하시오 ~ ' declare cursor emp_cursor is -- 커서 선언 #emp_cursor는 커서이름 select ename, sal, job, deptno from emp where deptno = &p_deptno ; v_ename emp.ename%type; v_sal emp.sal%type; v_job emp.job%type; v_deptno emp.deptno%type; begin open emp_cursor; -- 커서 열기 loop fetch emp_cursor into v_ename, v_sal, v_job, v_deptno; -- 커서 패치 exit when emp_cursor%notfound; dbms_output.put_line( v_ename || ' ' || v_sal || ' ' || v_job || ' ' || v_deptno); end loop; close emp_cursor; --커서 닫기 end; / ============================================================== begin절에서 basic loop를 사용해 fetch 반복시킴 --커서 패치(커서에 있는 데이터들을 한 행씩 패치 시킴) fetch emp_cursor into v_ename, v_sal, v_job, v_deptno; KING 5000 PRESIDENT 10 패치됨 CLARK 2450 MANAGER 10 패치됨 (loop에 의해 반복) MILLER 1300 CLERK 10 패치됨 (loop에 의해 반복) --exit when exit when emp_cursor%notfound; => emp_cursor에서 더 이상 데이터가 발견되지 않을 때 종료 => 즉, 더 이상 fetch할게 없으면 종료하라 --end loop; close emp_cursor; --커서 닫기 나중에 문제가 될 수 있기 때문에 열고 닫는걸 짝꿍으로 수행하는게 좋음 ===================================================== < 결과 예쁘게 출력 > dbms_output.put_line( v_ename || ' ' || v_sal || ' ' || v_job || ' ' || v_deptno); 을 공백 대신 dbms_output.put_line( v_ename || chr(9) || v_sal || chr(9) || v_job || chr(9) || v_deptno); 로 바꾸면 됨
부서번호 10번인 경우 아래 결과를 커서 선언으로 메모리에 올려놓은 것.
notion image
결과
notion image
예쁘게 출력
notion image
 

문제풀며 익히기

 
Q1.
직업을 물어보게 하고 직업을 입력하면 해당 직업을 갖는 사원들의 이름과 월급과 직업이 출력되게 PL/SQL 코드를 작성하시오
accept p_job prompt '직업을 입력하시오 ~ ' declare cursor emp_cursor is -- 커서 선언 select ename, sal, job from emp where job = '&p_job' ; v_ename emp.ename%type; v_sal emp.sal%type; v_job emp.job%type; begin open emp_cursor; -- 커서 열기 loop fetch emp_cursor into v_ename, v_sal, v_job; -- 커서 패치 exit when emp_cursor%notfound; dbms_output.put_line( v_ename || chr(9) || v_sal || chr(9) || v_job || chr(9) ); end loop; close emp_cursor; end; /
ctrl + H 눌러 바꾸기
notion image
 
notion image
 
 

037. 레코드(record) 변수를 사용한 커서문 작성하기

📖
PL/SQL 레코드 변수란 " 스칼라 변수(값을 한개만 저장 가능)와는 다르게 여러개의 값을 저장하는 변수 “
  • 문법
    • 1. 레코드 변수를 사용안했을때 코드 : accept p_deptno prompt '부서번호를 입력하시오 ~ ' declare cursor emp_cursor is -- 커서 선언 select ename, sal, job, deptno from emp where deptno = &p_deptno ; v_ename emp.ename%type; v_sal emp.sal%type; v_job emp.job%type; v_deptno emp.deptno%type; -------------------------------------------------------- 부서번호가 10번일 때 10번인 사원들의 이름,월급,직업,부서번호를 커서 메모리에 올려놓는 것. ======================================================== 2. 레코드 변수를 사용했을때의 코드: accept p_deptno prompt '부서번호를 입력하시오 ~ ' declare cursor emp_cursor is -- 커서 선언 select ename, sal, job, deptno from emp where deptno = &p_deptno ; emp_record emp_cursor%rowtype; ---------------------------------------------------------- emp_record emp_cursor%rowtype; 커서의 데이터 유형을 그대로 따르겠다는 것. 그리고 행이 4개이므로 4개의 데이터를 담을 수 있는 블록 4가지가 레코드 변수에 만들어짐
 
예제)
accept p_deptno prompt '부서번호를 입력하시오 ~ ' declare cursor emp_cursor is -- 커서 선언 select ename, sal, job, deptno from emp where deptno = &p_deptno ; emp_record emp_cursor%rowtype; begin open emp_cursor; -- 커서 열기 loop fetch emp_cursor into emp_record; -- 커서 패치 exit when emp_cursor%notfound; dbms_output.put_line( emp_record.ename || chr(9) || emp_record.sal || chr(9) || emp_record.job || chr(9) || emp_record.deptno); end loop; close emp_cursor; end; /
notion image
notion image

문제풀며 익히기

 
Q1.
입사한 년도를 물어보게하고 입사한 년도를 입력하면 해당 입사한 년도에 입사한 사원들의 이름과 월급과 입사일이 출력되는 PL/SQL 코드를 레코드 변수를 이용한 커서루프문으로 작성하시오
  • 출력예시 : 입사한 년도를 4자리로 입력하시오 ~
set serveroutput on accept p_year prompt '입사한 년도를 4자리 입력하시오 ~ ' declare cursor emp_cursor is -- 커서 선언 select ename, sal, hiredate from emp where to_char(hiredate,'RRRR') = '&p_year' ; emp_record emp_cursor%rowtype; begin open emp_cursor; -- 커서 열기 loop fetch emp_cursor into emp_record; -- 커서 패치 exit when emp_cursor%notfound; dbms_output.put_line( emp_record.ename || chr(9) || emp_record.sal || chr(9) || emp_record.hiredate); end loop; close emp_cursor; end; /
notion image
 
 

038. 레코드를 사용한 커서 for loop문

📖
레코드를 사용한 커서 for loop문
  1. 기존 커서문: 1.커서 선언 --> 2. 커서 오픈 --> 3. 커서 패치 --> 4. 커서 닫기
  1. 레코드를 사용한 커서 for loop문 : 1. 커서 선언
    1. 즉, 커서 오픈, 커서패치, 커서 닫기 모두생략됨
 
예제 1) 기존 PL/SQL 커서문
직업을 물어보게하고, 직업을 입력하면, 해당 지업인 사원들의 이름, 월급, 직업을 출력하는 코드
accept p_job prompt '직업을 입력하시오 ~ ' declare cursor emp_cursor is -- 커서 선언 select ename, sal, job from emp where job = upper('&p_job') ; emp_record emp_cursor%rowtype; begin open emp_cursor; -- 커서 열기 loop fetch emp_cursor into emp_record; -- 레코드 변수에 커서 패치 exit when emp_cursor%notfound; dbms_output.put_line( emp_record.ename || chr(9) || emp_record.sal || chr(9) || emp_record.job ); end loop; close emp_cursor; end; / ========================== SQL> @P 직업을 입력하시오 ~ CLERK JAMES 950 CLERK SMITH 800 CLERK ADAMS 1100 CLERK MILLER 1300 CLERK PL/SQL 처리가 정상적으로 완료되었습니다.
notion image
 
예제 2) 커서 for 루프문 실습 예제
set verify off accept p_job prompt '직업을 입력하세요 ' declare cursor emp_cursor is -- 커서 선언 select ename, sal, job from emp where job= upper('&p_job'); begin for emp_record in emp_cursor loop dbms_output.put_line( emp_record.ename || chr(9) || emp_record.sal || chr(9) || emp_record.job ); end loop; end; / ====================================================== 레코드 변수를 선언해주지 않아도 BEGIN절에서 바로 사용 가능 따라서 emp_record를 바로 사용했음
notion image
아래는 job이 clerk일 때 커서 메모리에 올라가있는 데이터
notion image
notion image
결과
notion image
 

문제풀며 익히기

 
Q1.
위의 코드를 프로시져로 만들어 다음과 같이 수행되게 하시오
  • 수행할 사항 : exec pro_job('SALESMAN');
프로시저로 만들어야 data base에 pl/sql코드가 저장이 되기 때문! 프로시저 이름만 불러서 호출할 수 있는 장점이 있음. ------------------------------------------------------ create or replace procedure pro_job (p_job emp.job%type) is cursor emp_cursor is -- 커서 선언 select ename, sal, job from emp where job= upper(p_job); -- &을 없애줘야함 begin for emp_record in emp_cursor loop dbms_output.put_line( emp_record.ename || chr(9) || emp_record.sal || chr(9) || emp_record.job ); end loop; end; / exec pro_job('SALESMAN'); ====================================================== 레코드 변수를 선언해주지 않아도 BEGIN절에서 바로 사용 가능 따라서 emp_record를 바로 사용했음 에러시 show err을 하면 오류 확인 가능 (cmd창 명령어)
notion image
 
Q2.
부서번호를 입력해서 프로시저를 실행하면, 해당 부서번호에 속한 사원들의 이름과 월급과 부서번호가 출력되는 프로시져를 생성하시오.
  • 실행 문장 : exec pro_deptno(10);
create or replace procedure pro_deptno ( p_deptno emp.deptno%type ) is cursor emp_cursor is select ename, sal, deptno from emp where deptno = p_deptno; begin for emp_record in emp_cursor loop dbms_output.put_line( emp_record.ename || chr(9) || emp_record.sal || chr(9) || emp_record.deptno ); end loop; end; /
notion image
 

039. 서브쿼리를 사용하는 커서 for 루프문 작성하기

📖
서브쿼리를 사용하는 커서 for 루프문 작성하기
💡
서브쿼리를 사용하는 커서 for 루프문 작성하기
  1. 기존 커서문: 1.커서 선언 --> 2. 커서 오픈 --> 3. 커서 패치 --> 4. 커서 닫기
  1. 레코드를 사용한 커서 for loop문 : 1. 커서 선언
    1. 즉, 커서 오픈, 커서패치, 커서 닫기 모두생략됨
    2. 커서 선언만 해주면 오라클이 암시적으로 알아서 오픈, 패치, 닫기
  1. 서브쿼리를 사용하는 커서 for 루프문 : x
    1. 즉, 선언조차 하지 않고 사용 가능
⇒ ※ 점점 코드가 간결하고 단순화 되고 있습니다.
즉, 굳이 어렵게 하려고 하지 말고 DB 프로그래밍을 하라는 말.
PL/SQL이 DB프로그래밍의 끝판왕
 
예제)
부서번호를 물어보게하고 부서번호를 입력하면 해당 부서번호인 사원들의 이름과 월급과 부서번호가 출력되게 커서 for loop문을 작성하시오 !
  • 출력예시 ; 부서번호를 입력하세요 ~ 10
set verify off set serveroutput on accept p_deptno prompt '부서번호를 입력하세요 ' begin for emp_record in (select ename, sal, deptno from emp where deptno= &p_deptno ) loop dbms_output.put_line( emp_record.ename || chr(9) || emp_record.sal || chr(9) || emp_record.deptno ); end loop; end; / ====================================================================== declare절의 커서 선언이 없어지고, 커서 선언에 필요한 select 문장을 (커서 선언하는 select문장) begin절에 바로 넣고 emp_record 라는 레코드 변수를 바로 선언해주며 값을 넣음 즉, 노란 형관펜 부분이 오라클 메모리로 올라가는 것. 올라갈 때 부서번호 10번이 들어왔다면, 아래 cmd창 결과와 같이 부서번호 10번인 사원들이 오라클 메모리로 올라가는 것 올라간 후 바로 for emp_record in으로 한 행 fetch후 출력되고 for emp_record in으로 한 행 fetch후 출력되고 for emp_record in으로 한 행 fetch후 출력된 후 패치할게 없으면 end loop; chr(9) => tab키 역
notion image
notion image
notion image

문제풀며 익히기

 
Q1.
예제의 PL/SQL을 프로시져로 생성하여 다음과 같이 수행되게하시오
  • 실행 문 : SQL> exec pro_deptno(10);
create or replace procedure pro_deptno ( p_deptno emp.deptno%type ) is begin for emp_record in (select ename, sal, deptno from emp where deptno= p_deptno ) loop dbms_output.put_line( emp_record.ename || chr(9) || emp_record.sal || chr(9) || emp_record.deptno ); end loop; end; / exec pro_deptno(10); ================================================================= is 또는 as를 넣어주고 치환변수를 날려줘야함. 즉, &제거하기 (프로시져 생성시 치환변수 사용안됨) 자바 프로그래머들이 procedure을 자주 사용. 그 프로시져 관리는 DBA들이 하고 있음
notion image
 
Q2.
아래와 같이 직업을 입력해서 프로시져를 실행하면 해당 직업인 사원들의 이름, 월급, 직업이 출력되게 프로시저를 실행하시오.
  • 실행 문 : SQL> exec pro_job(’SALESMAN’);
create or replace procedure pro_job ( p_job emp.job%type ) is begin for emp_record in (select ename, sal, job from emp where job= p_job ) loop dbms_output.put_line( emp_record.ename || chr(9) || emp_record.sal || chr(9) || emp_record.job ); end loop; end; / ===================================================== 위 코드는 DBA 관리 수업시 자주 사용할 예정
notion image
 
 

040. where current of 절 사용법

📖
where current of 절 사용법 update set 문에서 사용하는 절
  • Where current of 사용 이유
    • 방금 fetch해온 행의 data를 갱신 - 이 절을 사용하지 않으면 예를들어 where ename = v_ename 이런식으로 해줘야하는데, 동명이인이 있을경우 이상해짐 - 따라서 for update 와 함께 where current of를 사용해주는게 좋음
       
  • 문법
    • update emp set grade = v_grade where current of emp_cursor; ===================================================== emp_cursor에서 지금 패치한 행의 데이터를 갱신하라는 의미
 
예제 1) emp 테이블에 grade 라는 컬럼을 추가하시오
alter table emp add grade varchar2(10);
 
예제 2)
emp 테이블에 grade 라는 컬럼에 다음의 조건에 해당되는 데이터로 값을 갱신하시오 (커서문을 이용한 PL/SQL로 자동으로 등급 값이 갱신되도록 하고 싶음)
  • 조건
    • 월급이 3000 이상이면 A 등급 월급이 2000 이상이면 B 등급 월급이 1000 이상이면 C 등급 나머지 월급은 D 등급으로 갱신
declare cursor emp_cursor is -- 커서 선언 select empno, ename, sal from emp for update; -- 내가 갱신하고 있는 데이터를 -- 누구도 갱신하지 못하도록 LOCK을 거는 것 -- FOR UPDATE를 반드시 선언해줘야 -- 아래의 WHERE CURRENT OF를 사용 할 수 있음 emp_record emp_cursor%rowtype; -- 레코드 변수 생성 v_grade varchar2(5); --스칼라 변수 생성 begin open emp_cursor ; -- 커서 열기(메모리에 올라간 것 열기) loop fetch emp_cursor into emp_record; -- fetch exit when emp_cursor%notfound; if emp_record.sal >= 3000 then v_grade := 'A'; elsif emp_record.sal >= 2000 then v_grade := 'B'; elsif emp_record.sal >=1000 then v_grade := 'C' ; else v_grade := 'D'; end if; update emp set grade = v_grade where current of emp_cursor; -- 매우 중요 end loop; commit; close emp_cursor; end; / =============================================================== FOR UPDATE; 를 사용하여 LOCK을 걸어둠. 즉, EMP의 총 14개 행이 잠겨버림 emp_record emp_cursor%rowtype; 레코드 변수를 선언하는데, emp_cursor의 type을 따름 fetch emp_cursor into emp_record; 커서(emp_cursor)에 있는 데이터를 레코드(emp_record)에 넣음 exit when emp_cursor%notfound; 커서에 더이상 데이터가 없으면 종료 update emp set grade = v_grade where current of emp_cursor; -- 매우 중요 방금 fetch해온 행의 data를 갱신 즉, 방금 king의 데이터를 fetch에 의해 가져왔으면, 방금 fetch된 행의 v_grade 값을 갱신하겠다는 의미. => if문에 의해 해당된 조건인 emp_record.sal >= 3000에 해당하므로 v_grade에 'A'가 할당되며 그 할당 된 값으로 갱신하겠다는 의미.
notion image
 

문제풀며 익히기

 
Q1.
방금 수행했던 예제2번을 프로시져로 만들어서 다음과 같이 직업을 입력하고 실행하면, 해당 직업인 사원들의 데이터만 아래의 월급의 조건에 맞춰 grade 값이 갱신되게하시오.
  • 실행 문 : exec cursor_job('SALESMAN');
  • 조건 :
    • 월급이 3000 이상이면 A 등급 월급이 2000 이상이면 B 등급 월급이 1000 이상이면 C 등급 나머지 월급은 D 등급으로 갱신
SQL> update emp 2 set grade = null; SQL> commit; ------------------------------------------------------------------- create or replace procedure cursor_job (p_job emp.job%type) is cursor emp_cursor is -- 커서 선언 select empno, ename, sal from emp where job = p_job #where절에 조건 추가해주면 됨 for update; -- 내가 갱신하고 있는 데이터를 -- 누구도 갱신하지 못하도록 LOCK을 거는 것 -- FOR UPDATE를 반드시 선언해줘야 -- 아래의 WHERE CURRENT OF를 사용 할 수 있음 emp_record emp_cursor%rowtype; -- 레코드 변수 생성 v_grade varchar2(5); --스칼라 변수 생성 begin open emp_cursor ; -- 커서 열기(메모리에 올라간 것 열기) loop fetch emp_cursor into emp_record; -- fetch exit when emp_cursor%notfound; if emp_record.sal >= 3000 then v_grade := 'A'; elsif emp_record.sal >= 2000 then v_grade := 'B'; elsif emp_record.sal >=1000 then v_grade := 'C' ; else v_grade := 'D'; end if; update emp set grade = v_grade where current of emp_cursor; -- 매우 중요 end loop; commit; close emp_cursor; end; /
notion image
 

8장. 예외처리

041. 예외처리란 무엇인가?

📖
예외처리란 무엇인가?
예외 처리(exception handling)란, 프로그래머가 예기치못한 예외의 발생에 미리 대처하는 코드를 작성하는 것으로,
실행중인 프로그램의 비정상적인 종료를 막고 상태를 정상상태로 유지하는 것이다. ⇒ 즉, 코드에 이상은 없는데, PL/SQL에 입력된 데이터에 문제가 있는 경우
미리 대처하는 코드를 작성해. 비정상적 종료를 막고, 정상상태로 유지
 
  • 문법
    • declare 변수 선언 ; 예외 선언 ; begin 실행문; 예외처리문; end; / ======================================= --declare 절에서 예외선언을 해줘야 begin절에서 예외처리문 작성 가능
 

실습

 
▣ 실습1. —예외처리 하기 전 PL/SQL 코드
이름을 입력하면 해당 사원의 월급이 출력되는 PL/SQL코드를 작성하시오 !
set verify off set serveroutput on accept p_ename prompt '이름을 입력하세요 ~' declare v_ename emp.ename%type := upper('&p_ename'); v_sal emp.sal%type ; begin select sal into v_sal from emp where ename = v_ename; dbms_output.put_line( v_sal ); end; / ============================= -- 존재하는 이름 입력시 SQL> @p 이름을 입력하세요 ~scott 3000 --존재하지 않는 입력시 SQL> @p 이름을 입력하세요 ~aaaa !!ERROR!! declare * 1행에 오류: ORA-01403: 데이터를 찾을 수 없습니다. ORA-06512: 5=> 코드문제가 아니라 입력된 데이터에 문제가 있을 때 발생하는 오류
notion image
 
▣ 실습2.
MARTIN 의 이름을 SCOTT 으로 변경하고 실습1번의 PL/SQL 코드를 수행하는데 이름을 SCOTT 으로 입력해보세요.
update emp set ename='SCOTT' where ename='MARTIN'; commit; ---------------------------------------------- SQL> select count(*) 2 from emp 3 where ename = 'SCOTT'; COUNT(*) ---------- 2 =============================================== --SCOTT 입력해보기 SQL> @P 이름을 입력하세요 ~SCOTT !!ERROR!! declare * 1행에 오류: ORA-01422: 실제 인출은 요구된 것보다 많은 수의 행을 추출합니다 ORA-06512: 5=> 이 에러 메시지를 고객의 눈 높이에 맞춰서 바꿔주는게 예외처리
notion image
 
▣ 실습3. 위의 경우를 대비하기 위한 예외처리를 하세요.
set verify off set serveroutput on accept p_ename prompt '이름을 입력하세요 ~' declare v_ename emp.ename%type := upper('&p_ename'); v_sal emp.sal%type ; begin select sal into v_sal from emp where ename = v_ename; dbms_output.put_line( v_sal ); EXCEPTION ---예외 처리절 when too_many_rows THEN dbms_output.put_line('해당 사원이름이 여러명 있습니다. 고객센터로 문의하세요' ); end; / -------------------------------------------------------------- SQL> @P 이름을 입력하세요 ~SCOTT 해당 사원이름이 여러명 있습니다. 고객센터로 문의하세요 =============================================================== too_many_rows : 오라클 개발자가 만들어놓은 예외 함수로 여러개가 스칼라 변수에 담기려고 할 때 발생하는 에러 예외 함수
notion image
 
 
 

문제풀며 익히기

 
실습환경 구성
EMP 테이블 초기화 (ADAMS의 사원번호를 SCOTT과 동일하게 7788로 변경)
alter session set nls_Date_format='RR/MM/DD'; drop table emp; drop table dept; CREATE TABLE DEPT (DEPTNO number(10), DNAME VARCHAR2(14), LOC VARCHAR2(13) ); INSERT INTO DEPT VALUES (10, 'ACCOUNTING', 'NEW YORK'); INSERT INTO DEPT VALUES (20, 'RESEARCH', 'DALLAS'); INSERT INTO DEPT VALUES (30, 'SALES', 'CHICAGO'); INSERT INTO DEPT VALUES (40, 'OPERATIONS', 'BOSTON'); CREATE TABLE EMP ( EMPNO NUMBER(4) NOT NULL, ENAME VARCHAR2(10), JOB VARCHAR2(9), MGR NUMBER(4) , HIREDATE DATE, SAL NUMBER(7,2), COMM NUMBER(7,2), DEPTNO NUMBER(2) ); INSERT INTO EMP VALUES (7839,'KING','PRESIDENT',NULL,'81-11-17',5000,NULL,10); INSERT INTO EMP VALUES (7698,'BLAKE','MANAGER',7839,'81-05-01',2850,NULL,30); INSERT INTO EMP VALUES (7782,'CLARK','MANAGER',7839,'81-05-09',2450,NULL,10); INSERT INTO EMP VALUES (7566,'JONES','MANAGER',7839,'81-04-01',2975,NULL,20); INSERT INTO EMP VALUES (7654,'MARTIN','SALESMAN',7698,'81-09-10',1250,1400,30); INSERT INTO EMP VALUES (7499,'ALLEN','SALESMAN',7698,'81-02-11',1600,300,30); INSERT INTO EMP VALUES (7844,'TURNER','SALESMAN',7698,'81-08-21',1500,0,30); INSERT INTO EMP VALUES (7900,'JAMES','CLERK',7698,'81-12-11',950,NULL,30); INSERT INTO EMP VALUES (7521,'WARD','SALESMAN',7698,'81-02-23',1250,500,30); INSERT INTO EMP VALUES (7902,'FORD','ANALYST',7566,'81-12-11',3000,NULL,20); INSERT INTO EMP VALUES (7369,'SMITH','CLERK',7902,'80-12-09',800,NULL,20); INSERT INTO EMP VALUES (7788,'SCOTT','ANALYST',7566,'82-12-22',3000,NULL,20); INSERT INTO EMP VALUES (7788,'ADAMS','CLERK',7788,'83-01-15',1100,NULL,20); INSERT INTO EMP VALUES (7876,'MILLER','CLERK',7782,'82-01-11',1300,NULL,10); commit;
 
Q1.
사원 번호를 물어보게 하고 사원번호를 입력하면 해당 사원의 직업이 출력되는 PL/SQL을 생성하세요. 사원번호가 여러개가 있으면 '해당 사원번호가 중복되어 여러개가 있습니다. 고객센터로 문의하세요' 라는 메세지가 출력되게 예외처리를 하세요.
  • 출력 예시 :
    • 사원번호를 입력하세요 ~ 7788
      SCOTT 의 직업은 ANALYST 입니다.
set verify off set serveroutput on accept p_empno prompt '사원번호를 입력하세요 ~' declare v_empno emp.empno%type := &p_empno ; v_ename emp.ename%type ; v_job emp.job%type ; begin select ename, job into v_ename, v_job from emp where empno = v_empno; dbms_output.put_line( v_ename ||'의 직업은 ' || v_job || ' 입니다.'); EXCEPTION ---예외 처리문 when too_many_rows THEN dbms_output.put_line('해당 사원번호가 중복되어 여러개가 있습니다. 고객센터로 문의하세요' ); end; / ----------------------- SQL> @p 사원번호를 입력하세요 ~7839 KING의 직업은 PRESIDENT 입니다. SQL> @p 사원번호를 입력하세요 ~7788 해당 사원번호가 중복되어 여러개가 있습니다. 고객센터로 문의하세요
notion image
notion image

042. 오라클에서 미리 정의한 예외처리

📖
오라클에서 미리 정의한 예외처리
오라클에서 미리 정의한 예외란 PL/SQL 프로그램이 수행될 때 발생하는 예상치 못한 오류에 대처할 수 있는 예외처리 함수들을 오라클에서 미리 만들어 놓은것으로 사용하는것을 말합니다.
 
  • 오라클 예외처리의 종류 3가지
      1. 오라클에서 미리 정의한 예외
        1. NO_DATA_FOUND, TOO_MANY_ROWS 를 가장 많이 사
      1. 오라클에서 미리 정의하지 않은 예외
      1. 사용자 정의 예외
 
notion image
 
실습 환경 구성
alter session set nls_Date_format='RR/MM/DD'; drop table emp; drop table dept; CREATE TABLE DEPT (DEPTNO number(10), DNAME VARCHAR2(14), LOC VARCHAR2(13) ); INSERT INTO DEPT VALUES (10, 'ACCOUNTING', 'NEW YORK'); INSERT INTO DEPT VALUES (20, 'RESEARCH', 'DALLAS'); INSERT INTO DEPT VALUES (30, 'SALES', 'CHICAGO'); INSERT INTO DEPT VALUES (40, 'OPERATIONS', 'BOSTON'); CREATE TABLE EMP ( EMPNO NUMBER(4) NOT NULL, ENAME VARCHAR2(10), JOB VARCHAR2(9), MGR NUMBER(4) , HIREDATE DATE, SAL NUMBER(7,2), COMM NUMBER(7,2), DEPTNO NUMBER(2) ); INSERT INTO EMP VALUES (7839,'KING','PRESIDENT',NULL,'81-11-17',5000,NULL,10); INSERT INTO EMP VALUES (7698,'BLAKE','MANAGER',7839,'81-05-01',2850,NULL,30); INSERT INTO EMP VALUES (7782,'CLARK','MANAGER',7839,'81-05-09',2450,NULL,10); INSERT INTO EMP VALUES (7566,'JONES','MANAGER',7839,'81-04-01',2975,NULL,20); INSERT INTO EMP VALUES (7654,'MARTIN','SALESMAN',7698,'81-09-10',1250,1400,30); INSERT INTO EMP VALUES (7499,'ALLEN','SALESMAN',7698,'81-02-11',1600,300,30); INSERT INTO EMP VALUES (7844,'TURNER','SALESMAN',7698,'81-08-21',1500,0,30); INSERT INTO EMP VALUES (7900,'JAMES','CLERK',7698,'81-12-11',950,NULL,30); INSERT INTO EMP VALUES (7521,'WARD','SALESMAN',7698,'81-02-23',1250,500,30); INSERT INTO EMP VALUES (7902,'FORD','ANALYST',7566,'81-12-11',3000,NULL,20); INSERT INTO EMP VALUES (7369,'SMITH','CLERK',7902,'80-12-09',800,NULL,20); INSERT INTO EMP VALUES (7788,'SCOTT','ANALYST',7566,'82-12-22',3000,NULL,20); INSERT INTO EMP VALUES (7876,'ADAMS','CLERK',7788,'83-01-15',1100,NULL,20); INSERT INTO EMP VALUES (7876,'MILLER','CLERK',7782,'82-01-11',1300,NULL,10); commit;
 
▣ 실습1.
아래의 PL/SQL 코드를 수행하는데 사원 이름을 물어볼때 없는 사원 이름을 넣고 실행하세요
set verify off set serveroutput on accept p_ename prompt '이름을 입력하세요 ~' declare v_ename emp.ename%type := upper('&p_ename'); v_sal emp.sal%type ; begin select sal into v_sal from emp where ename = v_ename; dbms_output.put_line( v_sal ); end; / ---------------------------------------- SQL> @P 이름을 입력하세요 ~aaaa declare * 1행에 오류: ORA-01403: 데이터를 찾을 수 없습니다. ORA-06512: 5
notion image
 
▣ 실습2.
위의 코드를 수정하여, 없는 사원 이름을 넣으면, 에러가 발생하지 않고 , 예외 처리를 해서 해당 사원은 없습니다. 라는 메세지가 화면에 출력되게 하시오.
set verify off set serveroutput on accept p_ename prompt '이름을 입력하세요 ~' declare v_ename emp.ename%type := upper('&p_ename'); v_sal emp.sal%type ; begin select sal into v_sal from emp where ename = v_ename; dbms_output.put_line( v_sal ); EXCEPTION when no_data_found then dbms_output.put_line('해당 사원은 없습니다.'); end; / ------------------------------------------------------- SQL> @p 이름을 입력하세요 ~aaaa 해당 사원은 없습니다.
notion image
 
 

문제풀며 익히기

 
Q1.
다음과 같이 부서번호를 물어보게 하고 부서번호를 입력하면 해당 부서번호의 부서위치가 출력되는 PL/SQL 코드를 실행하는데 없는 부서번호를 입력하면 해당 부서번호는 없는 부서번호입니다. 라는 메세지가 출력되게 예외처리를 하세요.
set serveroutput on accept p_deptno prompt '부서번호를 입력하세요 ~' declare v_deptno dept.deptno%type := &p_deptno; v_loc dept.loc%type ; begin select loc into v_loc from dept where deptno = v_deptno; dbms_output.put_line( v_loc ); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('해당 부서번호는 없는 부서번호입니다.'); end; /
notion image
 
 
 

043. 미리 정의하지 않은 오라클 예외처리

📖
미리 정의하지 않은 오라클 예외처리 (OCP시험에 자주 출제)
오라클에서 미리 정의 하지 않은 예외처리 인 경우는 직접 예외처리를 해야하는데 pragma exception_init 를 이용해서 예외처리 하면 됩니다.
declare [정의할 예외 이름] exception; --e_insert_except라는 예외를 선언하겠다는 것 pragma exception_init([예외이름], [연결해줄 에러번호]); --예외와 에러번호 연결
 
▣ 실습1. 오라클에서 미리 정의하지 않은 예외처리
SCRIPT
아래의 스크립트를 demo.sql로 저장하시오
alter session set nls_Date_format='RR/MM/DD'; drop table emp; drop table dept; CREATE TABLE DEPT (DEPTNO number(10), DNAME VARCHAR2(14), LOC VARCHAR2(13) ); INSERT INTO DEPT VALUES (10, 'ACCOUNTING', 'NEW YORK'); INSERT INTO DEPT VALUES (20, 'RESEARCH', 'DALLAS'); INSERT INTO DEPT VALUES (30, 'SALES', 'CHICAGO'); INSERT INTO DEPT VALUES (40, 'OPERATIONS', 'BOSTON'); CREATE TABLE EMP ( EMPNO NUMBER(4) NOT NULL, ENAME VARCHAR2(10), JOB VARCHAR2(9), MGR NUMBER(4) , HIREDATE DATE, SAL NUMBER(7,2), COMM NUMBER(7,2), DEPTNO NUMBER(2) ); INSERT INTO EMP VALUES (7839,'KING','PRESIDENT',NULL,'81-11-17',5000,NULL,10); INSERT INTO EMP VALUES (7698,'BLAKE','MANAGER',7839,'81-05-01',2850,NULL,30); INSERT INTO EMP VALUES (7782,'CLARK','MANAGER',7839,'81-05-09',2450,NULL,10); INSERT INTO EMP VALUES (7566,'JONES','MANAGER',7839,'81-04-01',2975,NULL,20); INSERT INTO EMP VALUES (7654,'MARTIN','SALESMAN',7698,'81-09-10',1250,1400,30); INSERT INTO EMP VALUES (7499,'ALLEN','SALESMAN',7698,'81-02-11',1600,300,30); INSERT INTO EMP VALUES (7844,'TURNER','SALESMAN',7698,'81-08-21',1500,0,30); INSERT INTO EMP VALUES (7900,'JAMES','CLERK',7698,'81-12-11',950,NULL,30); INSERT INTO EMP VALUES (7521,'WARD','SALESMAN',7698,'81-02-23',1250,500,30); INSERT INTO EMP VALUES (7902,'FORD','ANALYST',7566,'81-12-11',3000,NULL,20); INSERT INTO EMP VALUES (7369,'SMITH','CLERK',7902,'80-12-09',800,NULL,20); INSERT INTO EMP VALUES (7788,'SCOTT','ANALYST',7566,'82-12-22',3000,NULL,20); INSERT INTO EMP VALUES (7876,'ADAMS','CLERK',7788,'83-01-15',1100,NULL,20); INSERT INTO EMP VALUES (7876,'MILLER','CLERK',7782,'82-01-11',1300,NULL,10); commit;
1. 위에 첨부된 script 저장 2. emp 와 dept 를 초기화 합니다. SQL> @demo.sql 3. dept 테이블에 loc 컬럼에 not null 제약을 겁니다. SQL> alter table dept modify loc not null; -- null값이 들어갈 수 없음 4. dept 테이블에 데이터를 입력하는 PL/SQL 프로그램을 작성합니다. accept p_deptno prompt '부서번호를 입력하세요~ ' --10 accept p_dname prompt '부서명을 입력하세요~ ' --hr accept p_loc prompt '부서위치를 입력하세요~ ' --seoul BEGIN insert into dept values(&p_deptno, '&p_dname', '&p_loc'); --문자형은 '로 둘러줌 commit; end; / SQL> @p 부서번호를 입력하세요~ 70 부서명을 입력하세요~ hr 부서위치를 입력하세요~ seoul PL/SQL 처리가 정상적으로 완료되었습니다. SQL> select * from dept; DEPTNO DNAME LOC ---------- ---------------------------- -------------------------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS 30 SALES CHICAGO 40 OPERATIONS BOSTON 10 ; ; 70 hr seoul 5. loc 에 null 값을 입력해봅니다. 부서번호를 입력하세요~ 80 부서명을 입력하세요~ hr2 부서위치를 입력하세요~ SQL> @p 부서번호를 입력하세요~ 80 부서명을 입력하세요~ hr2 부서위치를 입력하세요~ !!ERROR!! BEGIN * 1행에 오류: ORA-01400: NULL을 ("C##SCOTT"."DEPT"."LOC") 안에 삽입할 수 없습니다 ORA-06512: 26. 예외처리를 합니다. (위의 예외 번호 외워두기) accept p_deptno prompt '부서번호를 입력하세요~ ' accept p_dname prompt '부서명을 입력하세요~ ' accept p_loc prompt '부서위치를 입력하세요~ ' declare e_insert_except exception; --e_insert_except라는 예외를 선언하겠다는 것 pragma exception_init(e_insert_except, -01400); --예외와 에러번호 연결 --prama exception_init([예외이름],[연결해줄 에러번호]) BEGIN insert into dept values(&p_deptno, '&p_dname', '&p_loc'); commit; exception when e_insert_except then dbms_output.put_line(chr(9)); -- chr(9) = tab키 dbms_output.put_line('입력 못했어요. 부서위치를 꼭 입력하셔야해요 ~' ); end; / -----------------------------------------------------
notion image
notion image
notion image
 

문제풀며 익히기

 
Q1.
emp 테이블에 empno 컬럼에 중복된 데이터가 입력되지 못하게 primary key 제약을 거시오
alter table emp add constraint emp_empno_pk primary key(empno);
notion image
 
Q2.
emp 테이블에 데이터를 입력하는 아래의 PL/SQL 프로그램을 작성하시오
accept p_empno prompt '사원번호를 입력하세요~ ' accept p_ename prompt '사원이름을 입력하세요~ ' accept p_sal prompt '월급을 입력하세요~ ' BEGIN insert into emp(empno, ename, sal) values(&p_empno, '&p_ename', &p_sal ); commit; end; / ------------------------------------------------ SQL> @p 사원번호를 입력하세요~ 7788 사원이름을 입력하세요~ james 월급을 입력하세요~ 2500 !!ERROR!! 1행에 오류: ORA-00001: 무결성 제약 조건(C##SCOTT.EMP_EMPNO_PK)에 위배됩니다 ORA-06512: 2
notion image
 
Q3.
empno 에 중복된 사원 번호를 입력했을 때
‘해당 사원번호는 기존에 존재합니다.’ 라는 메세지가 출력되게 하시오
accept p_empno prompt '사원번호를 입력하세요~ ' accept p_ename prompt '사원이름을 입력하세요~ ' accept p_sal prompt '월급을 입력하세요~ ' DECLARE E_EXCEPT1 EXCEPTION; PRAGMA EXCEPTION_INIT( E_EXCEPT1, -00001); BEGIN insert into emp(empno, ename, sal) values(&p_empno, '&p_ename', &p_sal ); commit; -- exception 전에 넣기 -- 그래야 insert에서 에러가 발생했을 때 commit을 수행하지 않고 -- 바로 exception절로 뛰어넘을 수 있음 EXCEPTION WHEN E_EXCEPT1 THEN DBMS_OUTPUT.PUT_LINE('해당 사원번호는 기존에 존재합니다.'); end; /
 
 
 

044. 사용자 정의 예외처리

📖
사용자 정의 예외처리
PL/SQL 프로그램을 개발하다보면 오라클이 제공하는 예외 만으로는 다양한 종류의 예외를 다 표현할 수 없습니다.
 
오라클 에러가 발생하는 것은 아닌데, 특정 부분을 예외처리 하고 싶을 때
이때, 직접 예외를 정의하여 사용하는 예외를 사용자 정의 예외 라고 합니다
 
if [조건] THEN raise [예외명]; end if;
 
예제)
set serveroutput on accept p_deptno prompt '부서번호를 입력하세요 ~ ' declare v_deptno number := &p_deptno; e_invalid exception; -- 예외 선언 begin update dept set dname ='testing' where deptno = v_deptno; if SQL%NOTFOUND THEN --암시적 커서..에서 본 조건 raise e_invalid; end if; commit; exception when e_invalid then dbms_output.put_line('부서번호가 없어요'); end; / ================================================================ if SQL%NOTFOUND THEN UPDATE문에 의해서 0건의 행이 갱신되었다고 나오면 SQL%NOTFOUND가 TRUE가 됨 즉, 없는 부서번호 90번을 넣으면 90번은 DEPTNO에 없기 때문에 0건의 행이 갱신되었다고 나와서 SQL%NOTFOUND = TRUE가 되는 것 이게 TRUE라면 예외처리 하도록 만들어진 SQL문임. 없는 번호를 넣으면 0 건의 행이 갱신되지, 오라클 에러가 발생하는 것이 아님 => 따라서 사용자 정의 예외처리 해준 것
notion image

문제풀며 익히기

 
Q1.
이름을 물어보게하고 이름을 입력하면 해당 사원의 월급이 출력되는 PL/SQL 코드를 작성하세요.
그런데 월급이 3000 이상이면 해당 사원의 월급은 볼 수 없니다. 라는 메세지가 출력되게 사용자 정의 예외처리를 생성하세요.
set serveroutput on accept p_ename prompt '이름을 입력하세요 ~ ' declare v_ename emp.ename%type := upper('&p_ename'); v_sal emp.sal%type; e_invalid exception; begin select sal into v_sal from emp where ename = v_ename; if v_sal >= 3000 THEN raise e_invalid; end if; dbms_output.put_line( v_sal ); commit; --예외 전에 commit; exception when e_invalid then dbms_output.put_line('해당 사원의 월급은 볼 수 없습니다.'); end; / ================================================================= < 차이 > if v_sal >= 3000 THEN dbms_output.put_line('해당 사원의 월급은 볼 수 없습니다.'); 위와 같이 조건으로 그냥 특정 조건이면 메세지를 출력하도록 할 수 있지만, exception의 장점은 중간에 여러 코드 줄을 실행하지 않고 skip한 후 진행할 수 있다.
notion image
 

045. 예외트랩함수(SQLCODE, SQLERRM)

📖
예외트랩함수 PL/SQL 프로그램에서 발생하는 오류 번호와 오류 메세지를 잡아내는 함수
  • 예외트랩 함수의 종류 2가지
      1. SQLCODE : PL/SQL에서 발생하는 에러 번호를 트랩(trap) 하는 함수
        1. PL/SQL에서 오류번호를 잡아내는 함수
      1. SQLERRM : PL/SQL에서 발생하는 에러 메세지를 트랩(trap) 하는 함수
      EX) 쿠팡 오늘 총 매출액 -> 매출 테이블 - 프로시저 - 스케줄 이 두가지를 통해 자동화 해놓는데, 만약 밤 사이 에러가 발생했다면..? 빠르게 원인 파악을 해줘야함. 원인 파악을 했을 때 어떤 에러가 발생했는지 잡아내는 함수를 사용해서 각각 테이블에 입력하게 하는 코드가 필요함
 
▣ 관련 실습
1. 직업과 직업별 토탈월급을 저장할 테이블을 생성합니다. create table daily_machul ( job varchar2(10), sumsal number(10) ); 2. 사원 테이블에서 직업과 직업별 토탈월급을 daily_machul 에 입력하는 프로시져를 생성합니다. create or replace procedure daily_sum is begin insert into daily_machul select job, sum(sal) from emp group by job; commit; end; / -- 직업과 직업별 토탈 월급을 ISERT함(DAILY_MACHUL에) 3. 프로시져를 수행합니다. exec daily_sum; SQL> SELECT * FROM DAILY_MACHUL; JOB SUMSAL -------------------- ---------- PRESIDENT 5000 MANAGER 8275 SALESMAN 5600 CLERK 4150 ANALYST 6000 4. 프로시져를 수행했을때 오류가 나겠금 데이터를 수정합니다. update emp set sal = 0 where job='SALESMAN'; truncate table daily_machul; alter table daily_machul add constraint m_ck check( sumsal between 100 and 9000); -- CHECK제약 걸려있음(100~9000사이) exec daily_sum; 1행에 오류: ORA-02290: 체크 제약조건(C##SCOTT.M_CK)이 위배되었습니다 5. 프로시져를 수행했을 때 발생하는 오류가 error 테이블에 입력되게 daily_sum 프로시져를 수정합니다. create table error --LOG_TABLE 혹은 ERROR_TABLE만듦 ( e_date timestamp, -- 에러 발생 날짜 e_code number(10), -- 에러 코드 e_message varchar2(200) ); --에러메세 create or replace procedure daily_sum is e_code number; e_message varchar2(200); begin insert into daily_machul select job, sum(sal) from emp group by job; commit; | exception 추 | when others then --모든 에러를 다 잡아내는| rollback; 된 | e_code := SQLCODE; -- 에러번호 | e_message := SQLERRM; --에러 메세지|| insert into error | values ( systimestamp, e_code, e_message ); --현재시간 --에러코드 --에러메세지 end; / SQL> exec daily_sum; PL/SQL 처리가 정상적으로 완료되었습니다. --에러나지 않고 정상 완료 -- 하지만 에러 났을 순 있음 SQL> select * from daily_machul; 선택된 레코드가 없습니다. SQL> select * from error; SELECT * FROM ERROR;
notion image
notion image

문제풀며 익히기

 
Q1.
위의 예제의 프로시져를 수행했을때 발생하는 에러 코드와 에러 메세지가 error 테이블에 입력됨과 동시에 출력도 되게 프로시져를 수정하세요
  • 실행문 : exec daily_sum;
SQL> truncate table error; --------------------------------------------------------- create or replace procedure daily_sum is e_code number; e_message varchar2(200); begin insert into daily_machul select job, sum(sal) from emp group by job; commit; exception when others then rollback; e_code := SQLCODE; e_message := SQLERRM; insert into error values ( systimestamp, e_code, e_message ); dbms_output.put_line('에러 번호는 : ' || e_code || '에러 메세지는 : ' || e_message ); end; /
notion image
 
 
 

9장. 프로시저(Procedure)

046.Procedure 의 정의와 장점

📖
프로시저(procedure) 란?
개발자가 자주 실행해야하는 특정 작업을 필요할 때 호출하기위해 절차적인 언어를 이용하여 이름이 있는 프로그램 모듈(Block)을 의미한다.
 
즉, PL/SQL코드에 이름을 부여해서 DB에 저장
DB에 저장된 프로시저 확인하기 invaild된 프로시저를 컴파일 눌러서 vaild 할 수 있음.
notion image
 
  • 프로시저(procedure) 의 장점?
      1. 프로그램 동작의 간편한 실행
        1. 하나의 요청으로 여러 SQL문을 실행 할 수 있습니다.
        2.  
      1. 보안성 향상
        1. 프로시저 단위로 실행 권한을 부여할 수 있기 때문에 기본적인 보안 사고에대처가 유연한 편이다. ( 즉, A에게는 프로시저 k, B에게는 프로시저 m)
           
      1. 네트워크 소요 시간 절감
        1. 쿼리를 다중으로 실행하는 경우, 한번의 호출을 통해 다중의 쿼리가 실행되기 때문에 네트워크에 대한 부담과 소요 시간을 줄일 수 있다.
           
      1. 소스 코드의 운영 배포 용이성
        1. 별도의 WAS 서버 재기동 없이 프로시져 수정으로 조회, 수정, 추가 등의 가벼운 소스 변경 등이 가능하여, 긴급 배포 등이 용이하다.
          Web Was <-------> DB HTML코드 JAVA코드 JAVA코드 수정 시 Was 서버 재가동 없이 프로시저 수정으로 조회, 수정 ,추가 등의 가벼운 소스 변경은 가능
           
  • 문법
    • create [ or replace ] procedure [ 프로시저 이름 ] ( 입력 매개 변수 데이터 타입 ) is/as --is 또는 as [ 선언부 ] begin [ 실행부 ] exception [ 예외 처리부 ] end [ 프로시저 이름 ];
 
예제 1)
익명 PL/SQL 일 때(즉, 이름 없는 코드)-이름이 없는 pl/sql은 DB에 저장 안됨
accept p_deptno prompt '부서번호를 입력하세요 ~ ' declare v_deptno emp.deptno%type := &p_deptno; v_sumsal number(10,2) ; begin select sum(sal) into v_sumsal from emp where deptno = v_deptno ; dbms_output.put_line ( '토탈월급은 : ' || v_sumsal ); end; / ============================================================== DB의 소스코드가 변경되지 않고 단발성으로 사용할 수 있음
 
예제 2) 프로시져 일때
create or replace procedure pro1 ( p_deptno emp.deptno%type) is v_deptno emp.deptno%type := p_deptno; v_sumsal number(10,2) ; begin select sum(sal) into v_sumsal from emp where deptno = v_deptno ; dbms_output.put_line ( '토탈월급은 : ' || v_sumsal ); end pro1; / ===================================================== 맨 위의 두줄 즉, 입력매개변수만 입력해줘도 DB에 저장되어 언제든지 EXEC명령으로 호출해서 실행할 수 있음
 

문제풀며 익히기

 
Q1.
위에서 생성한 프로시져 목록을 SQL로 조회하시오
select * from dba_objects where object_type = 'PROCEDURE' --대문자 AND owner = 'C##SCOTT'; ---대문자
notion image
select object_name, status from dba_objects where object_type = 'PROCEDURE' AND owner = 'C##SCOTT';
notion image
alter procedure get_data compile; select object_name, status from dba_objects where object_type = 'PROCEDURE' AND owner = 'C##SCOTT'; ============================== 변경됨 INVALID로
alter table emp add grade number(10); alter procedure cursor_job compile; select object_name, status from dba_objects where object_type = 'PROCEDURE' AND owner = 'C##SCOTT'; 컬럼 추가 삭제가 일어나면, 그 컬럼 추가, 삭제한 테이블 관련 프로시져들이 전부 invalid 상태가 됨. 그래서 dba는 전부 valid될 수 있게 해줘야함
notion image
초급 dba 중급 dba 고급 dba 새벽 6시 출근 (아래와 같음) 자동화시킴 ------------------------------------------------------ select 'alter procedure ' || object_name || ' compile;' from dba_objects where owner = 'C##SCOTT' and object_type = 'PROCEDURE' AND status = 'INVALID'; -------------------------------------------------------- 출력된 결과 스크립트를 복사해서 실행하면 됨 alter procedure INSERT_EMP compile; alter procedure INSERT_DEPT compile; alter procedure DELETE_DEPTNO compile; alter procedure UPDATE_JOB compile; alter procedure UPDATE_DEPTNO compile; alter procedure PRO_JOB compile; alter procedure PRO_DEPTNO compile; Procedure CURSOR_JOB이(가) 변경되었습니다. Procedure INSERT_EMP이(가) 변경되었습니다. Procedure INSERT_DEPT이(가) 변경되었습니다. Procedure DELETE_DEPTNO이(가) 변경되었습니다. Procedure UPDATE_JOB이(가) 변경되었습니다. Procedure UPDATE_DEPTNO이(가) 변경되었습니다. Procedure PRO_JOB이(가) 변경되었습니다. Procedure PRO_DEPTNO이(가) 변경되었습니다.
notion image

047. 파라미터가 없는 형태의 프로시저

📖
파라미터가 없는 형태의 프로시저
  • 입력 파라미터가 없는 프로시저 문법
    • create [ or replace ] procedure [ 프로시저 이름 ] is | as [ 선언부 ] begin [ 실행부 ] exception [ 예외 처리부 ] end [ 프로시저 이름 ];
 
▣ 파라미터가 없는 프로시저 생성 예제
1. demo.sql 을 수행해서 emp 와 dept 테이블을 초기화 시킵니다. SQL> @demo.sql 2. emp 테이블에 grade 컬럼을 추가합니다. SQL> alter table emp add grade varchar2(10); SQL> select ename, sal, grade from emp; ENAME SAL GRADE -------------------- ---------- -------------------- KING 5000 BLAKE 2850 CLARK 2450 JONES 2975 MARTIN 1250 ALLEN 1600 TURNER 1500 JAMES 950 WARD 1250 FORD 3000 SMITH 800 ENAME SAL GRADE -------------------- ---------- -------------------- SCOTT 3000 ADAMS 1100 MILLER 1300 3. 프로시저 사용하지 않은 익명 PL/SQL 블럭 코드: emp 테이블에 grade 라는 컬럼에 다음의 조건에 해당되는 데이터로 값을 갱신하시오 ! 월급이 3000 이상이면 A 등급 월급이 2000 이상이면 B 등급 월급이 1000 이상이면 C 등급 나머지 월급은 D 등급으로 갱신 단, 월급이 없는 사원들은 등급은 null 로 갱신되어야합니다. declare cursor emp_cursor is select empno, ename, sal from emp for update; emp_record emp_cursor%rowtype; v_grade varchar2(5); begin open emp_cursor ; loop fetch emp_cursor into emp_record; exit when emp_cursor%notfound; if emp_record.sal >= 3000 then v_grade := 'A'; elsif emp_record.sal >= 2000 then v_grade := 'B'; elsif emp_record.sal >=1000 then v_grade := 'C' ; else v_grade := 'D'; end if; update emp set grade = v_grade where current of emp_cursor; end loop; commit; close emp_cursor; end; / SQL> select ename, sal, grade 2 from emp; ENAME SAL GRADE -------------------- ---------- -------------------- KING 5000 A BLAKE 2850 B CLARK 2450 B JONES 2975 B MARTIN 1250 C ALLEN 1600 C TURNER 1500 C JAMES 950 D WARD 1250 C FORD 3000 A SMITH 800 D ENAME SAL GRADE -------------------- ---------- -------------------- SCOTT 3000 A ADAMS 1100 C MILLER 1300 C 14 행이 선택되었습니다. ================================================= 프로시저를 생성한게 아니기 때문에 매번 @p.sql해야지만 등급을 갱신할 수 있음... 그래서 프로시저를 만들어 편하게 해보자! ================================================== 4. grade 컬럼을 null 값으로 변경합니다. SQL> update emp set grade = null; SQL> commit; 4. 프로시저 생성 코드: SQL> create or replace procedure pro2 is cursor emp_cursor is select empno, ename, sal from emp for update; emp_record emp_cursor%rowtype; v_grade varchar2(5); begin open emp_cursor ; loop fetch emp_cursor into emp_record; exit when emp_cursor%notfound; if emp_record.sal >= 3000 then v_grade := 'A'; elsif emp_record.sal >= 2000 then v_grade := 'B'; elsif emp_record.sal >=1000 then v_grade := 'C' ; else v_grade := 'D'; end if; update emp set grade = v_grade where current of emp_cursor; end loop; commit; close emp_cursor; end; / SQL> exec pro2; SQL> select ename, sal, grade from emp; ENAME SAL GRADE -------------------- ---------- -------------------- KING 5000 A BLAKE 2850 B CLARK 2450 B JONES 2975 B MARTIN 1250 C ALLEN 1600 C TURNER 1500 C JAMES 950 D WARD 1250 C FORD 3000 A SMITH 800 D ENAME SAL GRADE -------------------- ---------- -------------------- SCOTT 3000 A ADAMS 1100 C MILLER 1300 C
notion image
 

문제풀며 익히기

 
Q1.
다음의 예제를 먼저 수행해보고 문제를 풀어주세요
1. grade 컬럼을 null 값으로 변경합니다. SQL> update emp set grade = null; SQL> commit; 2. SMITH 의 월급을 null 값으로 변경합니다. SQL> update emp set sal = null where ename='SMITH'; SQL> commit; 3. 앞의 예제에서 생성한 프로시저를 수행합니다. SQL> exec pro2; PL/SQL 처리가 정상적으로 완료되었습니다. SQL> select ename, sal, grade from emp; ENAME SAL GRADE -------------------- ---------- -------------------- KING 5000 A BLAKE 2850 B CLARK 2450 B JONES 2975 B MARTIN 1250 C ALLEN 1600 C TURNER 1500 C JAMES 950 D WARD 1250 C FORD 3000 A SMITH D SCOTT 3000 A ADAMS 1100 C MILLER 1300 C ======================================================== SMITH와 같이 월급이 null값이면 grade가 갱신되면 안됨
 
Q2.
SMITH 처럼 월급이 NULL 값인 사원들의 GRADE 는 갱신되지 않도록 프로시저를 수정하세요.
1. grade 컬럼을 null 값으로 변경합니다. SQL> update emp set grade = null; SQL> commit; 2. SMITH 의 월급을 null 값으로 변경합니다. SQL> update emp set sal = null where ename='SMITH'; SQL> commit; SQL> create or replace procedure pro2 is cursor emp_cursor is select empno, ename, sal from emp WHERE SAL IS NOT NULL for update; emp_record emp_cursor%rowtype; v_grade varchar2(5); begin open emp_cursor ; loop fetch emp_cursor into emp_record; exit when emp_cursor%notfound; if emp_record.sal >= 3000 then v_grade := 'A'; elsif emp_record.sal >= 2000 then v_grade := 'B'; elsif emp_record.sal >=1000 then v_grade := 'C' ; else v_grade := 'D'; end if; update emp set grade = v_grade where current of emp_cursor; end loop; commit; close emp_cursor; end; / 3. 앞의 예제에서 생성한 프로시저를 수행합니다. SQL> exec pro2; PL/SQL 처리가 정상적으로 완료되었습니다. SQL> select ename, sal, grade from emp; ENAME SAL GRADE -------------------- ---------- -------------------- KING 5000 A BLAKE 2850 B CLARK 2450 B JONES 2975 B MARTIN 1250 C ALLEN 1600 C TURNER 1500 C JAMES 950 D WARD 1250 C FORD 3000 A SMITH SCOTT 3000 A ADAMS 1100 C MILLER 1300 C
 
 
 

048. 파라미터가 있는 형태의 프로시저 생성 방법

📖
  • 입력 파라미터가 있는 프로시저 문법
    • create [ or replace ] procedure [ 프로시져 이름 ] ( 입력 매개 변수 데이터 타입 ) is | as [ 선언부 ] begin [ 실행부 ] exception [ 예외 처리부 ] end [ 프로시져 이름 ];
 
예제 )
1. demo.sql 을 수행해서 emp 와 dept 테이블을 초기화 시킵니다. SQL> @demo.sql 2. 프로시저가 아닌 익명 PL/SQL 코드 set serveroutput on set verify off accept p_deptno prompt '부서번호를 입력하세요 ~ ' declare v_deptno emp.deptno%type := &p_deptno; v_sumsal number(10,2) ; begin select sum(sal) into v_sumsal from emp where deptno = v_deptno ; dbms_output.put_line ( '토탈월급은 : ' || v_sumsal ); end; / 3. 프로시저를 사용했을때의 코드 create or replace procedure pro1 ( p_deptno emp.deptno%type ) is v_deptno emp.deptno%type := p_deptno; v_sumsal number(10,2) ; begin select sum(sal) into v_sumsal from emp where deptno = v_deptno ; dbms_output.put_line ( '토탈월급은 : ' || v_sumsal ); end; / 4. 프로시저 수행하는 방법 SQL> set serveroutput on SQL> exec pro1(10); 토탈월급은 : 8750 SQL> exec pro1(20); 토탈월급은 : 10875
notion image

문제풀며 익히기

 
Q1.
부서번호를 입력하게하고 부서번호의 토탈월급이 10000 이상이면 high_salary 테이블에 부서번호와 토탈월급이 입력되게하고 10000 보다 작으면 normal_salary 테이블에 입력되게하는 아래의 익명 PL/SQL 을 프로시져로 변경하시오 !
만들기 전 환경 구축
drop table high_sal; drop table normal_sal; create table high_sal ( deptno number(10), sumsal number(10,2) ); create table normal_sal ( deptno number(10), sumsal number(10,2) );
1. 익명 PL/SQL 블럭 set serveroutput on accept p_deptno prompt '부서번호를 입력하세요 ~ ' declare v_deptno emp.deptno%type := &p_deptno; v_sumsal number(10,2) ; --스칼라변수 begin select sum(sal) into v_sumsal from emp where deptno = v_deptno ; if v_sumsal >= 10000 then insert into high_sal values( v_deptno, v_sumsal ); else insert into normal_sal values( v_deptno, v_sumsal ); end if; commit; end; / 2. 프로시저 실행 exec pro1(10); exec pro1(20); select * from high_sal; select * from normal_sal;
<프로시저 생성> CREATE OR REPLACE PROCEDURE PRO1 (P_DEPTNO EMP.DEPTNO%TYPE) IS v_deptno emp.deptno%type := p_deptno; -- &빼 v_sumsal number(10,2) ; begin select sum(sal) into v_sumsal from emp where deptno = v_deptno ; if v_sumsal >= 10000 then insert into high_sal values( v_deptno, v_sumsal ); else insert into normal_sal values( v_deptno, v_sumsal ); end if; commit; end; / < 프로시저 실행 > exec pro1(10); exec pro1(20); select * from high_sal; DEPTNO SUMSAL ---------- ---------- 20 10875 select * from normal_sal; DEPTNO SUMSAL ---------- ---------- 10 8750
notion image
 
 
 

10장. 함수(function)

049. 함수(function)의 정의와 장점

📖
사용자 정의 함수(function)란?
오라클에 내장되어 있는 함수가 아니라 PL/SQL 개발자가 필요에 의해서 직접 작성한 함수를 말합니다.
프로그래밍을 하다보면 특정 기능을 만들어서 사용하고 싶은 경우가 있습니다. 그럴때 유용한 PL/SQL 객체가 사용자 정의 함수입니다.
 
단, 너무 과용할 경우 튜닝의 대상이 될 수 있으므로 주의
EX) 사용자 정의 함수 안에 사용자 정의 함수 안에 사용자 정의 함수가 있는,,
 
원래는 사용자정의 함수를 사용하여 튜닝을 자주
 
  • 사용자 정의 함수(function)의 장점?
      1. 간결하고 이해하기 쉬운 SQL을 만들 수 있다.
      1. 반복 사용되는 기능을 함수로 정의하면 동일 코드를 중복 작성하지 않아도 된다.
      1. 잘 설계하여 만든 함수는 그 기능이 필요한 여러 가지 다른 SQL에서 재사용하기 쉽다.
 
▣ 장점 설명 예제
1. 함수를 사용하지 않았을 때: SELECT ename, sal, case when sal >= 3000 then '고소득' when sal >= 2000 then '중간소득' else '저소득' end "소득" from emp; 2. 함수를 사용했을 때 : SELECT ename, sal, fun179(ename) 소득 FROM emp; /* 결과 ENAME SAL 소득 -------------------- ---------- ------------------- KING 5000 고소득 BLAKE 2850 중간소득 CLARK 2450 중간소득 JONES 2975 중간소득 MARTIN 1250 저소득 ALLEN 1600 저소득 TURNER 1500 저소득 JAMES 950 저소득 WARD 1250 저소득 FORD 3000 고소득 SMITH 800 저소득 SCOTT 3000 고소득 ADAMS 1100 저소득 MILLER 1300 저소득 14 개의 행이 선택되었습니다. */
- -소득이 '고소득' 인 사원의 이름과 월급을 출력하시오 !
1. 함수를 사용하지 않았을 때 : select ename, sal, 소득 from ( SELECT ename, sal, case when sal >= 3000 then '고소득' when sal >= 2000 then '중간소득' else '저소득' end "소득" from emp ) where 소득='고소득'; /* 결과 ENAME SAL 소득 ---------- ---------- --------- KING 5000 고소득 FORD 3000 고소득 SCOTT 6000 고소득 */ 2. 함수를 사용했을 때 : SELECT ename, sal, fun179(ename) 소득 FROM emp WHERE fun179(ename) = '고소득'; /* 결과 ENAME SAL 소득 ---------- ---------- --------- KING 5000 고소득 FORD 3000 고소득 SCOTT 6000 고소득 */

050. 함수(function) 사용예제1 —select

📖
함수(function) 사용예제1
  • 사용자 정의 함수 문법
    • create or replace function 함수이름 (입력변수 데이터 유형) is | as return 출력 데이터 유형 --프로시져와 차이점 -- 사용자 정의 함수가 출력될 데이터 유형을 RETURN begin 실행문 return 출력변수; -- 출력변수를 RETURN으로 출력 -- dbms_output.put_line으로 출력하는 것이 아님 end; / ================================= return 출력 데이터 유형 return 출력변수; 이 두가지 반드시 작성해줘야함
 
▣ 사용자 정의 함수 생성 예제1
이름을 입력했을때, 해당 사원의 월급의 순위가 출력되게하는 함수를 생성하시오
/* SQL> select ename, sal, fun178(ename) from emp; KING 5000 1 SCOTT 3000 2 */
CREATE OR REPLACE FUNCTION fun178 ( p_ename emp.ename%TYPE ) RETURN NUMBER -- 사용자 정의 함수가 출력될 데이터 유형을 RETURN AS v_rank NUMBER(5); BEGIN SELECT 순위 INTO v_rank FROM ( SELECT ename, DENSE_RANK() OVER ( ORDER BY sal DESC ) 순위 FROM emp ) WHERE ename = UPPER(p_ename); RETURN v_rank; -- 출력변수를 RETURN으로 출력 END; / /* 결과확인 */ SELECT ename, sal, fun178(ename) 순위 FROM emp ORDER BY 순위; /* 결과 ENAME SAL 순위 ---------- ---------- ---------- SCOTT 6000 1 KING 5000 2 FORD 3000 3 JONES 2975 4 BLAKE 2850 5 CLARK 2450 6 ALLEN 1600 7 TURNER 1500 8 MILLER 1300 9 WARD 1250 10 MARTIN 1250 10 ADAMS 1100 11 JAMES 950 12 SMITH 800 13 14 개의 행이 선택되었습니다. */ /* 기타 출력해보기 */ SQL> select fun178('SCOTT') 2 from dual; FUN178('SCOTT') --------------- 2 SQL> select ename, sal, fun178(ename) 2 from emp 3 where job = 'SALESMAN'; ENAME SAL FUN178(ENAME) -------------------- ---------- ------------- MARTIN 1250 9 ALLEN 1600 6 TURNER 1500 7 WARD 1250 9 => 그냥 RANK()함수로는 출력된 것 중 순위만 가능한데 => 사용자 정의 함수를 사용하여 전체 중 직업이 SALESMAN인 사원의 순위를 그대로 가져올 수 있
notion image
 
▣ 사용자 정의 함수 생성 예제2
- 위의 함수코드를 수정해서 아래와 같이 결과가 나오게 하시오 ! -- 즉, 월급의 순위가 1 ~ 3등까지는 고소득, -- 나머지 순위는 저소득 이란 글씨가 출력되게 하는 함수를 생성하시오 !
/* SELECT ename, sal, fun179(ename) 소득 FROM emp; KING 5000 고소득 SCOTT 3000 고소득 SMITH 1200 저소득 ... */ CREATE OR REPLACE FUNCTION fun179 ( p_ename emp.ename%TYPE ) RETURN VARCHAR2 --사용자 정의함수 fun179의 datatype VARCHAR2 AS v_rank NUMBER(5); v_msg VARCHAR2(20); BEGIN SELECT 순위 INTO v_rank FROM ( SELECT ename, DENSE_RANK() OVER ( ORDER BY sal DESC ) 순위 FROM emp ) WHERE ename = UPPER(p_ename); IF v_rank < 4 THEN v_msg := '고소득'; ELSE v_msg := '저소득'; END IF; RETURN v_msg; END; / /* 결과확인 */ SELECT ename, sal, fun179(ename) 소득 FROM emp; /* 결과 ENAME SAL 소득 ---------- ---------- ------------ KING 5000 고소득 BLAKE 2850 저소득 CLARK 2450 저소득 JONES 2975 저소득 MARTIN 1250 저소득 ALLEN 1600 저소득 TURNER 1500 저소득 JAMES 950 저소득 WARD 1250 저소득 FORD 3000 고소득 SMITH 800 저소득 SCOTT 6000 고소득 ADAMS 1100 저소득 MILLER 1300 저소득 14 개의 행이 선택되었습니다. */
notion image
notion image

문제풀며 익히기

 
Q1.
위에서 생성한 사용자 정의 함수 FUN178을 이용해서 사원테이블에서 월급의 순위가 12위인 사원의 이름과 월급을 출력하시오.
select ename, sal from emp where fun178(ename) = 12; /* 결과 ENAME SAL -------------------- ---------- SMITH 800 */
notion image
 
Q2.
위의 결과를 사용자 정의 함수를 이용하지 말고 출력하시오.
select ename, sal from ( select ename, sal, dense_rank() over (order by sal desc) 순위 from emp ) where 순위 = 12;
notion image
 
Q3.
위에서 만든 함수(FUN129)를 사용해서 고소득인 사원의 이름과 월급을 출력하시오
select ename, sal from emp where fun179(ename) = '고소득'; /* 결과 ENAME SAL -------------------- ---------- KING 5000 JONES 2975 FORD 3000 SCOTT 3000 */
notion image
 
Q4.
소득이 고소득자인 사원들을 출력하는데, 월급이 3000 이상인 사원들만 출력하고, 월급이 높은 사원부터 출력되게하시오.
select ename, sal from emp where fun179(ename) = '고소득' and sal >= 3000 order by sal desc; /* 결과 ENAME SAL -------------------- ---------- KING 5000 SCOTT 3000 FORD 3000 */
notion image
 
Q5.
우리반 테이블에서 나이를 입력하면, 아래와 같이 출력되는 함수를 생성하시오.
/* select ename, func3(age) from emp18; 최재건 20대 전수진 30대 서형진 30대 오운학 20대 */
create or replace function func3 ( p_age emp18.age%type ) return varchar2 as v_agedae varchar2(10); begin select substr(p_age, 1, 1) || '0대' into v_agedae from dual; --입력 받는 age를 기반으로 잘라내는 것이므로 dual테이블 return v_agedae; end; / select ename, func3(age) from emp18;
notion image

051. 함수(function) 사용예제2 —update

📖
사용자 정의 함수를 이용하여 테이블에 UPDATE 하기
  • UPDATE 방법
      1. 상호관련 서브쿼리 update 문 사용
      1. merge문을 사용하여 update
      1. 사용자 정의 함수를 사용하여 update
       
  • 사용자 정의 함수를 이용하여 테이블에 UPDATE 하기 생성 예제)
    • -- 예제1. 먼저 demo.sql 을 수행합니다. SQL> @demo.sql -- 예제2. 부서번호, 부서번호별 토탈월급을 출력하시오 ! SQL> select deptno, sum(sal) from emp group by deptno; /*결과 DEPTNO SUM(SAL) ---------- ---------- 10 8750 30 9400 20 10875 */ -- 예제3. 이름과 월급과 부서번호, 자기가 속한 부서번호의 토탈월급이 출력되게하시오 /* 출력 SQL> SELECT ename, sal, deptno, fun51(deptno) FROM emp; SCOTT 3000 10 8750 SMITH 1200 20 10875 ... */ 답: CREATE OR REPLACE FUNCTION fun51 ( p_deptno emp.deptno%TYPE ) #입력 매개변수 RETURN NUMBER AS v_sum NUMBER(10); #스칼라변수 선언 BEGIN SELECT SUM(sal) INTO v_sum FROM emp WHERE deptno = p_deptno; RETURN v_sum; END; / /* 결과 확인 */ SQL> SELECT ename, sal, deptno, fun51(deptno) FROM emp; /* 결과 ENAME SAL DEPTNO FUN51(DEPTNO) ---------- ---------- ---------- -------------- KING 5000 10 8750 BLAKE 2850 30 9400 CLARK 2450 10 8750 JONES 2975 20 13875 MARTIN 1250 30 9400 ALLEN 1600 30 9400 TURNER 1500 30 9400 JAMES 950 30 9400 WARD 1250 30 9400 FORD 3000 20 13875 SMITH 800 20 13875 SCOTT 6000 20 13875 ADAMS 1100 20 13875 MILLER 1300 10 8750 14 개의 행이 선택되었습니다. */ -- 예제4. 부서테이블에 sumsal 컬럼을 숫자형으로 추가하시오 ! SQL> ALTER TABLE dept ADD sumsal NUMBER(10); -- 예제5. 방금 추가한 sumsal 컬럼에 해당 부서번호의 토탈월급으로 값을 갱신하시오 ! SQL> UPDATE dept SET sumsal = fun51(deptno); SQL> COMMIT; SQL> SELECT * FROM dept; /* 결과 DEPTNO DNAME LOC SUMSAL CNT ---------- -------------- ------------- ---------- ---------- 10 ACCOUNTING NEW YORK 8750 3 20 RESEARCH DALLAS 13875 5 30 SALES CHICAGO 9400 6 40 OPERATIONS BOSTON 0 */
      notion image
       

문제풀며 익히기

 
Q1.
부서테이블에 cnt 라는 컬럼을 아래와 같이 추가하시오.
SQL> ALTER TABLE dept ADD cnt NUMBER(10);
 
Q2.
CNT 컬럼의 data 를 해당 부서번호의 인원수로 갱신하는 함수를 생성하시오
-- 업데이트 수행 SQL> UPDATE dept SET cnt = pro_cnt(deptno); SQL> COMMIT; -- 결과 확인 SQL> SELECT * FROM dept; /* 결과 DEPTNO DNAME LOC SUMSAL CNT ---------- -------------- ------------- ---------- ---------- 10 ACCOUNTING NEW YORK 8750 3 20 RESEARCH DALLAS 13875 5 30 SALES CHICAGO 9400 6 40 OPERATIONS BOSTON 0 */
A. CREATE OR REPLACE FUNCTION pro_cnt ( p_deptno emp.deptno%TYPE ) RETURN NUMBER AS v_cnt NUMBER(10); BEGIN SELECT count(*) INTO v_cnt FROM emp WHERE deptno = p_deptno; RETURN v_cnt; END; /
notion image
 
 
 
 
Share article

Hye-Min Son