• 다소 쌩뚱맞긴 합니다만... ^^;; 
    통계작업에 유용하게 사용할 수 있는 달력테이블을 만들어서 사용하기도 합니다. 
    create table 달력 (년 varchar2(4), 월 varchar2(2), 일 varchar2(2), 년월일 varchar2(8)); 
    요로코롬 말이지요. 
    말씀하시는바와 같이 화면에 임의의 년에대한 모든 월데이터를 보인다던가.. 할때 유용하게 사용할 수 있지요.
    통계화면이 한두개가 아니다보니.. 요로코롬 미리 테이블을 구성한 후 join으로 이용하는것도 꽤 괜찮은 방법입니다. 
    DBA님이나 사수분께 의논해보심은 어떠시련지요. ^^;; 
    아니면.. 제 오래된 기억에 mssql 2000의 경우엔 템프테이블을 만들 수 있는 방법이 있었는데요.. 
    요 템프테이블을 달력테이블처럼 구성하여 사용하셔도 될듯 합니다. 
    그럼 ^^;;
  • dazzilove

from
http://okjsp.pe.kr/seq/117001

통계 시 실제 데이터가 없는 일/월/연을 0건으로 표시하기 위해서 위와 같은 처리가 필요.
아님

  • select 1 as month, 0 as cnt 
    from dual 
    union all 
    select 2 as month, 0 as cnt 
    from dual 
    union all 
    select 3 as month, 0 as cnt 
    from dual 
    union all 
    select 4 as month, 0 as cnt 
    from dual 
    union all 
    select 5 as month, 0 as cnt 
    from dual 
    .... 

    으로 가라로 뷰를 하나 만든다음에 outer join 하면 되지 않을까요?
  • mgcos

이건 넘 쿼리가 길어지는 듯....

/*OUTER JOIN 테스트용.
임시로 월간 캘린더 테이블을 만들어서 실제 데이타와 조인. 그런 다음 갯수를 세면 데이터가 없는 월의 것도 카운트 가능해진다
임시 테이블을 만들어서 데이타를 뽑아내긴 하였으나, 검색기간이 동적으로 변하므로 그것에 대한 처리는 어떻게 하는가?
실제 물리적인 테이블에 모든 데이타를 넣어두어 그것을 가지고 해야하지 않나?
*/
SELECT
    COUNT(EVT_CODE), MONTHLY
FROM
(
    SELECT
        R5E.EVT_CODE,
        /*CASE //이것은 필요없을 듯. MSSQL서 COUNT시 NULL은 자동으로 0으로 인식됨
            WHEN R5E.EVT_CODE IS NULL THEN 0
            WHEN R5E.EVT_CODE IS NOT NULL THEN 1
        END AS CODE_COUNT,*/
        CASE
            WHEN R5E.MON IS NULL THEN TM2.MON
            WHEN R5E.MON IS NOT NULL THEN R5E.MON
        END AS MONTHLY
    FROM
    (
        (
            SELECT EVT_CODE, CONVERT(CHAR(7), EVT_DATE, 121) AS MON FROM R5EVENTS
            WHERE CONVERT(CHAR(7), EVT_DATE, 121) BETWEEN '2008-12' AND '2009-01'
        ) R5E
        FULL OUTER JOIN
        (
            SELECT MON, CNT FROM
            (
                SELECT '2009-01' AS MON, 0 AS CNT FROM DUAL
                UNION ALL
                SELECT '2009-02' AS MON, 0 AS CNT FROM DUAL
                UNION ALL
                SELECT '2009-03' AS MON, 0 AS CNT FROM DUAL
                UNION ALL
                SELECT '2009-04' AS MON, 0 AS CNT FROM DUAL
                UNION ALL
                SELECT '2009-05' AS MON, 0 AS CNT FROM DUAL
                UNION ALL
                SELECT '2009-06' AS MON, 0 AS CNT FROM DUAL
                UNION ALL
                SELECT '2009-07' AS MON, 0 AS CNT FROM DUAL
                UNION ALL
                SELECT '2009-08' AS MON, 0 AS CNT FROM DUAL
                UNION ALL
                SELECT '2009-09' AS MON, 0 AS CNT FROM DUAL
                UNION ALL
                SELECT '2009-10' AS MON, 0 AS CNT FROM DUAL
                UNION ALL
                SELECT '2009-11' AS MON, 0 AS CNT FROM DUAL
                UNION ALL
                SELECT '2009-12' AS MON, 0 AS CNT FROM DUAL
            ) TM
        ) TM2
        ON TM2.MON = R5E.MON
    )
) A
GROUP BY
    MONTHLY

길어졌다...

[펌] MySQL 사용하기

프로그래밍/DB 2008. 11. 25. 17:46 Posted by galad

MySQL에서 root 암호 설정

mysqladmin을 이용하여 root 암호 설정

MySQL을 처음 설치하면 root 암호가 설정되어 있지 않기 때문에 반드시 설치 후 바로 root 암호를 먼저 설정해주세요.%%% root 암호를 설정하기 위해서는

 $ mysqladmin -u root -p password new-password

이러한 명령을 실행하면 됩니다. 실행하면 패스워드를 입력하라고 하는데 처음 root의 패스워드는 없으므로 그냥 엔터를 치시면 root 패스워드가 변경됩니다.

update문을 이용하여 root 암호 설정

 $ mysql -u root mysql

 mysql> update user set password = password('new-password') where user = 'root';
 mysql> flush privileges;

set password를 이용하여 root 암호 설정

 mysql> set password for root = password('new-password');

마지막으로 패스워드가 재대로 변경되었는지 테스트를 합니다.

 $ mysql -u root -p

MySQL에서 Database 보기와 생성

데이터베이스 보기

 myslq> show databases;

데이터베이스 생성

 mysql> create database DB명;

MySQL에서 새로운 사용자 추가

MySQL에서 사용할 Database를 만든 후에 사용자를 추가하려면, 일단 root로 접속한 후

 $ mysql (?h localhost) ?u root ?p
 Enter password: *******

 mysql> use mysql
 mysql> INSERT INTO user VALUES('%', '사용자', PASSWORD('비밀번호'),
     -> 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y');

이렇게 해서 사용자를 하나 만들었습니다. 맨 처음 ‘%’ 가 붙은 것은 다른 어느 호스트에서도 접속이 가능하게 하기 위해서 입니다. 간단하게 추가 하는 방법이 있습니다.


'GRANT' 명령을 이용하면 쉽게 사용자 추가 및 권한 설정이 가능합니다. (MySQL Manual 4.3.5)

  • 일반 사용자 추가
 mysql> grant all privileges on dbuser.* to dbuser@localhost identified by 'password' 
with grant option;

  • 특정 이름의 데이터베이스에 대한 모든 권한을 가지는 사용자 추가
 mysql> grant all privileges on `dbuser_%`.* to dbuser@localhost identified by 'password' 
with grant option;

'dbuser_'으로 시작되는 데이터베이스에 대한 모든 권한을 가지는 'dbuser' 사용자 계정 추가%%% 이런식으로 계정을 만들면 새로운 사용자에 대한 개별적인 데이터베이스 권한 설정을 생략할 수 있습니다. -- 이현진


사용자에게 Database 사용 권한을 주기

 mysql> INSERT INTO db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, 
Create_priv, Drop_priv)
     -> VALUES('%', 'DB명', '사용자', 'Y', 'Y', 'Y', 'Y','Y','Y');

모든 권한을 준다면 아래와 같은 방법을 하는것이 더욱 편리힙니다.

 mysql> GRANT ALL on DB명.* TO id@'localhost'  ;
 mysql> GRANT ALL on DB명.* TO id;
 mysql> GRANT ALL on DB명.* TO id@'xxx.xxx.xxx.%';

첫번째 localhost?로컬 호스트 (DB가 자기컴퓨터에 깔려 있는경우..에만 접속을 허용하겠다는 뜻입니다.

두번째 문장은 로컬 호스트를 제외하고는 모든 ip에서 접근을 시키겠다는 내용입니다.

세번째는 c클래스만 허용하겠다는 의미 입니다. xxx.xxx.%.% 면 B클래스 xxx.%.%.%면 A클래스로 제한을 할수 있습니다.

그리고 변경된 내용을 메모리에 반영하기 위해 다음 명령을 실행합니다.

 mysql> FLUSH PRIVILEGES;

이렇게 MySQL을 설정해 놓으면 사용자가 다른 호스트에서도 접속할 수 있고 테이블 추가 삭제 등도 할 수 있습니다.


이렇게 수동으로도 가능 하지만 요즘은 툴을 많이 사용합니다. 툴은 DB에 대한 기본적인 지식만 있으면 무리가 없이 사용 되는 녀석이죠 ^^%%% 저같이 쿼리에 약한 사람들에게는 정말 유리 합니다. 아래 페이지에 가셔서 다운로드를 받으세요 :) 한글 사용에 약간의 제약이 있습니다.%%% 다른 여러가지 DB관리 툴이 있지만 상당히 고가의 툴이랍니다 :) 암흑의 루트에서 돌고 있는 녀석들이 종종 있습니다.

http://www.mysql.com/downloads/mysqlcc.html


출처: http://www.gpgstudy.com/gpgiki/MySQL%EC%97%90%EC%84%9C%20%EC%82%AC%EC%9A%A9%EC%9E%90%EC%99%80%20%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%20%EB%A7%8C%EB%93%A4%EA%B8%B0

'프로그래밍 > DB' 카테고리의 다른 글

ORLCLE->MSSQL  (0) 2009.01.14
[펌/DB관련팁] 일간/월간/연간 통계 시  (0) 2009.01.09
[펌] mysql root 암호 분실 시  (1) 2008.11.25
[펌]PLSQL 초보  (0) 2008.01.10
PL SQL문제  (0) 2008.01.10

[펌] mysql root 암호 분실 시

프로그래밍/DB 2008. 11. 25. 17:19 Posted by galad


1>암호 초기화
# killall mysqld (데몬을 죽입니다)

# cd /usr/local/mysql (mysql이 깔린 곳으로 갑니다)

# ./bin/safe_mysqld --skip-grant &

여기까지가 초기화


2>새 암호 넣기
# ./bin/mysql (sql 실행)

mysql>use mysql

mysql>update user set password=password('새암호') where user='root';
(root 암호 변경합니다. '새암호' 자리에 새암호를 넣으세요
예>update user set password=password('1234') where user='root';

mysql> FLUSH PRIVILEGES
mysql> exit

# ./bin/mysql -uroot -p1234 으로 들어와지면 성공입니다

출처: http://kurapa.com

'프로그래밍 > DB' 카테고리의 다른 글

[펌/DB관련팁] 일간/월간/연간 통계 시  (0) 2009.01.09
[펌] MySQL 사용하기  (0) 2008.11.25
[펌]PLSQL 초보  (0) 2008.01.10
PL SQL문제  (0) 2008.01.10
DBMS_LOCK 사용 시 에러  (0) 2008.01.10

[펌]PLSQL 초보

프로그래밍/DB 2008. 1. 10. 15:57 Posted by galad

- PLSQL은 SQL에 프로그래맹 언어 설계 기능을 추가한 것이다.

- SQL의 데이터 조작문 및 질의문은 프로시저 단위코드에 포함된다.


< PL/SQL >

- Anonymou block : 평상시 script로 저장했다가 사용

- Function, Procedure

- Package : Function과 Procedure의 모음

- Trigger : 이벤트에 반응하는 로직


< PL/SQL의 블록구조 >

  -- DECLARE

          변수선언, 커서, 사용자가 정의한 예외사항

  -- BEGIN

          SQL문, PL/SQL문

  -- EXCEPTION

          오류가 발생할 때의 수행작업

  -- END;


※첨부파일 실행시 스키마를 고려하여 DB에 접속해야 한다.


< PL/SQL 공통 >


-- 생성한 함수/프로시져 조회
SQL> SELECT DISTINCT NAME FROM USER_SOURCE;
--생성 후에는 꼭 에러 여부를 확인한다.
SQL> SELECT * FROM USER_ERRORS; //마지막에 실행한 명령의 에러를 가지고 있다.


< PLSQL - EditPlus환경설정 >

※첨부파일

plsql2.zip

plsql2_구문강조_사용설명서.txt


======================================================================


< PLSQL - variable >

※첨부파일

plsql_variable01.sql

plsql_variable02.sql

plsql_variable03.sql


< PLSQL - cursor >

※첨부파일

plsql_cursor01.sql

plsql_cursor02.sql

plsql_cursor03.sql

plsql_cursor04.sql

plsql_cursor05.sql


< PLSQL - exception >

※첨부파일

plsql_exception01.sql

plsql_exception02.sql


< PLSQL - 종합문제 >

※첨부파일

plsql_exe01.sql : 종합문제 NO1 :사원별 급여현황에 따른 '*',급여 출력

plsql_exe02.sql : 종합문제 NO3 :급여 누적값 출력(1)

plsql_exe03.sql : 종합문제 NO3 :급여 누적값 출력(2)

plsql_exe04.sql : 종합문제 NO2 : 년도별 입사한 사원수 출력


 

======================================================================

** PROCEDURE : 어떠한 작업처리가 목적이다.

                         VALUE를 반환 할수도 안할 수도 있다.

                         SQL문에서 사용불가,

                         여러개의 인자를 받을수있다.


** FUNCTION : 반드시 한개의 VALUE를 반환한다.

                      SQL문에서 사용가능(사용자 정의 함수)


< PLSQL - PROCEDURE(프로시져) >

※첨부파일

procedure01_raise_salary.sql

procedure02_updownsalary.sql

procedure03_query_emp.sql

procedure03_query_emp_plsql.sql


< PLSQL - FUNCTION(함수) >

※첨부파일

function01_get_sal.sql : 사원번호를 받아 급여를 출력하는 함수

function02_annsal.sql : 연봉을 구하는 함수

function03_find_dname.sql : 특정 사번을 그 사원의 부서명을 출력하는 함수


< PLSQL - TRIGGER >

※첨부파일

trigger01_secure_emp.sql

trigger02_copy_insert.sql

trigger03_update_dept.sql

trigger04_delete_dept.sql

trigger05_delete_emp.sql

trigger06_set_null_emp.sql

trigger07_update_cascade_emp.sql

trigger08_audit_emp_values.sql

'프로그래밍 > DB' 카테고리의 다른 글

[펌] MySQL 사용하기  (0) 2008.11.25
[펌] mysql root 암호 분실 시  (1) 2008.11.25
PL SQL문제  (0) 2008.01.10
DBMS_LOCK 사용 시 에러  (0) 2008.01.10
[펌]SQL*plus FAQ 및 TIPS  (0) 2008.01.09

PL SQL문제

프로그래밍/DB 2008. 1. 10. 15:43 Posted by galad

금일 PL SQL문제중 이런게 나왔었다. 잘 기억은 안난다만... 인사관리를 짜라고 했는데...

등록되어 있는 사원들의 평균 급여를 내어 출력하고 평균 급여가 메니져보다 높은 사람의 급여를

15% 삭감하고, 삭감할 사람의 나이가 평균 나이보다 높을 사람의 경우에는 그대로 두어라...

물론 실제 문제랑은 많이 차이가 있음. 책에도 이런문제는 많이 있을 듯...


주요 조건은 프로시져로 짜야하며 패키지와를 시켜야 하고 폴더는 어디에 주석은 어떻게 달아야 하고

제한시간 1시간. 저건 문제중 한개이고... 총 문제수 5문제......-_-;;;;

'프로그래밍 > DB' 카테고리의 다른 글

[펌] mysql root 암호 분실 시  (1) 2008.11.25
[펌]PLSQL 초보  (0) 2008.01.10
DBMS_LOCK 사용 시 에러  (0) 2008.01.10
[펌]SQL*plus FAQ 및 TIPS  (0) 2008.01.09
[펌]DECODE 이용사례 (4) - BETWEEN OPERATOR  (0) 2008.01.09

DBMS_LOCK 사용 시 에러

프로그래밍/DB 2008. 1. 10. 10:20 Posted by galad
DBMS_LOCK.SLEEP()을 사용하려고 하는데, isql모드(sqlPlus)에서는 무리없이 수행되나, PL/SQL에서 사용하면 하기와 같은 에러가 발생합니다.
PLS-00201: identifier 'SYS.DBMS_LOCK' must be declared



system 계정에서 다음과 같이 권한을 주세요
> sqlplus /nolog

// sys유저에 sysdba권한으로 접속을 합니다
SQL*Plus: Release 9.2.0.5.0 - Production on Mon Feb 26 13:56:02 2007
Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.
SQL> conn sys/manager@oracle as sysdba

SQL> grant execute on dbms_lock to scott;

'프로그래밍 > DB' 카테고리의 다른 글

[펌]PLSQL 초보  (0) 2008.01.10
PL SQL문제  (0) 2008.01.10
[펌]SQL*plus FAQ 및 TIPS  (0) 2008.01.09
[펌]DECODE 이용사례 (4) - BETWEEN OPERATOR  (0) 2008.01.09
[펌]DECODE를 이용하여 원치 않는 자료를 없애는 방법  (0) 2008.01.09

[펌]SQL*plus FAQ 및 TIPS

프로그래밍/DB 2008. 1. 9. 17:45 Posted by galad
(V7.X ~ V8.X) : SQL*plus FAQ 및 TIPS
=============================================

Q) Row wrapping없이 flat ASCII file을 만드는 방법
Q) 두 개의 Date type column 사이의 평균 시간을 구하는 방법
Q) Ampersand나 특수 문자를 갖는 데이타를 insert하는 방법
Q) SQL 내의 문자열 주변에 인용부호를 생성하거나 QUOTES를 생성하는 방법
Q) `LIKE` 연산자를 사용하여 case insensitive search를 할 수 있는 방법
Q) Column delimited flat file을 생성하는 방법
Q) Product_User_Profile에 관하여

1. Q) Row wrapping없이 flat ASCII file을 만드는 방법은 무엇입니까?

    A) `SPOOL` command와 `SET LINESIZE` 와 `SET BUFFER` command를 사용하시기 바랍니다.
sql query를 화일에 저장하기 위해서는 spool command를 사용하시면 됩니다.
화일 확장자를 기술하지 않으면, O/S에 따라서 다르지만, 대부분의 O/S에서는 .lst나 .lis를 사용합니다.
    SQL*plus에서 flat file을 생성하기 위해서는 먼저 다음과 같은 SET command를 기술하여야 합니다.
    SET NEWPAGE 0
    SET SPACE 0
    SET LINESIZE 80
    SET PAGESIZE 0
    SET ECHO OFF
    SET FEEDBACK OFF
    SET HEADING OFF

위의 command를 기술한 후에, spool command를 사용하시면 됩니다.


2. Q) 두 개의 Date type column 사이의 평균 시간을 구하는 방법은 무엇입니까?

    A) 다음과 같은 두 개의 row를 가지는 date field가 있다고 가정합니다.

    01-jan-96 01:10
    01-jan-96 01:20

sql> select avg(to_number(to_char(a, `MMDDYYHHMI`))) from x;

AVG(TO_NUMBER(TO_CHAR(A,`MMDDYYHHMI`)))
---------------------------------------
                               101960115

즉, 1:10과 1:20 사이의 평균 시간은 1:15입니다.


3. Q) Ampersand나 특수 문자를 갖는 데이타를 insert하는 방법은 무엇입니까?

    A) 해결 방법은 세 가지가 있습니다.

1> SQL*plus에서 SET DEFINE OFF나 SET SCAN OFF를 실행하여 substitution
variable(&)을 turn off시키는 방법입니다.

(Example)
    sql> SET DEFINE OFF
    sql> INSERT INTO table_name VALUES(`AT&T`);
    /
    1 row created

2> SET DEFINE ON 상태로 유지시키면서 substitution variable을 다른
non-alphanumeric 문자나 non-white space 문자(*, % 등등)로 대체시킨다.
 
(Example)
    sql> SET DEFINE %
    sql> INSERT INTO table_name VALUES(`AT&T`);
    /
    1 row created

3> SET ESCAPE ON 상태에서(DEFINE은 &로, SCAN은 ON상태로 유지) 특수 문자 앞에
escape 문자인 backslash(``)를 선행하도록 합니다.

(Example)
    sql> SET ESCAPE ON
    sql> SHOW ESCAPE
         ESCAPE "" (hex 5c)
    sql> INSERT INTO table_name VALUES (`select * from emp where ename=&1`);
    1 row created.
    sql> select * from table_name;

    COL1
    ---------------------------------------------------------
    SELECT * FROM EMP WHERE ENAME=&1


4. Q) SQL 내의 문자열 주변에 인용부호를 생성하거나 QUOTES를 둘 수 있습니까?

    A) 해결 방법은 다음과 같습니다.

1> Single quotation mark를 생성하기 위해서는 4 single quote(````)를 사용하
시기 바랍니다.

(Example)
    sql> select ```` from dual;
위의 문장을 실행 시 return되는 결과는 다음과 같습니다.
`
-
`

2> 문자열 주변에 single quote를 두기 위해서는 3 single quote 안에(```) 문자
열을 포함시키면 됩니다.

(Example)
    sql> select ```character string in quotes``` result
         from dual;

위의 문장을 실행 시 return되는 결과는 다음과 같습니다.

RESULT
-----------------------------------------------
`CHARACTER STRING IN QUOTES`

3> 참고로, Literal single quote를 생성하기 위해 연속적인 single quote mark
를 사용하는 방법은 다음과 같습니다.

(Example)
    srw.do_sql(`select decode(dname, ``NONE``, NULL, ``A``)
                from dept
                where deptno = 10`);

위의 문장을 실행 시 return되는 결과는 다음과 같습니다.

    select decode(dname, `NONE`, NULL, `A`)
    from dept
    where deptno = 10

(Example)
    DECLARE
       a varchar2(200);
       q char(1) := ````;
    BEGIN
       a := ```this is a ` || q || `quoted`` string` || q;
    END;

위의 문장을 수행 결과 스트링 a에는 `this is a `quoted` string` 이 저장됩니다.


5. Q) `LIKE` 연산자를 사용하여 case insensitive search를 할 수 있는 방법은
무엇입니까?

    A) LIKE 연산자를 사용하여 비교하기 위해 필드에 `UPPER` 함수를 사용하시면
됩니다.


6. Q) Column delimited flat file을 생성하는 방법은 무엇입니까?

    A) 해결 방법은 두 가지가 있습니다.

1> query 내에서 column 분리자를 concatenate시키면 됩니다.

(Example)
To delineate column text with TABs:
------------------------------------
sql> select deptno || chr(9) || dname from dept;

DEPTNO || CHR(9) || DNAME
------------------------------------
10        ACCOUNTING
20        RESEARCH
30        SALES
40        OPERATIONS

CHR(9) 문자함수는 query 문 안에 TAB을 embedding하기 위해 사용됩니다.

(Example)
To deleneate column test with COMMAs:
-------------------------------------
sql> select deptno || `,` || dname from dept;

DEPTNO || `,` || DNAME
-------------------------------------
10,ACCOUNTING
20,RESEARCH
30,SALES
40,OPERATIONS

Query 결과를 화일로 보내기 전에 다음과 같이 SET COMMAND를 수행하시기 바랍니다.

SET TERMOUT OFF
SET NEWPAGE 0
SET SPACE 0
SET LINESIZE 80
SET PAGESIZE 0
SET ECHO OFF
SET FEEDBACK OFF
SET HEADING OFF
SPOOL file_name

원하는 Query문 수행 후에는 다음과 같이 다시 setting 하시기 바랍니다.

SPOOL OFF
SET TERMOUT ON

SQL*plus 버젼 3.0.x와 3.1.x에서는 컬럼 사이의 간격을 조정하기 위해 column
분리자를 명시해 주는 SET COMMAND가 지원되지 않습니다.

2> SQL*plus 3.2 이상의 버젼에서는 COLSEP command를 사용하시면 됩니다.

Query 결과를 comma delimited flat file에 보내기 전에 다음과 같이 SET COMMAND
를 수행하시기 바랍니다.

SET TERMOUT OFF
SET NEWPAGE 0
SET SPACE 0
SET LINESIZE 80
SET PAGESIZE 0
SET ECHO OFF
SET FEEDBACK OFF
SET HEADING OFF
SET COLSEP `,`
SPOOL file_name

원하는 Query 문 수행 후에는 다음과 같이 다시 setting하시기 바랍니다.

SPOOL OFF
SET TERMOUT ON


7. Q) PUPBLD.SQL 스크립트를 찾지 못하여 public synonym을 drop할 수 없습니다.
해결 방법은 무엇이며, Product_User_Profile에 관하여 알고 싶습니다.

   A) Product_User_Profile은 product-level security를 제공하기 위해 DBA가
수정할 수 있는 테이블입니다.
    대부분의 오라클 product들은 SQL GRANT와 REVOKE command에 의해 제공되는
user-level security와 user role들을 추가한 product-level security를 제공하는
이 테이블을 사용합니다.  
    이 테이블을 생성하기 위해서는 SYSTEM으로 connect하여 PUPBLD.SQL이나
V7PUP.SQL을 돌려주어야 합니다.
   PUPBLD.SQL 스크립트를 실행하면 SYSTEM 계정 내에 product_profile과
user_profile 테이블을 생성해 줍니다. 여기에는 두 개의 view가 있는데,
product_privs와 user_privs가 그것입니다.
    사용자들은 product_profile과 user_profile이라는 이름으로 조회할 수 있습
니다.
이 view를 조회하면 해당 계정에 관한 privileges와 profile들을 알 수 있습니다.
    그런데, 현재 사용 중인 일부 product나 user profile structure는 제거하지
않기 때문에 manual하게 table, synonym, view들을 drop해 주어야 합니다.
    만약, delete해야 할 내재하는 structure가 있다면, 다음의 SQL문을 SQL*plus
에서 수행해 주시기 바랍니다.

    drop synonym product_user_profile;
    drop table product_profile;
    drop table product_user_profile;
    drop view product_privs;
    revoke select on product_privs from public;
    drop public synonym product_profile;
    drop synonym product_user_profile;
    drop public synonym product_user_profile;
    drop table user_profile;
    revoke select on user_profile from public;
    drop view user_privs;
    revoke select, update, insert, delete on user_privs from public;
    drop public synonym user_profile;
PURPOSE
---------

특정 범위를 설정하고 그 범위 사이에 있는 salary를 가진 사람의 count를
구하려면 다음과 같이 한다.


Explanation
-----------

    직원의 급여 테이블에서 특정 범위의 급여를 받는 사람이 몇 명인지
    나타내주는 SQL을 DECODE, COUNT 함수를 이용하여 구현하기로 한다.


Example
--------

SQL> SELECT count(decode(sign(sal-4999), 1, sal)) "5000 - ",
             count(decode(sign(sal-5000), -1,
                      decode(sign(sal-3999), 1, sal))) "4000-4999",
             count(decode(sign(sal-4000), -1,
                      decode(sign(sal-2999), 1, sal))) "3000-3999",
             count(decode(sign(sal-3000),-1,
                      decode(sign(sal-1999),1,sal))) "2000-2999",
             count(decode(sign(sal-2000),-1,
                      decode(sign(sal- 999),1,sal))) "1000-1999",
             count(decode(sign(sal-1000),-1,sal)) "- 1000"
      FROM   emp;


    5000 -   4000-4999  3000-3999   2000-2999   1000-1999     - 1000
   --------  ---------  ---------   ---------   ---------    --------
        1           0          2           3           6           2
다음과 같은 자료를 갖는 TABLE을 가정해보자.

Employee Name     Employee Address   City         State     ZIP
-------------     ----------------   -----------  -----    ---------
Donald Duck       101 Cool Pond      Bedford      OR       12345
Bugs Bunny        540 Carrot Way     Atlanta      GA       29292
Elmer Fudd
Taz Devil         999 Wildwoods      Nashville    TN       99999  

REPORT를 생성할 때에는 종종 CITY와 STATE를 COMMA(,)로 연결하여 출력하곤
한다.
이때 다음과 같이 COLUMN 간에 CONCATENATING를 이용하면 가능하다.

select employee_name,
        employee_address,
        city || `, ` || state || ` ` || zip  city_state_zip
   from decode_sample;


위의 문장은 다음과 같은 결과를 갖는다.

EMplOYEE_NAME        EMplOYEE_ADDRESS     CITY_STATE_ZIP
-------------------- -------------------- ------------------------
Donald Duck          101 Cool Pond        Bedford, OR 12345
Bugs Bunny           540 Carrot Way       Atlanta, GA 29292
Elmer Fudd                                ,
Taz Devil            999 Wildwoods        Nashville, TN 99999

위와 같은 경우에서 주소의 자료가 없는 경우 필요없는 COMMA만 출력되게 된다.
이런 경우에 DECODE문을 이용하여 다음과 같이 처리하면 깔끔한 OUTPUT을
받을 수 있게 된다.

select employee_name,
        employee_address,
        city || decode(`, ` || state,`, `,``,`, ` || state) || ` `
             || zip  city_state_zip
   from decode_sample;

EMplOYEE_NAME        EMplOYEE_ADDRESS     CITY_STATE_ZIP
-------------------- -------------------- ------------------------
Donald Duck          101 Cool Pond        Bedford, OR 12345
Bugs Bunny           540 Carrot Way       Atlanta, GA 29292
Elmer Fudd
Taz Devil            999 Wildwoods        Nashville, TN 99999
PURPOSE
---------

절대적인 값이 아닌 A와 B 사이의 값과 같은 range에 따른 값에 따라
DECODE function을 이용할 수 있을까? 이미 BETWEEN A AND B가 사용되지
못함을 경험했을 것이다.


Explanation
-------------

Emp table에서 2000과 4000사이의 값을 `2000~4000`로 4001과 5000
사이의 값을 `4001~5000`로 나머지 값들은 `other`로 display하고자 한다.


범위           Decode
-------------  ------
2000 - 4000     2000~4000
4001 - 5000     4001~5000
default         other


Example
---------

  select  empno, ename, sal,
          DECODE(sal, GREATEST(LEAST(sal, 4000), 2000), `2000~4000`,
                      GREATEST(LEAST(sal, 5000), 4001), `4001~5000`,
                      `other`)
          from emp;

           EMPNO ENAME                  SAL DECODE(SA
--------------- ---------- --------------- ---------
            7369 SMITH                  800 other
            7499 ALLEN                 1600 other
            7521 WARD                  1250 other
            7566 JONES                 2975 2000~4000
            7654 MARTIN                1250 other
            7698 BLAKE                 2850 2000~4000
            7782 CLARK                 2450 2000~4000
            7788 SCOTT                 3000 2000~4000
            7839 KING                  5000 4001~5000
            7844 TURNER                1500 other
            7876 ADAMS                 1100 other
            7900 JAMES                  950 other
            7902 FORD                  3000 2000~4000
            7934 MILLER                1300 other
방법1) SQL*plUS에서 각각의 TABLE에 대하여 ANALYZE를 실행하면
        USER_TABLES Data Dictionary에 각각의 record 건수가 등록된다.

방법2) SQLplUS에서 pl/SQL Program으로 처리한다.
==============================================================
create or replace procedure row_cnt
is
   cursor_name     integer;
   rows_processed  integer;
   rowcnt          number := 0;
   cursor c1 is select table_name from user_tables;
begin
   cursor_name := dbms_sql.open_cursor;
   for i in c1 loop
       dbms_sql.parse(cursor_name,
                      `select count(*) from `|| i.table_name,
                      dbms_sql.v7);
       dbms_sql.define_column(cursor_name,1,rowcnt);
       rows_processed := dbms_sql.execute(cursor_name);
       if dbms_sql.fetch_rows(cursor_name)> 0 then
          dbms_sql.column_value(cursor_name,1,rowcnt);
          dbms_output.put_line(rpad(i.table_name,30,` `)||` `
                               ||lpad(to_char(rowcnt),12,` `));
     end if;
   end loop;
   dbms_sql.close_cursor(cursor_name);
end;
==============================================================
$ sqlplus scott/tiger

SQL>  select trunc(date2-date1) || ` day  ` ||
              trunc(mod((date2-date1),1)*24) || ` hour  ` ||
              trunc(mod((date2-date1)*24,1)*60) || ` minute  ` ||
              trunc(round(mod((date2-date1)*24*60,1)*60)) || ` sec  `
              " Time Interval "
         from dual;
PURPOSE
-------
다음은 특정위치의 문자열이 한글인지 영문인지 즉, 문자유형을 판별하는
방법에 대해 간단한 예와 함께 소개한다.

Explanation
-----------
PL/SQL을 사용할 때 문자열의 특정 위치에 있는 문자가 ASCII인지, 한글의 첫번째
바이트인지, 또는 한글의 두번째 바이트인지를 확인해야 할 경우가 있습니다.
이런 경우, 문자 코드 값이 특정 값(예를 들어, KSC5601의 경우에는 0xA1)보다 큰지
작은지를 비교하여 판단하는 루틴을 사용하면 DB CharacterSet이 다른 경우에는
전혀 사용할 수 없습니다.  아래의 PL/SQL 함수는 Oralce WebServer의 NLS 문제를
해결하기 위해 만든 것입니다.  절대값과 비교하는 방식을 사용하지 않았으므로
DB CharacterSet에 관계없이 사용할 수 있습니다.
 
Example
-------
/* determine character type from cbuf of specified position bytepos */
/*   bytepos is zero-based */
/*   return : 0 if ascii, 1 if 1st byte of DBCS, 2 if 2nd byte of DBCS */
 
function nls_getchartype (cbuf in varchar2, bytepos in integer) return
integer
is
    loc integer:=0;
    ret integer:=0;
begin
    if cbuf is NULL then  
       return 0;  
    end if;
    
    while (loc <= bytepos)
    loop
       if (ret = 1 ) then
          ret := 2;
       elsif (length(substrb(cbuf, loc+1, 2)) = 1) then
          ret := 1;
       else
          ret := 0;
       end if;
       loc := loc + 1;
    end loop;
    return ret;
end;


위가 않되는 경우 아래의 function을 실행하십시요.

create or replace function nls (cbuf in varchar2, bytepos in integer) return
integer
is
    loc integer:=1;
    ret integer:=0;
begin
    if cbuf is NULL then  
       return 0;  
    end if;
    
    while (loc <= bytepos)
    loop
       if (ret = 1 ) then
          ret := 2;
       elsif (length(substrb(cbuf, loc, 2)) = 1) then
          ret := 1;
       else
          ret := 0;
       end if;
       loc := loc + 1;
    end loop;
    return ret;
end;

**실행 **


SQL>variable a number;
SQL>  exec :a := nls(`t하t`,1);

PL/SQL procedure successfully completed.

SQL> print a

          A
----------
          0

SQL> exec :a:=nls(`t하t`,2);

PL/SQL procedure successfully completed.

SQL> print a

          A
----------
          1

SQL> exec :a := nls(`t하t`,3);

PL/SQL procedure successfully completed.

SQL> print a

          A
----------
          2
PURPOSE
-------

간단한 xml 문서를 PLSQL을 이용해서 database 에
저장하고 PLSQL parser 를 이용해 validate 하는 방법을
예제를 통해 알아 봅니다.


Example
-------

Step1
-----

xml 문서를 저장할 table 을 생성합니다.

CREATE TABLE xml_doc (
docname    VARCHAR2(100) PRIMARY KEY,
doc        CLOB,
insertdate DATE DEFAULT SYSDATE);


Step 2
------

XML 문서는 os 의 physical directory 에 있으므로 oracle database
에 logical directory 를 만들어 mapping 시켜주어야 합니다.

SQL> CREATE DIRECTORY xmldocs AS `C:XML`;


만일 ORA-1031 에러가 발생할 경우 SYS 또는 SYSTEM user 로 접속해서
다음 명령으로 권한을 줍니다.

SQL> grant create any directory to scott;
Grant succeeded.


Step 3
------

database 에 넣을 XML 문서를 만듭니다.



  
     7844
     Manager
     100000
  

  
     100
     President
     200000
  




위에서 만든 emp.xml 파일을 db 에 insert 할 PLSQL procedure 를 생성합니다.


CREATE OR REPLACE PROCEDURE insertXML (dirname IN VARCHAR2,
                                       filename IN VARCHAR2)
IS
   xmlfile BFILE;
   myclob  CLOB;
BEGIN
   INSERT INTO xml_doc (docname, doc)
   VALUES (filename, empty_clob())
   RETURNING doc into myclob;  
  
   -- get a handle to the xml file on the OS
   xmlfile := Bfilename(dirname,filename);

   -- open the file
   DBMS_LOB.fileOpen(xmlfile);

   -- copy the contents of the file into the empty clob
   DBMS_LOB.loadFromFile(myclob, xmlfile, dbms_lob.getLength(xmlfile));

END insertXML;
/

Execute the Procedure

SQL> EXEC insertXML(`XMLDOCS`, `emp.xml`);

PL/SQL procedure successfully completed.



Step 4
------

insert 된 data 를 확인해 봅니다.

SQL> select * from xml_doc;

DOCNAME         DOC                                      INSERTDATE
--------------- ---------------------------------------- ---------------
emp.xml        
                          27-OCT-00
                                  
                                                  
                     7844

[펌] mysql start & shutdown

프로그래밍/DB 2007. 11. 27. 15:08 Posted by galad

먼저 데이터베이스의 시작과은 다음과같은 방법으로 할 수가 있습니다.

MySQL이 설치되는 위치는 일반적으로 /usr/local/mysql/입니다.

그리고 MySQL관련 명령어들이 들어있는 디렉토리는 /usr/local/mysql/bin입니다.

이 디렉토리의 내용을 보면 다음과 같습니다.

여기서 Mysql데이터베이스를 기동하기 방법에는 여러 가지가 있지만 가장 일반적으로 사용하는 것은 "safe_mysql"입니다.

다음과 같은 방법으로 데이터베이스를 기동할 수 있습니다.

그리고 정상적으로 기동이 되어서 mysqld라는 데몬이 뜨있는지를 유닉스명령어인 ps로 확인해 본 것입니다.

mysql이 기동이 되고나면 보시는 바와 같이 4개의 데몬이 뜨있어야만 정상적으로 작동하게 됩니다.

 

 

Mysql 데이터베이스의 종료

이 디렉토리에 존재하는 mysqladmin이라는 명령어를 사용하여 데이터베이스를 다음과 같이 종료할 수 있습니다.

 

그리고 다음과 같이 다시한번 ps로 확인을 해보면 mysql관련 데몬들이 모두 종료된 것을 알 수 있습니다.

 

 

Mysql 데이터베이스의 재시작

다음과 같이 데이터베이스를 재시작 하기위해서는 mysqladmin이라는 데이터베이스 관리자명령어로 할 수가 있습니다.

아무런 메시지 없이 쉘프롬프트가 떨어지면 정상적으로 재시작이 되었다는 것을 의미합니다.

[펌] 계정 생성 및 권한부여

프로그래밍/DB 2007. 11. 27. 15:08 Posted by galad
계정 생성 및 권한부여
-출처 : 근사모
## MySQL 계정 생성하기

MySQL 의 계정을 생성하는 방법에 대해서 알아 보도록 하겠습니다.

일단 MySQL 콘솔로 접속해야 겠죠 ?
콘솔로 접속하기 위해서 MySQL 의 bin 디렉토리로 이동합니다.

리눅스 라면 기본적으로 /usr/local/mysql/bin 디렉토리이고
윈도우의 APM_Setup 이라면 APM_Setup\Server\MySQL\bin 디렉토리 일 것 입니다.

이젠 MySQL 에 접속해 볼까요 ?

mysql -uroot -p


의 명령으로 MySQL 콘솔 모드로 접속합니다.
Password 를 물어 보면 root 패스워드를 입력해야 겠죠 ?

그럼..

이제 생성할 계정이 사용할 데이터베이스를 추가해야 겠죠 ?
데이터 베이스 추가 명령은

CREATE DATABASE [IF NOT EXISTS] db_name


이런식의 SQL 문 입니다.

그럼 많은 사람들이 제로보드를 사용하니 zboard 를 만들어 보겠습니다. 그러면

CREATE DATABASE IF NOT EXISTS zboard;


이렇게 입력합니다.
그럼 zboard 디비가 추가 되겠죠!

이제 MySQL 계정을 만들어 볼까요 ?

먼저 계정 생성 SQL 문 먼저 알아 볼까요 ?

GRANT USAGE ON *.* TO 사용자ID@접속호스트 IDENTIFIED BY "패스워드"


이렇게 구성되어 있습니다.

그럼 호스트는 localhost 사용자 ID 는 zboard 패스워드는 password
추가 하시려면..

GRANT USAGE ON *.* TO zboard@localhost IDENTIFIED
BY "password";


위와 같은 SQL 문이되겠죠 ?

이젠.. 위에서 생성한 데이터 베이스의 사용권한을 부여해야 겠죠..

GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON
db_name TO 사용자ID@접속호스트


이런 명령으로 이루어져 있습니다.

그럼 호스트는 localhost 사용자 ID 는 zboard 데이터 베이스 이름은
zboard 라고 한다면..

GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON
zboard.* TO zboard@localhost;


이렇게 되겠네욤..

이제 설정을 다시 했으니 권한을 다시 로드 해야 하므로

FLUSH PRIVILEGES;


SQL 문을 입력해 주면.. 되겠죵..

이제 종료 하시고...

만든 계정으로 MySQL 에 접속해 볼까욤

mysql -uzboard -p zboard


이런 명령으로 접속하시면 됩니다.
패스워드를 물어 보면 계정의 패스워드를 입력하시면 됩니다.

객체직렬화를 통해 직렬화된 객체를 Oracle BLOB에 저장하기


자바의 객체직렬화를 이용하면 직렬화된 객체를 파일, 데이터베이스 또는 원격으로 전송 하는 것이 가능 합니다.


물론 객체직렬화가 되기 위해선 Object는 자바의 Seriablizable 이라는 Interface를 구현해야 합니다. 그래서 이 Object는 Byte Stream 형태로 저장 되어 파일이나 DB에 저장 된 후 나중에 Load되어서 복원이 가능 하게 되는 것입니다.


아래의 예제는 ObjectSerTest 라는 클래스를 직렬화 가능하게 만든 후 오라클의 BLOB에  입력 한 후 읽어내는 예제 입니다.



=====================================================


SQL>conn scott/tiger
SQL> create sequence object_ser_seq increment by 1 start with 1;


주문번호가 생성되었습니다.


SQL> create table object_table (
  2  no number,
  3  obj_name varchar2(2000),
  4  obj_value blob default empty_blob()
  5  );


테이블이 생성되었습니다.


=================================================



/*
* Created on 2005. 2. 11
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package jdbc;


import java.io.*;
import java.sql.*;
import oracle.sql.*;


class ObjectSerTest implements java.io.Serializable{


  static final String driver_class = "oracle.jdbc.driver.OracleDriver";
  static final String connectionURL = "jdbc:oracle:thin:@localhost:1521:wink";
  static final String userID = "scott";
  static final String userPassword = "tiger";
  static final String getSequenceSQL = "SELECT object_ser_seq.nextval FROM dual";
  static final String writeObjSQL    = "BEGIN " +
                                       "  INSERT INTO object_table(no, obj_name, obj_value) " +
                                       "  VALUES (?, ?, empty_blob()) " +
                                       "  RETURN obj_value INTO ?; " +
                                       "END;";
  static final String readObjSQL     = "SELECT obj_value FROM object_table WHERE no = ?";


  /*
   ** +--------------------------------------------------+
   ** | METHOD: writeObj                                 |
   ** +--------------------------------------------------+
  */
  public static long writeObj(Connection conn, Object obj) throws Exception {


    long id = getNextSeqVal(conn);
   
    String className = obj.getClass().getName();
    CallableStatement stmt = conn.prepareCall(writeObjSQL);
   
    stmt.setLong(1, id);
    stmt.setString(2, className);   
    stmt.registerOutParameter(3, java.sql.Types.BLOB);
    stmt.executeUpdate();
   
    BLOB blob = (BLOB) stmt.getBlob(3);
    OutputStream os = blob.getBinaryOutputStream();
    ObjectOutputStream oop = new ObjectOutputStream(os);
    oop.writeObject(obj);
    oop.flush();
    oop.close();
    os.close();
    stmt.close();
    System.out.println("Done serializing: " + className);
    return id;


  } // END: writeObj



  /*
   ** +--------------------------------------------------+
   ** | METHOD: readObj                                  |
   ** +--------------------------------------------------+
  */
  public static Object readObj(Connection conn, long id) throws Exception {


    PreparedStatement stmt = conn.prepareStatement(readObjSQL);
    stmt.setLong(1, id);
    ResultSet rs = stmt.executeQuery();
    rs.next();
    InputStream is = rs.getBlob(1).getBinaryStream();
    ObjectInputStream oip = new ObjectInputStream(is);
    Object obj = oip.readObject();
    String className = obj.getClass().getName();
    oip.close();
    is.close();
    stmt.close();
    System.out.println("Done de-serializing: " + className);
    return obj;


  } // END: readObj


  /*
   ** +--------------------------------------------------+
   ** | METHOD: getNextSeqVal                            |
   ** +--------------------------------------------------+
  */
  private static long getNextSeqVal (Connection conn) throws SQLException {


    Statement stmt = conn.createStatement();
    ResultSet rs   = stmt.executeQuery(getSequenceSQL);
    rs.next();
    long id = rs.getLong(1);
    rs.close();
    stmt.close();
    return id;


  } // END: getNextSeqVal


  /*
   ** +--------------------------------------------------+
   ** | METHOD: main                                     |
   ** +--------------------------------------------------+
  */
  public static void main (String args[]) throws SQLException {


    Connection conn = null;
    Statement stmt = null;
    ResultSet rset = null;
    int insertResults;
    int deleteResults;


    try {
    
      System.out.print("\n");
      System.out.print("Loading JDBC Driver  -> " + driver_class + "\n");
      Class.forName (driver_class).newInstance();


      /*
      ** CONNECT TO THE DATABASE
      */
      System.out.print("Connecting to        -> " + connectionURL + "\n");
      conn = DriverManager.getConnection(connectionURL, userID, userPassword);
      System.out.print("Connected as         -> " + userID + "\n\n");


      /*
      ** TURN OFF AutoCommit
      */
      conn.setAutoCommit(false);


      ObjectSerTest obj = new ObjectSerTest();     


      long no = writeObj(conn, obj);
      conn.commit();


      System.out.print("Serialized OBJECT_ID => " + no + "\n\n");


      System.out.print("OBJECT VALUES  => " + readObj(conn, no) + "\n\n");
      conn.close();


    }  // TRY:


    catch (Exception e) {
      e.printStackTrace();
    }


    finally {
      if (conn != null) {
        try {
          System.out.print("Closing down all connections...\n\n");
          conn.close();
객체직렬화를 통해 직렬화된 객체를 Oracle BLOB에 저장하기



자바와 오라클 개발자 전문가과정





자바의 객체직렬화를 이용하면 직렬화된 객체를 파일, 데이터베이스 또는 원격으로 전송 하는 것이 가능 합니다.


물론 객체직렬화가 되기 위해선 Object는 자바의 Seriablizable 이라는 Interface를 구현해야 합니다. 그래서 이 Object는 Byte Stream 형태로 저장 되어 파일이나 DB에 저장 된 후 나중에 Load되어서 복원이 가능 하게 되는 것입니다.


아래의 예제는 ObjectSerTest 라는 클래스를 직렬화 가능하게 만든 후 오라클의 BLOB에  입력 한 후 읽어내는 예제 입니다.



=====================================================


SQL>conn scott/tiger
SQL> create sequence object_ser_seq increment by 1 start with 1;


주문번호가 생성되었습니다.


SQL> create table object_table (
  2  no number,
  3  obj_name varchar2(2000),
  4  obj_value blob default empty_blob()
  5  );


테이블이 생성되었습니다.


=================================================



/*
* Created on 2005. 2. 11
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package jdbc;


import java.io.*;
import java.sql.*;
import oracle.sql.*;


class ObjectSerTest implements java.io.Serializable{


  static final String driver_class = "oracle.jdbc.driver.OracleDriver";
  static final String connectionURL = "jdbc:oracle:thin:@localhost:1521:wink";
  static final String userID = "scott";
  static final String userPassword = "tiger";
  static final String getSequenceSQL = "SELECT object_ser_seq.nextval FROM dual";
  static final String writeObjSQL    = "BEGIN " +
                                       "  INSERT INTO object_table(no, obj_name, obj_value) " +
                                       "  VALUES (?, ?, empty_blob()) " +
                                       "  RETURN obj_value INTO ?; " +
                                       "END;";
  static final String readObjSQL     = "SELECT obj_value FROM object_table WHERE no = ?";


  /*
   ** +--------------------------------------------------+
   ** | METHOD: writeObj                                 |
   ** +--------------------------------------------------+
  */
  public static long writeObj(Connection conn, Object obj) throws Exception {


    long id = getNextSeqVal(conn);
   
    String className = obj.getClass().getName();
    CallableStatement stmt = conn.prepareCall(writeObjSQL);
   
    stmt.setLong(1, id);
    stmt.setString(2, className);   
    stmt.registerOutParameter(3, java.sql.Types.BLOB);
    stmt.executeUpdate();
   
    BLOB blob = (BLOB) stmt.getBlob(3);
    OutputStream os = blob.getBinaryOutputStream();
    ObjectOutputStream oop = new ObjectOutputStream(os);
    oop.writeObject(obj);
    oop.flush();
    oop.close();
    os.close();
    stmt.close();
    System.out.println("Done serializing: " + className);
    return id;


  } // END: writeObj



  /*
   ** +--------------------------------------------------+
   ** | METHOD: readObj                                  |
   ** +--------------------------------------------------+
  */
  public static Object readObj(Connection conn, long id) throws Exception {


    PreparedStatement stmt = conn.prepareStatement(readObjSQL);
    stmt.setLong(1, id);
    ResultSet rs = stmt.executeQuery();
    rs.next();
    InputStream is = rs.getBlob(1).getBinaryStream();
    ObjectInputStream oip = new ObjectInputStream(is);
    Object obj = oip.readObject();
    String className = obj.getClass().getName();
    oip.close();
    is.close();
    stmt.close();
    System.out.println("Done de-serializing: " + className);
    return obj;


  } // END: readObj


  /*
   ** +--------------------------------------------------+
   ** | METHOD: getNextSeqVal                            |
   ** +--------------------------------------------------+
  */
  private static long getNextSeqVal (Connection conn) throws SQLException {


    Statement stmt = conn.createStatement();
    ResultSet rs   = stmt.executeQuery(getSequenceSQL);
    rs.next();
    long id = rs.getLong(1);
    rs.close();
    stmt.close();
    return id;


  } // END: getNextSeqVal


  /*
   ** +--------------------------------------------------+
   ** | METHOD: main                                     |
   ** +--------------------------------------------------+
  */
  public static void main (String args[]) throws SQLException {


    Connection conn = null;
    Statement stmt = null;
    ResultSet rset = null;
    int insertResults;
    int deleteResults;


    try {
    
      System.out.print("\n");
      System.out.print("Loading JDBC Driver  -> " + driver_class + "\n");
      Class.forName (driver_class).newInstance();


      /*
      ** CONNECT TO THE DATABASE
      */
      System.out.print("Connecting to        -> " + connectionURL + "\n");
      conn = DriverManager.getConnection(connectionURL, userID, userPassword);
      System.out.print("Connected as         -> " + userID + "\n\n");


      /*
      ** TURN OFF AutoCommit
      */
      conn.setAutoCommit(false);


      ObjectSerTest obj = new ObjectSerTest();     


      long no = writeObj(conn, obj);
      conn.commit();


      System.out.print("Serialized OBJECT_ID => " + no + "\n\n");


      System.out.print("OBJECT VALUES  => " + readObj(conn, no) + "\n\n");
      conn.close();


    }  // TRY:


    catch (Exception e) {
      e.printStackTrace();
    }


    finally {
      if (conn != null) {
        try {
          System.out.print("Closing down all connections...\n\n");
          conn.close();
        }
        catch (SQLException e) {
          e.printStackTrace();
        }
      }
    } // FINALLY:


  } // METHOD: main


} // CLASS: ObjectSerTest




[결과]



Loading JDBC Driver  -> oracle.jdbc.driver.OracleDriver
Connecting to        -> jdbc:oracle:thin:@localhost:1521:wink
Connected as         -> scott


Done serializing: jdbc.ObjectSerTest
Serialized OBJECT_ID => 2


Done de-serializing: jdbc.ObjectSerTest
OBJECT VALUES  => jdbc.ObjectSerTest@601bb1


Closing down all connections...
        }
        catch (SQLException e) {
          e.printStackTrace();
        }
      }
    } // FINALLY:


  } // METHOD: main


} // CLASS: ObjectSerTest




[결과]



Loading JDBC Driver  -> oracle.jdbc.driver.OracleDriver
Connecting to        -> jdbc:oracle:thin:@localhost:1521:wink
Connected as         -> scott


Done serializing: jdbc.ObjectSerTest
Serialized OBJECT_ID => 2


Done de-serializing: jdbc.ObjectSerTest
OBJECT VALUES  => jdbc.ObjectSerTest@601bb1


Closing down all connections...

'프로그래밍 > DB' 카테고리의 다른 글

[펌] mysql start & shutdown  (0) 2007.11.27
[펌] 계정 생성 및 권한부여  (0) 2007.11.27
[펌] Oracle - PL/SQL  (0) 2007.11.27
[펌] PL/SQL 조건문 만들기  (0) 2007.11.27
[펌] 오라클자료 - PL/SQL  (0) 2007.11.27

[펌] Oracle - PL/SQL

프로그래밍/DB 2007. 11. 27. 14:34 Posted by galad
출처 블로그 > 과거,현재 그리고 미래
원본 http://blog.naver.com/geerark/100005573550
Oracle - PL/SQL


PLSQL 이란?


PL/SQL 이란 ?

 
- PL/SQL 은 Oracle's Procedural Language extension to SQL. 의 약자 입니다.

 - SQL문장에서 변수정의, 조건처리(IF), 반복처리(LOOP, WHILE, FOR)등을 지원하며,
   오라클 자체에 내장되어 있는 Procedure Language입니다

 - DECLARE문을 이용하여 정의되며, 선언문의 사용은 선택 사항입니다.  

 - PL/SQL 문은 블록 구조로 되어 있고 PL/SQL 자신이 컴파일 엔진을 가지고 있습니다.



PL/SQL의 장점

 - PL/SQL 문은 BLOCK 구조다수의 SQL 문을 한번에 ORACLE DB 로 보내서 처리하므로
   수행속도를 향상 시킬수 있습니다.

 - PL/SQL 의 모든 요소는 하나 또는 두개이상의 블록으로 구성하여 모듈화가 가능하다.

 - 보다 강력한 프로그램을 작성하기 위해서 큰 블록안에 소블럭을 위치시킬 수 있습니다.

 - Variable, Constant, Cursor, Exception을 정의하고, SQL문장과 Procedural 문장에서
   사용합니다. .

 - 단순, 복잡한 데이터형태의 변수를 선언합니다.

 - 테이블의 데이터 구조와 DataBase의 컬럼럼에 준하여 동적으로 변수를 선언 할 수 있습니다.

 - Exception 처리 루틴을 이용하여 Oracle Server Error를 처리합니다.

 - 사용자 정의 에러를 선언하고 Exception 처리 루틴으로 처리 가능 합니다.


PL/SQL Block 구조


PL/SQL Block Structure

 - PL/SQL은 프로그램을 논리적인 블록으로 나누는 구조화된 블록 언어 입니다.

 - PL/SQL 블록은 선언부(선택적), 실행부(필수적),예외 처리부(선택적)로 구성되어 있고,  
   BEGIN과 END 키워드는 반드시 기술해 주어야 합니다.

 - PL/SQL 블록에서 사용하는 변수는 블록에 대해 논리적으로 선언할 수 있고 사용할 수 있습니다.

 

● DECLARE
    - Optional
    - Variables, cursors, user-defined exceptions

● BEGIN
    - Mandatory
    - SQL Statements
    - PL/SQL Statements

● EXCEPTION
    - Actions to perform when errors occur

● END;
    - Mandatory



  ◈ Declarative Section(선언부)

   -  변수, 상수, CURSOR, USER_DEFINE Exception선언


  ◈ Executable Section(실행부)

   -  SQL, 반복분, 조건문실행
   -  실행부는 BEGIN으로 시작하고 END 로 끝납니다.
   -  실행문은 프로그램 내용이 들어가는 부분으로서 필수적으로 사용되어야 합니다.


  ◈ Exception Handling Section(예외처리)

    - 예외에 대한 처리.
    - 일반적으로 오류를 정의하고 처리하는 부분으로 선택 사항입니다.



PL/SQL 프로그램의 작성 요령

 
 - PL/SQL 블록내에서는 한 문장이 종료할 때마다 세미콜론(;)을 사용합니다. .

  - END뒤에 ;을 사용하여 하나의 블록이 끝났다는 것을 명시 합니다.

  - PL/SQL 블록의 작성은 편집기를 통해 파일로 작성할 수도 있고,
    SQL프롬프트에서 바로 작성할 수도 있습니다.

  - SLQ*PLUS환경에서는 DELCLARE나 BEGIN이라는 키워드로 PL/SQL블럭이 시작하는 것을
    알 수 있습니다.

 - 단일행 주석 : --

 - 여러행 주석 : /* */

 - PL/SQL 블록은 행에 / 가있으면 종결 됩니다.
 


PL/SQL 블럭의 유형


Block Type(PL/SQL 블럭의 유형)

 
[ Anonymous ]

 
[ Procedure ]

 
[ Function ]



 ◈ Anonymous Block(익명 블록)
 
    이름이 없는 블록을 의미 합니다.
    실행하기 위해 프로그램 안에서 선언되고 실행 시에 실행을 위해 PL/SQL 엔진으로 전달됩니다.
    선행 컴파일러 프로그램과 SQL*Plus 또는 서버 관리자에서 익명의 블록을 내장할 수 있습니다.
 
   
 ◈ Procedure(프로시저)
   
    특정 작업을 수행할수 있는 이름이 있는 PL/SQL 블록으로서.
    매개 변수를 받을수 있고.. 반복적으로 사용할수 있는거죠..
    보통 연속 실행 또는 구현이 복잡한 트랜잭션을 수행하는 PL/SQL블록을
    데이터 베이스에 저장하기 위해 생성합니다.
 
 
 ◈ Function(함수)
 
    보통 값을 계산하고 결과값을 반환하기 위해서 함수를 많이 사용 합니다.
    대부분 구성이 프로시저와 유사하지만 IN 파라미터만 사용 할 수 있고,
    반드시 반환될 값의 데이터 타입을 RETURN문에 선언해야 합니다.
    또한 PL/SQL블록 내에서 RETURN문을 통해서 반드시 값을 반환해야 합니다.


프로시저(PROCEDURE)


프로시져란..


특정 작업을 수행할수 있고, 이름이 있는 PL/SQL 블록으로서. 매개 변수를 받을수
있고.. 반복적으로 사용
할수 있는거죠.. 보통 연속 실행 또는 구현이 복잡한 트랜잭션을 수행하는
PL/SQL블록을 데이터 베이스에 저장하기 위해 생성합니다.

CREATE OR REPLACE 구문을 사용하여 생성합니다.
IS 로 PL/SQL의 블록을 시작합니다.
LOCAL 변수는 IS 와 BEGIN 사이에 선언합니다.
 

    [Syntax]

    CREATE OR REPLACE procedure name
       IN argument
       OUT argument
       IN OUT argument

    IS

       [변수의 선언]

    BEGIN  
    --> 필수

       [PL/SQL Block]
       -- SQL문장, PL/SQL제어 문장

       [EXCEPTION]  --> 선택
      -- error가 발생할 때 수행하는 문장

    END;   --> 필수


프로시저 작성 예제

SQL>CREATE OR REPLACE PROCEDURE update_sal
        /* IN  Parameter */
        (v_empno         IN    NUMBER)
        
       IS

       BEGIN

       UPDATE emp
       SET sal = sal  * 1.1
       WHERE empno = v_empno;

       COMMIT;

       END update_sal;
       /

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


설명..
프로시저의 이름은 update_sal이고..
프로시저 update_sal은 사번(v_empno)를 입력받아서 급여를 update시켜주는 sql문입니다.
프로시저를 끝마칠때에는 항상 "/"를 지정 합니다.

프로시저의 실행
EXECUTE 문을 이용해 프로시저를 실행합니다.

SQL> execute update_sal(7369);
 
PL/SQL 처리가 정상적으로 완료되었습니다.

7369번 사원의 급여가 10% 인상됐습니다.
SELECT 문을 실행시켜보면 데이터가 수정된 것을 확인할수 있습니다.



Parameter란
 
실행 환경과 program사이에 값을 주고 받는 역할을 합니다.

 
블록 안에서의 변수와 똑같이 일시적으로 값을 저장하는 역할을 합니다.

 
function에서는 IN만 있고, OUT과, INOUT는 사용하지 않습니다.

 
Parameter의 타입
    - IN :  실행환경에서 program으로 값을 전달
    - OUT : program에서 실행환경으로 값을 전달
    - INOUT : 실행환경에서 program으로 값을 전달하고,
                  다시 program에서 실행환경으로 변경된 값을 전달
.
 


함수(FUNCTION)


 ◈ 함수(Function)

 - 보통 값을 계산하고 결과값을 반환하기 위해서 함수를 많이 사용 합니다.

 - 대부분 구성이 프로시저와 유사 하지만 IN 파라미터만 사용 할 수 있습니다.

 - 반드시 반환될 값의 데이터 타입을 RETURN문에 선언해야 합니다.

 - 또한 PL/SQL블록 내에서 RETURN문을 통해서 반드시 값을 반환해야 합니다.
 


[Syntax]

CREATE OR REPLACE FUNCTION function name
  [(argument...)]
  RETURN  datatype
    -- Datatype은 반환되는 값의 datatype입니다.

IS

   [변수 선언 부분]

BEGIN

  [PL/SQL Block]

    -- PL/SQL 블록에는 적어도 한 개의 RETURN 문이 있어야 합니다.
    -- PL/SQL Block은 함수가 수행할 내용을 정의한 몸체부분입니다.

END;
 



SQL> CREATE OR REPLACE FUNCTION FC_update_sal
         (v_empno         IN    NUMBER)

          -- 리턴되는 변수의 데이터타입을 꼭 정의해야 합니다
          RETURN NUMBER  .

        IS

        v_sal  emp.sal%type;

        BEGIN

        UPDATE emp
        SET sal  = sal  * 1.1
        WHERE empno  = v_empno;
   
        COMMIT;
 
        SELECT sal
        INTO v_sal
        FROM emp
        WHERE empno = v_empno;

        -- 리턴문이 꼭 존재해야 합니다
        RETURN v_sal;

       END;

함수가 생성되었습니다.
 



설명..

이 함수에는 v_sal이라는 %type 변수가 사용되고 있습니다.
스칼라 데이터 타입을 참고하세요.
프로지저와 마찬가지로 세미콜론(;)으로 블록을 종료한 뒤 "/"를 붙여 코드를 끝마칩니다.


함수의 실행

먼저 함수의 반환값을 저장할 변수를 선언합니다.

SQL> VAR salary NUMBER;


EXECUTE 문을 이용해 함수를 실행합니다.
SQL>EXECUTE :salary := FC_update_sal(7900);

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


오라클 SQL에서 선언된 변수의 출력은 PRINT문을 사용합니다.
PRINT문으로 함수의 반환값을 저장한 salary의 값을 확인하면 됩니다.

SQL>PRINT salary;
 
    SALARY
----------
      1045

결과가 이렇게 나옵니다.
 


스칼라 데이터 타입

변수 선언 방법






Identifier의 이름은 sql의 object명과 동일한 규칙을 따릅니다.

Identifier를 상수로 지정하고 싶은 경우는 CONSTANT라는 KEYWORD를 명시하고
    반드시 초기화를 할당합니다.

NOT NULL이 정의되어 있으면 초기값을 반드시 지정하고,
    정의되어 있지 않을 때는 생략 가능합니다.

초기값은 할당 연산자(:=)를 사용하여 정의 합니다.

초기값을 정의하지 않으면 Identifier는 NULL값을 가지게 됩니다.

일반적으로 한줄에 한 개의 Identifier를 정의 합니다.
 


스칼라 데이터 타입은 단수 데이터형으로 한가지의 데이터 값만 가집니다.
 

BINARY_INTEGER

-2147483647에서 2147483647 사이의 정수

NUMBER[(P, S)]

고정 및 부동 소숫점 수에 대한 기본 유형

CHAR[(최대길이)]

32767바이트까지의 고정길이 문자 데이터에 대한 기본 유형으로 최대길이를 명시하지 않으면 기본적으로 길이는 1로 설정

LONG

32760바이트까지의 가변길이 문자 데이타

VARCHAR2(최대길이)

32767바이트까지의 가변길이 문자 데이타

DATE

날짜와 시각에 대한 기본 유형

BOOLEAN

논리연산에 사용되는 세 가지 값(TRUE, FALSE, NULL) 중 하나를 저장 하는 기본 유형


선언 예제
v_price CONTANT NUMBER(4,2) := 12.34 ;     -- 상수 숫자 선언(변할 수 없다)

v_name VARCHAR22(20) ;

v_Bir_Type   CHAR(1) ;

v_flag   BOOLEAN  NOT NULL := TRUE ;      -- NOT NULL 값 TRUE로 초기화

v_birthday DATE;



%TYPE 데이터형


%TYPE 데이터형은 기술한 데이터베이스 테이블의 컬럼 데이터 타입을 모를 경우 사용할 수 있고,

또. 코딩이후 데이터베이스 컬럼의 데이터 타입이 변경될 경우 다시 수정할 필요가 없습니다.

이미 선언된 다른 변수나 데이터베이스 컬럼의 데이터 타입을 이용하여 선언합니다.

 데이터 베이스 테이블과 컬럼 그리고 이미 선언한 변수명이 %TYPE앞에 올수 있습니다.


%TYPE 속성을 이용하여 얻을 수 있는 장점
   - 기술한 DB column definition을 정확히 알지 못하는 경우에 사용할 수 있습니다.
   - 기술한 DB column definition이 변경 되어도 다시 PL/SQL을 고칠 필요가 없습니다.
 

예제
v_empno  emp.empno%TYPE := 7900 ;
v_ename emp.ename%TYPE;
 

예제 프로시져..

SQL>CREATE OR REPLACE PROCEDURE Emp_Info
      -- IN Parameter
      ( p_empno IN emp.empno%TYPE )

        IS

        -- %TYPE 데이터형 변수 선언
        v_empno emp.empno%TYPE;
        v_ename emp.ename%TYPE;
        v_sal   emp.sal%TYPE;

        BEGIN

        DBMS_OUTPUT.ENABLE;

        -- %TYPE 데이터형 변수 사용
        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>SET SERVEROUTPUT ON;     --  DBMS_OUTPUT 결과값을 화면에 출력 하기위해


실행 결과
SQL> EXECUTE Emp_Info(7369);

사원번호 : 7369
사원이름 : SMITH
사원급여 : 880
 
PL/SQL 처리가 정상적으로 완료되었습니다.
 


%ROWTYPE


하나 이상의 데이터값을 갖는 데이터 타입으로 배열과 비슷한 역할을 하고 재사용이 가능합니다.
%ROWTYPE데이터 형과, PL/SQL테이블과 레코드가 복합 데이터 타입에 속합니다.


%ROWTYPE


 테이블이나 뷰 내부의 컬럼 데이터형, 크기, 속석등을 그대로 사용할수 있습니다.

 %ROWTYPE 앞에 오는 것은 데이터 베이스 테이블 이름입니다.

 지정된 테이블의 구조와 동일한 구조를 갖는 변수를 선언할수 있습니다.

 데이터베이스 컬럼들의 수나 DATATYPE을 알지 못할 때 편리 합니다.

 테이블의 데이터 컬럼의 DATATYPE이 변경될 경우 프로그램을 재수정할 필요가 없습니다.  
 
 


%ROWTYPE 예제 프로시져..

SQL>CREATE OR REPLACE PROCEDURE RowType_Test
        ( p_empno IN emp.empno%TYPE )

    IS

        -- %ROWTYPE 변수 선언
        v_emp   emp%ROWTYPE ;

   BEGIN

        DBMS_OUTPUT.ENABLE;

        -- %ROWTYPE 변수 사용
        SELECT empno, ename, hiredate
        INTO v_emp.empno, v_emp.ename, v_emp.hiredate
        FROM emp
        WHERE empno = p_empno;

       DBMS_OUTPUT.PUT_LINE( '사원번호 : ' || v_emp.empno );
       DBMS_OUTPUT.PUT_LINE( '사원이름 : ' || v_emp.ename );
       DBMS_OUTPUT.PUT_LINE( '입 사 일 : ' || v_emp.hiredate );

   END;
        /

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



실행 결과

SQL> SET SERVEROUTPUT ON ;  -- DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용

SQL> EXECUTE RowType_Test(7900);

사원번호 : 7900
사원이름 : JAMES
입 사 일 : 81/12/03

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


PL/SQL 테이블


 
PL/SQL 에서의 테이블은 오라클 SQL에서의 테이블과는 다릅니다. PL/SQL에서의 테이블은
일종의 일차원 배열이라고 생각하시면 이해하기 쉬울겁니다.


테이블은 크기에 제한이 없으면 그 ROW의 수는 데이터가 들어옴에 따라 자동 증가 합니다.

BINARY_INTEGER 타입의인덱스 번호로 순서가 정해집니다.

하나의 테이블에 한 개의 컬럼 데이터를 저장 합니다.  
 




예제


TYPE prdname_table IS TABLE OF VARCHAR2(30)
INDEX BY BINARY_INTEGER;

--
prdname_table 테이블타입으로 prdname_tab변수를 선언해서 사용
prdname_tab   prdname_table ;
 



PL/SQL 테이블 예제 프로시져..

SQL>CREATE OR REPLACE PROCEDURE Table_Test
    (v_deptno IN emp.deptno%TYPE)

    IS

         -- 각 컬럼에서 사용할 테이블의 선언
         TYPE empno_table IS TABLE OF emp.empno%TYPE
         INDEX BY BINARY_INTEGER;

         TYPE ename_table IS TABLE OF emp.ename%TYPE
         INDEX BY BINARY_INTEGER;

         TYPE sal_table IS TABLE OF emp.sal%TYPE
         INDEX BY BINARY_INTEGER;

         -- 테이블타입으로 변수를 선언해서 사용
         empno_tab  empno_table ;
         
ename_tab  ename_table ;
         
sal_tab    sal_table;

         i BINARY_INTEGER := 0;

   BEGIN

         DBMS_OUTPUT.ENABLE;

         FOR emp_list IN(SELECT empno, ename, sal FROM emp WHERE deptno = v_deptno) LOOP

          /* emp_list는 자동선언되는 BINARY_INTEGER형 변수로 1씩 증가합니다.
           
emp_list대신 다른 문자열 사용가능 */

                i := i + 1;

               -- 테이블 변수에 검색된 결과를 넣습니다
                empno_tab(i) := emp_list.empno ;    
                ename_tab(i) := emp_list.ename ;
                sal_tab(i)   := emp_list.sal ;

          END LOOP;

          -- 1부터 i까지 FOR 문을 실행
          FOR cnt IN 1..i LOOP

             -- TABLE변수에 넣은 값을 뿌려줌
             DBMS_OUTPUT.PUT_LINE( '사원번호 : ' || empno_tab(cnt) );
             DBMS_OUTPUT.PUT_LINE( '사원이름 : ' || ename_tab(cnt) );
             DBMS_OUTPUT.PUT_LINE( '사원급여 : ' || sal_tab(cnt) );

          END LOOP;

  END;
  /

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

실행 결과

SQL> SET SERVEROUTPUT ON ; -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)
SQL> EXECUTE Table_Test(10);

사원번호 : 7782
사원이름 : CLARK
사원급여 : 2450
사원번호 : 7839
사원이름 : KING
사원급여 : 5000
사원번호 : 7934
사원이름 : MILLER
사원급여 : 1300

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

 emp 테이블에 있는 데이터의 입력한 부서에 해당하는 사원번호, 사원이름, 사원급여를
 뿌려주는 프로시져 입니다


PLSQL 레코드

여러개의 데이터 타입을 갖는 변수들의 집합입니다.

스칼라, RECORD, 또는 PL/SQL TABLE datatype중 하나 이상의 요소로 구성됩니다.

논리적 단위로서 필드 집합을 처리할 수 있도록 해 줍니다.

PL/SQL 테이블과 다르게 개별 필드의 이름을 부여할 수 있고, 선언시 초기화가 가능합니다.
 

예제

  TYPE record_test IS RECORD
    ( record_empno   NUMBER,
      record_ename   VARCHAR2(30),
      record_sal        NUMBER);

      prd_record    record_test;


PL/SQL RECORD 예제 프로시져..

SQL> CREATE OR REPLACE PROCEDURE Record_Test
      ( p_empno IN emp.empno%TYPE )

     IS

         -- 하나의 레코드의 세가지의 변수타입 선언

         TYPE emp_record IS RECORD
         (v_empno    NUMBER,
          v_ename    VARCHAR2(30),
          v_hiredate  DATE );

         emp_rec   emp_record ;

   BEGIN

         DBMS_OUTPUT.ENABLE;

         -- 레코드의 사용
         SELECT empno, ename, hiredate
         INTO emp_rec.v_empno, emp_rec.v_ename, emp_rec.v_hiredate
         FROM emp
         WHERE empno = p_empno;

        DBMS_OUTPUT.PUT_LINE( '사원번호 : ' || emp_rec.v_empno );
        DBMS_OUTPUT.PUT_LINE( '사원이름 : ' || emp_rec.v_ename );
        DBMS_OUTPUT.PUT_LINE( '입 사 일 : ' || emp_rec.v_hiredate );

   END;
 /


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


실행 결과
SQL> SET SERVEROUTPUT ON ;  
-- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

SQL> EXECUTE Record_Test(7369);

사원번호 : 7369
사원이름 : SMITH
입 사 일 : 80/12/17

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

 %ROWTYPE예제와 비교해 보세요


PL/SQL Table of Record

⊙ PL/SQL TABLE변수 선언과 비슷하며 데이터타입을 %ROWTYPE으로 선언하면 됩니다.
PL/SQL TABLE과 RECORD의 복합 기능을 합니다.

 



DECLARE

TYPE dept_table_type IS TABLE OF dept%ROWTYPE
INDEX BY BINARY_INTEGER;

-- Each element of dept_table  is a record
dept_table   dept_table_type ;
 



PL/SQL TABLE OF RECORD 예제 프로시져..

CREATE OR REPLACE PROCEDURE Table_Test
IS
        i BINARY_INTEGER := 0;
 
     -- PL/SQL Table of Record의 선언
    TYPE dept_table_type IS TABLE OF dept%ROWTYPE
     INDEX BY BINARY_INTEGER;
 
     dept_table dept_table_type;
 
BEGIN
 
 
    FOR dept_list IN (SELECT * FROM dept) LOOP
 
                i:= i+1;
 
        -- TABLE OF RECORD에 데이터 보관
        dept_table(i).deptno := dept_list.deptno ;    
        dept_table(i).dname := dept_list.dname ;
        dept_table(i).loc   := dept_list.loc ;
 
    END LOOP;
 
 
    FOR cnt IN 1..i LOOP
 
     -- 데이터 출력
     DBMS_OUTPUT.PUT_LINE( '부서번호 : ' || dept_table(cnt).deptno ||
                                          '부서명 : ' ||  dept_table(cnt).dname ||
                                          '위치 : ' || dept_table(cnt).loc );
 
    END LOOP;
 
 END;
/
 
 
SQL>set serveroutput on;
SQL>exec Table_test;
부서번호 : 10부서명 : ACCOUNTING위치 : NEW_YORK
부서번호 : 20부서명 : RESEARCH위치 : DALLAS
부서번호 : 30부서명 : 인사과위치 : CHICAGO
부서번호 : 40부서명 : OPERATIONS위치 : BOS%TON
 
PL/SQL 처리가 정상적으로 완료되었습니다.
 


INSERT

PL/SQL에서의 INSERT 문은 SQL과 비슷합니다.
 

사원 등록 예제 프로시져..

SQL> CREATE OR REPLACE PROCEDURE Insert_Test
        ( v_empno  IN emp.empno%TYPE,
          v_ename  IN emp.ename%TYPE,
          v_deptno IN emp.deptno%TYPE )

        IS

      BEGIN

          DBMS_OUTPUT.ENABLE;
   
          INSERT INTO emp(empno, ename, hiredate, deptno)
          VALUES(v_empno, v_ename, sysdate, v_deptno);

          DBMS_OUTPUT.PUT_LINE( '사원번호 : ' || v_empno );
          DBMS_OUTPUT.PUT_LINE( '사원이름 : ' || v_ename );
          DBMS_OUTPUT.PUT_LINE( '사원부서 : ' || v_deptno );
          DBMS_OUTPUT.PUT_LINE( '데이터 입력 성공 ' );

       END ;
      /           

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


실행 결과
SQL> SET SERVEROUTPUT ON ;  
-- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

SQL>  EXECUTE Insert_Test(1000, 'brave', 20);

사원번호 : 1000
사원이름 : brave
사원부서 : 20
데이터 입력 성공

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


UPDATE

상품 수정 예제 프로시저..
※ 특정 사원의 급여를 일정%센트 인상/인하하는 프로시져


SQL>CREATE OR REPLACE PROCEDURE Update_Test
       ( v_empno IN    emp.empno%TYPE,       -- 급여를 수정한 사원의 사번
         v_rate    IN    NUMBER )                     -- 급여의 인상/인하율

         IS

         -- 수정 데이터를 확인하기 위한 변수 선언
         v_emp  emp%ROWTYPE ;

         BEGIN

         DBMS_OUTPUT.ENABLE;

         UPDATE emp
         SET sal = sal+(sal * (v_rate/100))   -- 급여를 계산
         WHERE empno = v_empno ;

         DBMS_OUTPUT.PUT_LINE( '데이터 수정 성공 ' );

         -- 수정된 데이터 확인하기 위해 검색
         SELECT empno, ename, sal
         INTO v_emp.empno, v_emp.ename, v_emp.sal
         FROM emp
         WHERE empno = v_empno ;

         DBMS_OUTPUT.PUT_LINE( ' **** 수 정 확 인 **** ');
         DBMS_OUTPUT.PUT_LINE( '사원번호 : ' || v_emp.empno );
         DBMS_OUTPUT.PUT_LINE( '사원이름 : ' || v_emp.ename );
         DBMS_OUTPUT.PUT_LINE( '사원급여 : ' || v_emp.sal );

         END ;
         /

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

프로시저 실행
SQL> SET SERVEROUTPUT ON ;  -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

SQL> EXECUTE Update_Test(7900, -10);

데이터 수정 성공
**** 수 정 확 인 ****
사원번호 : 7900
사원이름 : JAMES
사원급여 : 855

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

7900번 사원의 급여를 10% 인하했습니다.


DELETE

사원 삭제 예제 프로시저..

SQL> CREATE OR REPLACE PROCEDURE Delete_Test
    ( p_empno IN  emp.empno%TYPE )

        IS

        -- 삭제 데이터를 확인하기 레코드 선언
        TYPE del_record IS  RECORD
        ( v_empno      emp.empno%TYPE,
          v_ename      emp.ename%TYPE,
          v_hiredate    emp.hiredate%TYPE) ;

          v_emp  del_record ;

        BEGIN

        DBMS_OUTPUT.ENABLE;

         -- 삭제된 데이터 확인용 쿼리
         SELECT empno, ename, hiredate
         INTO v_emp.v_empno, v_emp.v_ename, v_emp.v_hiredate
         FROM emp
         WHERE empno = p_empno ;

        DBMS_OUTPUT.PUT_LINE( '사원번호 : ' || v_emp.v_empno );
        DBMS_OUTPUT.PUT_LINE( '사원이름 : ' || v_emp.v_ename );
        DBMS_OUTPUT.PUT_LINE( '입 사 일 : ' || v_emp.v_hiredate );

        -- 삭제 쿼리
        DELETE
        FROM emp
        WHERE empno = p_empno ;

        DBMS_OUTPUT.PUT_LINE( '데이터 삭제 성공 ' );

       END;
 /

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


프로시저 실행 (결과화면)
SQL> SET SERVEROUTPUT ON ;  
-- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

SQL> EXECUTE Delete_Test(7900);
사원번호 : 7900
사원이름 : JAMES
입 사 일 : 81/12/03
데이터 삭제 성공

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

※ 7900사원을 삭제했습니다.


FOR LOOP

FOR LOOP문



  -  index 는 자동 선언되는 binary_integer 형 변수이고. 1씩 증가합니다.
  -  reverse 옵션이 사용될 경우 index 는 upper_bound에서 lower_bound로 1씩 감소합니다.
  -  IN 다음에는 coursor나 select 문이 올수 있습니다.



    FOR문 예제


    DECLARE

    -- 사원 이름을 출력하기 위한 PL/SQL 테이블 선언
    TYPE ename_table IS TABLE OF emp.ename%TYPE
    INDEX BY BINARY_INTEGER;

    -- 사원 급여를 출력하기 위한 PL/SQL 테이블 선언
    TYPE sal_table IS TABLE OF emp.sal%TYPE
    INDEX BY BINARY_INTEGER;

    ename_tab    ename_table;
    sal_tab      sal_table;

    i BINARY_INTEGER := 0;

    BEGIN

    DBMS_OUTPUT.ENABLE;

    FOR emp_list IN  (SELECT ename, sal FROM emp WHERE deptno = 10) LOOP

       i := i +1 ;

       ename_tab(i) := emp_list.ename;     -- 테이블에 상품 이름을 저장
       sal_tab(i)      := emp_list.sal;          -- 테이블에 상품 가격을 저장

    END LOOP;


    FOR cnt IN   1..i   LOOP         --  화면에 출력

       DBMS_OUTPUT.PUT_LINE('사원이름 : ' || ename_tab(cnt));
       DBMS_OUTPUT.PUT_LINE('사원급여 : ' || sal_tab(cnt));

    END LOOP;

    END;
    /



    사원이름 : CLARK
    사원급여 : 2450
    사원이름 : KING
    사원급여 : 5000
    사원이름 : MILLER
    사원급여 : 1300

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


LOOP문, WHILE문


EXIT 문이 사용되었을 경우, 무조건 LOOP문을 빠져나갑니다,

EXITH WHEN 이 사용될 경우  WHEN 절에 LOOP를 빠져 나가는 조건을 제어할수 있습니다.


LOOP 문 예제

SQL> SET SERVEROUTPUT ON ;  -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

SQL>  DECLARE

          v_cnt number(3) := 100;

          BEGIN

          DBMS_OUTPUT.ENABLE ;

          LOOP

              INSERT INTO emp(empno, ename , hiredate)
              VALUES(v_cnt, 'test'||to_char(v_cnt),  sysdate);

              v_cnt := v_cnt+1;

              EXIT WHEN v_cnt > 110;

          END LOOP;

          DBMS_OUTPUT.PUT_LINE('데이터 입력 완료');
          DBMS_OUTPUT.PUT_LINE(v_cnt-100 || '개의 데이터가 입력되었습니다');

          END;          
         /

데이터 입력 완료
11개의 데이터가 입력되었습니다

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


WHILE LOOP 문

    WHILE LOOP문은 FOR 문과 비슷하며 조건이 TRUE일 경우만 반복되는 LOOP문 입니다.

    예제

    WHILE cnt < 10 LOOP

       INSERT INTO emp(empno, ename , hiredate)
       VALUES(emp_seq.nextval, 'test',  sysdate);

    cnt := cnt + 1 ;

    END LOOP ;

    cnt가 10이면 반복 While Loop를 탈출

    EXIT WHEN
    조건 => 조건이 만족할 때 반복 loop를 탈출합니다. .


조건제어(IF)




IF문 예제 프로시저..

SQL>CREATE OR REPLACE PROCEDURE Dept_Search
(p_empno IN emp.empno%TYPE )

IS

v_deptno emp.deptno%type ;

BEGIN

DBMS_OUTPUT.ENABLE;

SELECT deptno
INTO v_deptno
FROM emp
WHERE empno = p_empno ;

IF v_deptno = 10 THEN

DBMS_OUTPUT.PUT_LINE( ' ACCOUNTING 부서 사원입니다. ' );

ELSIF v_deptno = 20 THEN

DBMS_OUTPUT.PUT_LINE( ' RESEARCH 부서 사원입니다. ' );

ELSIF v_deptno = 20 THEN

DBMS_OUTPUT.PUT_LINE( ' SALES 부서 사원입니다. ' );

ELSIF v_deptno = 20 THEN

DBMS_OUTPUT.PUT_LINE( ' OPERATIONS 부서 사원입니다. ' );

ELSE

DBMS_OUTPUT.PUT_LINE( ' 부서가 없네요... ' );

END IF ;

END ;
/


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

SQL> SET SERVEROUTPUT ON ; -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

프로시저 실행

SQL> EXECUTE Dept_Search(7900);

부서가 없네요...
PL/SQL 처리가 정상적으로 완료되었습니다.


SQL> EXECUTE Dept_Search(7369);

RESEARCH 부서 사원입니다.
PL/SQL 처리가 정상적으로 완료되었습니다.


암시적 커서(Implicit Cursor)


  암시적인 커서는 오라클이나 PL/SQL실행 메커니즘에 의해 처리되는 SQL문장이 처리되는 곳에 대한
  익명의 에드레스입니다. 오라클 데이터 베이스에서 실행되는 모든 SQL문장은 암시적인 커서이며
  그것들과 함께 모든 암시적인 커서 속성이 사용될 수 있습니다.

     -암시적 커서의 속성

    SQL%ROWCOUNT : 해당 SQL 문에 영향을 받는 행의 수

    SQL%FOUND : 해당 SQL 영향을 받는 행의 수가 1개 이상일 경우 TRUE

    SQL%NOTFOUND : 해당 SQL 문에 영향을 받는 행의 수가 없을 경우 TRUE

    SQL%ISOPEN : 항상 FALSE, 암시적 커서가 열려 있는지의 여부 검색

    (암시적 커서는 SQL 문이 실행되는 순간 자동으로 열림과 닫힘 실행)


    암시적 커서 예제

    CREATE OR REPLACE PROCEDURE Implicit_Cursor
    (p_empno emp.empno%TYPE)

    is

    v_sal  emp.sal%TYPE;
    v_update_row NUMBER;

    BEGIN

    SELECT sal
    INTO v_sal
    FROM emp
    WHERE empno = p_empno ;

    -- 검색된 데이터가 있을경우
    IF  SQL%FOUND THEN    

        DBMS_OUTPUT.PUT_LINE('검색한 데이터가 존재합니다 : '||v_sal);

    END IF;

    UPDATE emp
    SET sal = sal*1.1
    WHERE empno = p_empno;

    -- 수정한 데이터의 카운트를 변수에 저장
    v_update_row := SQL%ROWCOUNT;

    DBMS_OUTPUT.PUT_LINE('급여가 인상된 사원 수 : '|| v_update_row);

    END;

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


    SQL> SET SERVEROUTPUT ON ;  -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)
    SQL> EXECUTE Implicit_Cursor(7369);

    검색한 데이터가 존재합니다 : 880
    급여가 인상된 사원 수 : 1

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


Explicit Cursor


▣ 커서란 무엇인가?

커서는 Private SQL의 작업영역 입니다.
오라클 서버에 의해 실행되는 모든 SQL문은 연관된 각각의 커서를 소유하고 있습니다.
커서의 종류
   - 암시적 커서 : 모든 DML과 PL/SQL SELECT문에 대해 선언됩니다.
   - 명시적 커서 : 프로그래머에 의해 선언되며 이름이 있는 커서입니다.
 

▣ Explicit Cursor의 흐름도?

▣ 문법(Syntax)




▣ 커서 열기(OPEN)

커서의 열기는 OPEN문을 사용합니다.
커서안의 검색이 실행되며 아무런 데이터행을 추출하지 못해도 에러가 발생하지 않습니다.

    OPEN   cursor_name;


▣ 커서 패치(FETCH)

커서의 FETCH는 현재 데이터 행을 OUTPUT변수에 반환합니다.
커서의 SELECT문의 컬럼의 수와 OUTPUT변수의 수가 동일해야 합니다.
커서 컬럼의 변수의 타입과 OUTPUT변수의 데이터 타입도 동일해야 합니다.
커서는 한 라인씩 데이터를 패치 합니다.

    FETCH   cursor_name INTO variable1, variable2 ;


▣ 커서 닫기(CLOSE)

사용을 맞친 커서는 반드시 닫아 주어야 합니다.
필요하다면 커서를 다시 열 수 있습니다.
커서를 닫은 상태에서 FETCH를 할 수 없습니다.

    CLOSE   cursor_name;
 


Explicit Cursor 예제
특정 부서의 평균급여와 사원수를 출력..


FOR문에서 커서 사용(Cursor FOR Loops)


FOR문을 사용하면 커서의 OPEN, FETCH, CLOSE가 자동 발생하므로 따로 기술할
    필요가 없습니다
레코드 이름도 자동 선언되므로 따로 선언할 필요가 없습니다.


 

FOR문에서 커서 사용 예제
부서별 사원수와 급여 합계를 구하는 프로시저입니다.

SQL> CREATE OR REPLACE PROCEDURE ForCursor_Test
        IS

        CURSOR dept_sum IS
        SELECT b.dname, COUNT(a.empno) cnt, SUM(a.sal) salary
        FROM emp a, dept b
        WHERE a.deptno = b.deptno
        GROUP BY b.dname;

     BEGIN

       -- Cursor를 FOR문에서 실행시킨다
       FOR emp_list IN dept_sum LOOP

          DBMS_OUTPUT.PUT_LINE('부서명 : ' || emp_list.dname);
          DBMS_OUTPUT.PUT_LINE('사원수 : ' || emp_list.cnt);
          DBMS_OUTPUT.PUT_LINE('급여합계 : ' || emp_list.salary);

       END LOOP;

   EXCEPTION

       WHEN OTHERS THEN
          DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');

   END;
   /

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

실행 결과

SQL> SET SERVEROUTPUT ON ; -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

SQL> EXECUTE ForCursor_Test;
부서명 : ACCOUNTING
사원수 : 3
급여합계 : 8750
부서명 : RESEARCH
사원수 : 6
급여합계 : 10875
부서명 : SALES
사원수 : 6
급여합계 : 9305
 
PL/SQL 처리가 정상적으로 완료되었습니다.
 


명시적 커서의 속성(Explicit Cursor Attributes)

▣ Explicit Cursor 속성
 
%ISOPEN
      - 커서가 OPEN되어 있으면 TRUE
      -  %ISOPEN속성을 이용하여 커서가 열려있는지 알 수 있습니다.

%NOTFOUND
      - 패치한 데이터가 행을 반환하지 않으면 TRUE
      -  %NOTFOUND속성을 이용하여 루프를 종료할 시점을 찾습니다.

%FOUND
      - 패치한 데이터가 행을 반환하면 TRUE

%ROWCOUNT
     -
현재까지 반환된 모든 데이터 행의 수
      - %ROWCOUNT속성을 이용하여 정확한 숫자만큼의 행을 추출합니다.

 

커서의 속성 예제

SQL>CREATE OR REPLACE PROCEDURE AttrCursor_Test
       IS
 
       v_empno     emp.empno%TYPE;
       v_ename     emp.ename%TYPE;
       v_sal          emp.sal%TYPE;

      CURSOR emp_list IS
         SELECT empno, ename, sal
         FROM emp;    

 
    BEGIN


      DBMS_OUTPUT.ENABLE;

      OPEN emp_list;   

      LOOP    

        FETCH emp_list INTO v_empno, v_ename, v_sal;

           -- 데이터를 찾지 못하면 빠져 나갑니다
           EXIT WHEN emp_list%NOTFOUND;      
     
      END LOOP;    


       DBMS_OUTPUT.PUT_LINE('전체데이터 수 ' || emp_list%ROWCOUNT);
   

     CLOSE emp_list;
   
     EXCEPTION
   
       WHEN OTHERS THEN

         DBMS_OUTPUT.PUT_LINE('ERR MESSAGE : ' || SQLERRM);
       
    END;
   /



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

실행 결과

SQL> SET SERVEROUTPUT ON ; -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

SQL> EXECUTE AttrCursor_Test;
전체데이터 수 15

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


파라미터가 있는 커서(Cursors with Parameters)

커서가 열리고 질의가 실행되면 매개 변수 값을 커서에 전달한다.
다른 active set을 원할때 마다 explicit커서를 따로 선언해야 한다


▣ 문법(Syntax)

 


파라미터가 있는 커서 예제

SQL> CREATE OR REPLACE PROCEDURE ParamCursor_Test
        (param_deptno   emp.deptno%TYPE)
        IS

         v_ename     emp.ename%TYPE;

       -- Parameter가 있는 커서의 선언
        CURSOR emp_list(v_deptno emp.deptno%TYPE) IS
        SELECT ename
        FROM emp
        WHERE deptno = v_deptno;

       BEGIN

        DBMS_OUTPUT.ENABLE;
        DBMS_OUTPUT.PUT_LINE(' ****** 입력한 부서에 해당하는 사람들 ****** ');              

       -- Parameter변수의 값을 전달(OPEN될 때 값을 전달한다)
        FOR emplst IN emp_list(param_deptno) LOOP    

          DBMS_OUTPUT.PUT_LINE('이름 : ' || emplst.ename);

        END LOOP;    

        EXCEPTION    

          WHEN OTHERS THEN

             DBMS_OUTPUT.PUT_LINE('ERR MESSAGE : ' || SQLERRM);        

        END;
        /

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

실행 결과

SQL> SET SERVEROUTPUT ON ; -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

SQL> EXECUTE ParamCursor_Test(10);
****** 입력한 부서에 해당하는 사람들 ******
이름 : CLARK
이름 : KING
이름 : MILLER

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


The WHERE CURRENT OF Clause

WHERE CURRENT OF

   - ROWID를 이용하지 않고도 현재 참조하는 행을 갱신하고 삭제할 수 있게 합니다.
   - 추가적으로 FETCH문에 의해 가장 최근에 처리된 행을 참조하기 위해서
      "W
HERE CURRENT OF 커서이름 "    절로 DELETE나 UPDATE문 작성이 가능합니다..
   - 이 절을 사용할 때 참조하는 커서가 있어야 하며,  
     
FOR UPDATE절이 커서 선언 query문장 안에 있어야 합니다.
      그렇지 않으면 error가 발생합니다..

 

WHERE CURRENT OF 예제

SQL> SET SERVEROUTPUT ON ;  -- DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용

SQL>CREATE OR REPLACE PROCEDURE where_current
         IS

        CURSOR
emp_list IS
             SELECT empno
             FROM emp
             WHERE empno = 7934
            
FOR UPDATE;

      BEGIN

       
--DBMS_OUTPUT.PUT_LINE명령을 사용하기 위해서
        DBMS_OUTPUT.ENABLE;    

        FOR emplst IN emp_list LOOP
             
 --emp_list커서에 해당하는 사람의 직업을  SALESMAN으로 업데이트 시킵니다.
              UPDATE emp
              SET job = 'SALESMAN'
            
WHERE CURRENT OF emp_list;

            DBMS_OUTPUT.PUT_LINE('수정 성공');

        END LOOP;
 
        EXCEPTION
           WHEN OTHERS THEN
               
-- 에러 발생시 에러 메시지 출력
                DBMS_OUTPUT.PUT_LINE('ERR MESSAGE : ' || SQLERRM);
END;  
 

--먼저 데이터를 확인해 보세용
SQL> SELECT job FROM emp WHERE empno = 7934;

JOB
---------
CLERK

 --PLSQL을 실행시키고..
SQL> EXECUTE where_current;
수정 성공  
--DBMS_OUTPUT.PUT_LINE명령으로 출력한거..

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

-- 다시 데이터를 확인하면 변경된 것을 볼 수 있습니다.
SQL>  SELECT job FROM emp WHERE empno = 7934;
 
JOB
---------
SALESMAN


예외(Exception)



예외(Exception)란?

오라클 PL/SQL의 오류를 예외라고 부릅니다.

오류는 PL/SQL을 컴파일 할때 문법적인 오류로 발생하는 컴파일 타임 오류와,
    프로그램을 실행할때 발생하는 실행타임 오류로 구분할수 있습니다.
 
 



PL/SQL오류의 종류

 예  외

 설  명

 처  리


   미리 정의된 오라클 서버 오류
(Predefined Oracle Server)
 

  PL/SQL에서 자주 발생하는
  약20개의 오류

  선언할 필요도 없고, 발생시에
예외 절로 자동 트랩(Trap)
된다.


  미리 정의되지 않은 오라클
  서버 오류

  (Non-Predefined Oracle   Server)
 

  미리 정의된 오라클 서버
오류를 제외한
모든 오류

  선언부에서 선언해야 하고 발생시
  자동 트랩된다.


  사용자 정의 오류
  (User-Defined)
 

  개발자가 정한 조건에
만족하지 않을경우
발생하는 오류

   선언부에서 선언하고 실행부에서
   RAISE문을 사용하여 발생시켜야
   한다



Execption 문법(Syntax)
WHEN OTHERS절은 맨 마지막에 옵니다.

예외 처리절은 EXCEPTION부터 시작합니다.

허용합니다.

예외가 발생하면 여러 개의 예외 처리부 중에 하나의 예외 처리부에 트랩(Trap)됩니다.


 


미리 정의된 예외(Predefined Exceptions)

  오라클 PL/SQL은 자주 일어나는 몇가지 예외를 미리 정의해 놓았으며,
      이러한 예외는 개발자가 따로 선언할 필요가 없습니다
 

미리 정의된 예외의 종류?

NO_DATA_FOUND : SELECT문이 아무런 데이터 행을 반환하지 못할때

TOO_MANY_ROWS : 하나만 리턴해야하는 SELECT문이 하나 이상의 행을 반환할 때

INVALID_CURSOR : 잘못된 커서 연산

ZERO_DIVIDE : 0으로 나눌때

DUP_VAL_ON_INDEX : UNIQUE 제약을 갖는 컬럼에 중복되는 데이터가 INSERT될때

   이 외에도 몇 개가 더 있습니다.

미리 정의된 예외 예제

SQL> CREATE OR REPLACE PROCEDURE PreException_test
         (v_deptno  IN emp.empno%TYPE)  
   
   IS

       v_emp   emp%ROWTYPE;

   BEGIN

      DBMS_OUTPUT.ENABLE;

      SELECT empno, ename, deptno
      INTO v_emp.empno, v_emp.ename, v_emp.deptno
      FROM emp
      WHERE deptno = v_deptno ;

      DBMS_OUTPUT.PUT_LINE('사번 : ' || v_emp.empno);
      DBMS_OUTPUT.PUT_LINE('이름 : ' || v_emp.ename);
      DBMS_OUTPUT.PUT_LINE('부서번호 : ' || v_emp.deptno);

   EXCEPTION

      WHEN   DUP_VAL_ON_INDEX   THEN
   
          DBMS_OUTPUT.PUT_LINE('데이터가 존재 합니다.');
          DBMS_OUTPUT.PUT_LINE('DUP_VAL_ON_INDEX 에러 발생');

      WHEN   TOO_MANY_ROWS   THEN  

        DBMS_OUTPUT.PUT_LINE('TOO_MANY_ROWS에러 발생');

      WHEN   NO_DATA_FOUND   THEN  

        DBMS_OUTPUT.PUT_LINE('NO_DATA_FOUND에러 발생');

      WHEN   OTHERS   THEN  

        DBMS_OUTPUT.PUT_LINE('기타 에러 발생');

  END;
  /

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




프로시저 실행

SQL> SET SERVEROUTPUT ON ;  -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

SQL> EXECUTE PreException_Test(20);
TOO_MANY_ROWS에러 발생

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


 TOO_MANY_ROWS에러를 타는 이유?

 -
SELECT문의 결과가 1개 이상의 행을 리턴하기 때문이다..
 - TOO_MANY_ROWS를 피하기 위해서는 FOR문이나 LOOP문으로 SELECT문을 처리해야 합니다.

아래와 같이 바꾸면 에러가 발생하지 않습니다.


   
 FOR  emp_list  IN
         
(SELECT empno, ename, deptno
         FROM emp
         WHERE deptno = v_deptno)
  LOOP

         DBMS_OUTPUT.PUT_LINE('사번 : ' || emp_list.empno);
         DBMS_OUTPUT.PUT_LINE('이름 : ' || emp_list.ename);
         DBMS_OUTPUT.PUT_LINE('부서번호 : ' || emp_list.deptno);
 
     END LOOP;
 


미리 정의되지 않은 예외(Non-Predefined Exception)


 

STEP 1 : 예외의 이름을 선언(선언절)

STEP 2 : PRAGMA EXCEPTION_INIT문장으로 예외의 이름과 오라클 서버
                  오류 번호를 결합(선언절)

STEP 3 : 예외가 발생할 경우 해당 예외를 참조한다(예외절)
 

리 정의되지 않은 예외 예제

SQL> CREATE OR REPLACE PROCEDURE NonPreException_Test
       IS

          not_null_test EXCEPTION;    -- STEP 1

          /* not_null_test는 선언된 예외 이름
             -1400
Error 처리번호는 표준 Oracle7 Server Error 번호 */
          PRAGMA EXCEPTION_INIT(not_null_test, -1400);       -- STEP 2

        BEGIN

          DBMS_OUTPUT.ENABLE;

        -- empno를 입력하지 않아서 NOT NULL 에러 발생
        INSERT INTO emp(ename, deptno)
        VALUES('tiger', 30);

        EXCEPTION

        WHEN not_null_test THEN    -- STEP 3

            DBMS_OUTPUT.PUT_LINE('not null 에러 발생 ');

       END;
        /

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

실행 결과

SQL> SET SERVEROUTPUT ON ;    -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

SQL> EXECUTE NonPreException_Test;
not null 에러 발생

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


사용자 정의 예외(User-Defined Exceptions)


 오라클 저장함수 RAISE_APPLICATION_ERROR를 사용하여 오류코드 -20000부터
   -20999의 범위 내에서 사용자 정의 예외를 만들수 있습니다.


 

STEP 1 : 예외의 이름을 선언(선언절)

STEP 2 : RAISE문을 사용하여 직접적으로 예외를 발생시킨다(실행절)

STEP 3 : 예외가 발생할 경우 해당 예외를 참조한다(예외절)
 

사용자 정의 예외 예제 Procedure

입력한 부서의 사원이 5명보다 적으면 사용자 정의 예외가 발생하는 예제 입니다.

SQL>CREATE OR REPLACE PROCEDURE User_Exception
        (v_deptno IN emp.deptno%type )
      IS

       -- 예외의 이름을 선언
       user_define_error EXCEPTION;     -- STEP 1
       cnt     NUMBER;

     BEGIN

       DBMS_OUTPUT.ENABLE;  

       SELECT COUNT(empno)
       INTO cnt
       FROM emp
       WHERE deptno = v_deptno;

       IF cnt < 5 THEN
         -- RAISE문을 사용하여 직접적으로 예외를 발생시킨다
          RAISE user_define_error;         -- STEP 2
       END IF;

      EXCEPTION
        -- 예외가 발생할 경우 해당 예외를 참조한다.
       WHEN user_define_error THEN      -- STEP 3
           RAISE_APPLICATION_ERROR(-20001, '부서에 사원이 몇명 안되네요..');

   END;
  /


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

실행 결과
SQL> SET SERVEROUTPUT ON ; -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

SQL> EXECUTE user_exception(10);
BEGIN user_exception(10); END;
 *
1행에 오류:
ORA-20001: 부서에 사원이 몇명 안되네요..
ORA-06512: "SCOTT.USER_EXCEPTION", 줄 17에서
ORA-06512: 줄 1에서

 10부서의 사원이 5보다 적기 때문에 사용자 정의 예외가 발생했습니다.

SQL> EXECUTE user_exception(20);
PL/SQL 처리가 정상적으로 완료되었습니다. 

20부서로 실행을 하면 에러가 발생하지 않는 것 을 알 수 있습니다..


사용자 정의 예외(User-Defined Exceptions)


 오라클 저장함수 RAISE_APPLICATION_ERROR를 사용하여 오류코드 -20000부터
   -20999의 범위 내에서 사용자 정의 예외를 만들수 있습니다.


 

STEP 1 : 예외의 이름을 선언(선언절)

STEP 2 : RAISE문을 사용하여 직접적으로 예외를 발생시킨다(실행절)

STEP 3 : 예외가 발생할 경우 해당 예외를 참조한다(예외절)
 

사용자 정의 예외 예제 Procedure

입력한 부서의 사원이 5명보다 적으면 사용자 정의 예외가 발생하는 예제 입니다.

SQL>CREATE OR REPLACE PROCEDURE User_Exception
        (v_deptno IN emp.deptno%type )
      IS

       -- 예외의 이름을 선언
       user_define_error EXCEPTION;     -- STEP 1
       cnt     NUMBER;

     BEGIN

       DBMS_OUTPUT.ENABLE;  

       SELECT COUNT(empno)
       INTO cnt
       FROM emp
       WHERE deptno = v_deptno;

       IF cnt < 5 THEN
         -- RAISE문을 사용하여 직접적으로 예외를 발생시킨다
          RAISE user_define_error;         -- STEP 2
       END IF;

      EXCEPTION
        -- 예외가 발생할 경우 해당 예외를 참조한다.
       WHEN user_define_error THEN      -- STEP 3
           RAISE_APPLICATION_ERROR(-20001, '부서에 사원이 몇명 안되네요..');

   END;
  /


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

실행 결과
SQL> SET SERVEROUTPUT ON ; -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

SQL> EXECUTE user_exception(10);
BEGIN user_exception(10); END;
 *
1행에 오류:
ORA-20001: 부서에 사원이 몇명 안되네요..
ORA-06512: "SCOTT.USER_EXCEPTION", 줄 17에서
ORA-06512: 줄 1에서

 10부서의 사원이 5보다 적기 때문에 사용자 정의 예외가 발생했습니다.

SQL> EXECUTE user_exception(20);
PL/SQL 처리가 정상적으로 완료되었습니다. 

20부서로 실행을 하면 에러가 발생하지 않는 것 을 알 수 있습니다..


Package(패키지)

package?

   패키지(package)는 오라클 데이터베이스에 저장되어 있는 서로 관련있는 PL/SQL
      프로지져와 함수들의 집합 입니다

   패키지는 선언부와 본문 두 부분으로 나누어 집니다.


패키지 선언부

- 선언절은 패키지에 포함될 PL/SQL 프로시저나, 함수, 커서, 변수, 예외절을 선언 합니다.
- 패키지 선언부에서 선언한 모든 요소들은 패키지 전체에 적용됩니다.
- 즉 선언부에서 선언한 변수는 PUBLIC 변수로 사용 됩니다.



 


패키지 본문

- 패키지 본문은 패키지에서 선언된 부분의 실행을 정의 합니다.
- 즉 실재 프로시져나 함수의 내용에 해당하는 부분이 옵니다.



 


아주 간단한 패키지 예제입니다.

4개의 프로시저가 존재하고 있습니다.

프로시저명

프로시저 기능

all_emp_info

  모든 사원의  사원 정보 (사번, 성명, 입사일)

all_sal_info

  모든 사원의  급여 정보 (평균급여, 최고급여, 최소급여)

dept_emp_info

  특정 부서의  사원 정보 (사번, 성명, 입사일)

dept_sql_info

  특정 부서의  급여 정보 (평균급여, 최고급여, 최소급여)

위 4개의 프로시저를 가지고 패키지를 생성하겠습니다.


all_sal_info

  1. 모든 사원의  사원 정보(사번, 성명, 입사일)를 보여 주는 프로시져

SQL> CREATE OR REPLACE PROCEDURE all_emp_info
          IS
       
            CURSOR emp_cursor IS
            SELECT empno, ename, to_char(hiredate, 'RRRR/MM/DD') hiredate
            FROM emp
            ORDER BY hiredate;
       
          BEGIN
       
            FOR  aa  IN emp_cursor LOOP
       
                DBMS_OUTPUT.PUT_LINE('사번 : ' || aa.empno);
                DBMS_OUTPUT.PUT_LINE('성명 : ' || aa.ename);
                DBMS_OUTPUT.PUT_LINE('입사일 : ' || aa.hiredate);
       
            END LOOP;
       
          EXCEPTION
                WHEN OTHERS THEN
                        DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');
       
          END;

Procedure created.

all_sal_info

  2. 모든 사원의  급여 정보 (평균급여, 최고급여, 최소급여)

SQL>CREATE OR REPLACE PROCEDURE all_sal_info
        IS
               
            CURSOR emp_cursor IS
            SELECT round(avg(sal),3) avg_sal, max(sal) max_sal, min(sal) min_sal
            FROM emp;
               
        BEGIN
       
            FOR  aa  IN emp_cursor LOOP
               
                DBMS_OUTPUT.PUT_LINE('전체급여평균 : ' || aa.avg_sal);
                DBMS_OUTPUT.PUT_LINE('최대급여금액 : ' || aa.max_sal);
                DBMS_OUTPUT.PUT_LINE('최소급여금액 : ' || aa.min_sal);
                       
            END LOOP;
       
       
        EXCEPTION
            WHEN OTHERS THEN
                DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');
        END;
/

Procedure created.    

dept_emp_info

  3. 특정 부서의  사원 정보 (사번, 성명, 입사일)

SQL> CREATE OR REPLACE PROCEDURE dept_emp_info
          (v_deptno IN  NUMBER)
          IS
       
                CURSOR emp_cursor IS
                SELECT empno, ename, to_char(hiredate, 'RRRR/MM/DD') hiredate
                FROM emp
                WHERE deptno = v_deptno
                ORDER BY hiredate;
       
          BEGIN
       
            FOR  aa  IN emp_cursor LOOP
       
                DBMS_OUTPUT.PUT_LINE('사번 : ' || aa.empno);
                DBMS_OUTPUT.PUT_LINE('성명 : ' || aa.ename);
                DBMS_OUTPUT.PUT_LINE('입사일 : ' || aa.hiredate);
       
            END LOOP;
       
          EXCEPTION
            WHEN OTHERS THEN
                DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');

          END;

dept_sql_info

  4. 특정 부서의  급여 정보 (평균급여, 최고급여, 최소급여)  

SQL>CREATE OR REPLACE PROCEDURE dept_sal_info
          (v_deptno IN  NUMBER)
        IS
               
            CURSOR emp_cursor IS
            SELECT round(avg(sal),3) avg_sal, max(sal) max_sal, min(sal) min_sal
            FROM emp
            WHERE deptno = v_deptno;
               
               
        BEGIN
       
            FOR  aa  IN emp_cursor LOOP
               
                DBMS_OUTPUT.PUT_LINE('전체급여평균 : ' || aa.avg_sal);
                DBMS_OUTPUT.PUT_LINE('최대급여금액 : ' || aa.max_sal);
                DBMS_OUTPUT.PUT_LINE('최소급여금액 : ' || aa.min_sal);
                       
            END LOOP;
       
       
        EXCEPTION
            WHEN OTHERS THEN
                DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');
        END;      


선언부를 먼저 생성 합니다.

package 예제
(선언부)

SQL>CREATE OR REPLACE PACKAGE emp_info AS

            PROCEDURE all_emp_info;                                           -- 모든 사원의  사원 정보

            PROCEDURE all_sal_info;                                             -- 모든 사원의  급여 정보

            PROCEDURE dept_emp_info (v_deptno IN  NUMBER) ;     -- 특정 부서의  사원 정보

            PROCEDURE dept_sal_info (v_deptno IN  NUMBER) ;       -- 특정 부서의  급여 정보       

        END emp_info;

Package created.


선언부를 생성 하고 나서 본문 부분을 생성 합니다.

package 예제
(본문)

SQL>
CREATE OR REPLACE PACKAGE BODY emp_info AS

               
-- 모든 사원의  사원 정보
               
PROCEDURE all_emp_info
                IS        

                        CURSOR emp_cursor IS
                        SELECT empno, ename, to_char(hiredate, 'RRRR/MM/DD') hiredate
                        FROM emp
                        ORDER BY hiredate;
       
                BEGIN
       
                        FOR  aa  IN emp_cursor LOOP
       
                                DBMS_OUTPUT.PUT_LINE('사번 : ' || aa.empno);
                                DBMS_OUTPUT.PUT_LINE('성명 : ' || aa.ename);
                                DBMS_OUTPUT.PUT_LINE('입사일 : ' || aa.hiredate);
       
                        END LOOP;
       
                EXCEPTION
                        WHEN OTHERS THEN
                                DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');
       
                
END all_emp_info;
 

 
               
 -- 모든 사원의  급여 정보
               
PROCEDURE all_sal_info
                IS
               
                        CURSOR emp_cursor IS
                        SELECT round(avg(sal),3) avg_sal, max(sal) max_sal, min(sal) min_sal
                        FROM emp;
               
                BEGIN
       
                        FOR  aa  IN emp_cursor LOOP
               
                                DBMS_OUTPUT.PUT_LINE('전체급여평균 : ' || aa.avg_sal);
                                DBMS_OUTPUT.PUT_LINE('최대급여금액 : ' || aa.max_sal);
                                DBMS_OUTPUT.PUT_LINE('최소급여금액 : ' || aa.min_sal);
                       
                        END LOOP;
       
       
                EXCEPTION
                        WHEN OTHERS THEN
                                DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');
               
END all_sal_info;
 

 
               
--특정 부서의  사원 정보
               
PROCEDURE dept_emp_info (v_deptno IN  NUMBER)
                IS
       
                        CURSOR emp_cursor IS
                        SELECT empno, ename, to_char(hiredate, 'RRRR/MM/DD') hiredate
                        FROM emp
                        WHERE deptno = v_deptno
                        ORDER BY hiredate;
       
                BEGIN
       
                        FOR  aa  IN emp_cursor LOOP
       
                                DBMS_OUTPUT.PUT_LINE('사번 : ' || aa.empno);
                                DBMS_OUTPUT.PUT_LINE('성명 : ' || aa.ename);
                                DBMS_OUTPUT.PUT_LINE('입사일 : ' || aa.hiredate);
       
                        END LOOP;
       
                EXCEPTION
                        WHEN OTHERS THEN
                                DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');
 
              
 END dept_emp_info;
 
 
               
--특정 부서의  급여 정보
               
PROCEDURE dept_sal_info (v_deptno IN  NUMBER)
                IS
               
                        CURSOR emp_cursor IS
                        SELECT round(avg(sal),3) avg_sal, max(sal) max_sal, min(sal) min_sal
                        FROM emp
                        WHERE deptno = v_deptno;
               
               
                BEGIN

                        FOR  aa  IN emp_cursor LOOP
               
                                DBMS_OUTPUT.PUT_LINE('전체급여평균 : ' || aa.avg_sal);
                                DBMS_OUTPUT.PUT_LINE('최대급여금액 : ' || aa.max_sal);
                                DBMS_OUTPUT.PUT_LINE('최소급여금액 : ' || aa.min_sal);
                       
                        END LOOP;
       
       
                EXCEPTION
                        WHEN OTHERS THEN
                                DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');

               
END dept_sal_info;        
               
       
END emp_info;
         /
 
 Package body created.

 



패키지의 실행

패키지의 실행은 패키지 명 다음에 .을 찍고 프로시저냐 함수 명을 써 줍니다.


먼저 set serveroutput on을 실행한후..
SQL> SET SERVEROUTPUT ON ; -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)


다음 명령들을 실행해 보세요..
SQL> exec emp_info.all_emp_info;

SQL> exec emp_info.all_sal_info;

SQL> exec emp_info.dept_emp_info(10);

SQL> exec emp_info.dept_sal_info(10);


Trigger(트리거)

트리거란?

INSERT, UPDATE, DELETE문이 TABLE에 대해 행해질 때
묵시적으로 수행되는 PROCEDURE 입니다.

Trigger는 TABLE과는 별도로 DATABASE에 저장됩니다.
Trigger는 VIEW에 대해서가 아니라 TABLE에 관해서만 정의될 수 있습니다.





- BEFORE : INSERT, UPDATE, DELETE문이 실행되기 전에 트리거가 실행됩니다.
- AFTER : INSERT, UPDATE, DELETE문이 실행된 후 트리거가 실행됩니다.
- trigger_event : INSERT, UPDATE, DELETE 중에서 한 개 이상 올 수 있습니다.
- FOR EACH ROW : 이 옵션이 있으면 행 트리거가 됩니다.

-- 행 트리거 : 컬럼의 각각의 행의 데이터 행 변화가 생길때마다 실행되며,
그 데이터 행의 실제값을 제어할수 있습니다.
-- 문장 트리거 : 트리거 사건에 의해 단 한번 실행되며, 컬럼의 각 데이터 행을 제어할수 없습니다.

간단한 행 트리거 예제

SQL>CREATE OR REPLACE TRIGGER triger_test
BEFORE
UPDATE ON dept
FOR EACH ROW
BEGIN
DBMS_OUTPUT.PUT_LINE('변경 전 컬럼 값 : ' || : old.dname);
DBMS_OUTPUT.PUT_LINE('변경 후 컬럼 값 : ' || : new.dname);
END;
/


SQL> SET SERVEROUTPUT ON ; -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

-- UPDATE문을 실행시키면..
SQL>UPDATE dept
SET dname = '총무부'
WHERE deptno = 30

-- 트리거가 자동 실행되어 결과가 출력됩니당.
변경 전 컬럼 값 : 인사과
변경 후 컬럼 값 : 총무부

1 행이 갱신되었습니다.

간단한 행 트리거 예제2 (PLSQL BLOCK이 있는 트리거)

SQL>CREATE OR REPLACE trigger sum_trigger
BEFORE
INSERT OR UPDATE ON emp
FOR EACH ROW

DECLARE

-- 변수를 선언할 때는 DECLARE문을 사용해야 합니다
avg_sal NUMBER;

BEGIN

SELECT ROUND(AVG(sal),3)
INTO avg_sal
FROM emp;

DBMS_OUTPUT.PUT_LINE('급여 평균 : ' || avg_sal);

END;
/

트리거가 생성되었습니다.

SQL> SET SERVEROUTPUT ON ; -- (DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용)

-- INSERT문을 실행합니다..

SQL> INSERT INTO EMP(EMPNO, ENAME, JOB, HIREDATE, SAL)
VALUES(1000, 'LION', 'SALES', SYSDATE, 5000);

-- INSERT문을 실행되기 전까지의 급여 평균이 출력됩니다.
급여 평균 : 2073.214

1 개의 행이 만들어졌습니다.

출처 : http://www.oracleclub.com/

'프로그래밍 > DB' 카테고리의 다른 글

[펌] 계정 생성 및 권한부여  (0) 2007.11.27
직렬화된 객체를 Oracle BLOB에 저장하기  (0) 2007.11.27
[펌] PL/SQL 조건문 만들기  (0) 2007.11.27
[펌] 오라클자료 - PL/SQL  (0) 2007.11.27
[펌] PL-SQL 정리  (0) 2007.11.27

[펌] PL/SQL 조건문 만들기

프로그래밍/DB 2007. 11. 27. 14:33 Posted by galad
출처 블로그 > 나의 컴 메모
원본 http://blog.naver.com/nukiboy/40007931468

CREATE OR REPLACE
FUNCTION CF_TO_HEXA
(
    DECI NUMBER
) RETURN CHAR IS

V_RET CHAR;

BEGIN
    IF DECI = 10 THEN
        V_RET := 'A';
    ELSIF DECI = 11 THEN
        V_RET := 'B';
    ELSIF DECI = 12 THEN
        V_RET := 'C';
    ELSIF DECI = 13 THEN
        V_RET := 'D';
    ELSIF DECI = 14 THEN
        V_RET := 'E';
    ELSIF DECI = 15 THEN
        V_RET := 'F';
    ELSE
        V_RET := TO_CHAR(DECI);
    END IF;
    RETURN V_RET;
END;

'프로그래밍 > DB' 카테고리의 다른 글

직렬화된 객체를 Oracle BLOB에 저장하기  (0) 2007.11.27
[펌] Oracle - PL/SQL  (0) 2007.11.27
[펌] 오라클자료 - PL/SQL  (0) 2007.11.27
[펌] PL-SQL 정리  (0) 2007.11.27
select * from user_catalog;  (0) 2007.11.27

[펌] 오라클자료 - PL/SQL

프로그래밍/DB 2007. 11. 27. 14:32 Posted by galad
출처 블로그 > 하루...
원본 http://blog.naver.com/dewlast/140001355787

PL/SQL REFERENCE MANUAL  (출처 : 오라클)

제 13장 PL/SQL

SQL은 국제표준화 기구들에 의해서 최소한의 표준 문법이 정의되어 있고 DBMS를 제공하는 각 벤더들은 이 표준을 따르고 있으며 부가적인 명령을 추가하여 SQL 제품을 출시하고 있다. 이러한 SQL은 절차적인(procedural) 언어라고 하는 BASIC, COBOL, C, Visual BASIC 등과 달리 비절차적인 구조로 구성이 되어 있다. 절차적인 언어들은 if, for, while 등과 같은 일련의 명령어들을 조합해서 데이터들을 가공하고 제어할 수 있는 반면, 비절차적인 언어인 SQL은 하나의 명령어 단위로 데이터를 관리하고 있는 DBMS에게 데이터를 요청하고, 변경 의뢰하는 방식으로 운영되기 때문에 여러개의 SQL 명령을 조합해서 하나의 프로그램으로 구성할 수 없다는 제한사항이 있다.

이와 같은 SQL의 제한사항을 해소하는 일반적인 방법으로 Visual BASIC으로 작성된 프로그램 내에서 SQL 명령어로 DBMS에게 데이터를 요청해서 읽혀진 데이터들을 Visual BASIC 명령어로 가공하여 사용하는 Client/Server 방식을 사용하게 된다. 이러한 방법은 비절차적인 언어인 Visual BASIC으로 작성된 프로그램 내에서 비절차적인 SQL 명령을 사용하여 데이터를 읽어들인 다음 Visual BASIC 명령어로 데이터를 표현하거나 가공함으로서 Client는 데이터를 요청, 가공하는 역할을 하게되고, Server는 데이터를 제공하고 유지하는 역할을 하게 된다.

하지만, 경우에 따라서 일련의 SQL 명령어들을 if, for, while 등과 같은 절차적인 언어에서 사용하는 명령어와 결합하여 함수나 프로그램을 구성해서 이것을 DBMS가 처리해 줄 수 있다면 SQL의 효율성이 높일 수 있을 것이다.

PL/SQL(Procedural Language/SQL)은 SQL의 명령어들을 대부분 그대로 사용하면서 if, for, while 등과 같은 비절차적인 언어 등을 추가하여 절차적인 프로그래밍이 가능하도록 SQL을 확장시킨 오라클사의 고유 제품이다.


13.1 PL/SQL 개요

PL/SQL로 작성된 프로그램에는 비절차적인 SQL 명령어와 절차적인 명령어들이 함께 존재하게 됨으로 PL/SQL 엔진은 그림 13-1과 같이 SQL 명령어와 Non-SQL 명령어를 따로 분리되어 SQL 명령어는 SQL 엔진으로 처리되고 Non-SQL 명령어는 PL/SQL 엔진에서 처리된다.


PL/SQL로 작성된 프로그램이 이름 없이 스크립트(script) 형태로 작성되었다면 오라클 DBMS에 의해 실행만 가능한 반면, 특정한 이름으로 저장하게 되면 하나의 오라클 개체로서 DBMS에 의해서 관리되므로 언제든지 DBMS에게 요청하여 실행시킬 수 있다.

PL/SQL은 명령어를 사용하면서 절차적인 프로그램이 가능하도록 지원함으로서 다음과 같은 몇 가지 장점을 제공한다.

◈ 모듈화된 프로그램 개발

º 자주 사용되는 기능을 함수로 구성하여 다른 프로그램에서 실행

º 블록 내에서 논리적으로 관련된 문장들의 그룹화

º 강력한 프로그램을 작성하기 위해 서브 블록들을 큰 블록에 포함

º 복잡한 문제에 대한 프로그래밍이 적절히 나뉘어진 모듈들의 집합으로 구성

◈ 절차적 언어 구조로 된 프로그램 작성

º 조건에 따라 일련의 문장을 실행 (IF)

º 루프에서 반복적으로 일련의 문장을 실행 (LOOP)

◈ CURSOR

º Cursor를 이용한 Multi-row 질의와 개별 row에 대한 처리

º 데이터베이스의 테이블과 Record를 기반으로 하는 dynamic한 변수 선언이 가능

◈ EXCEPTION, ERROR 처리

º Exception 처리 루틴을 이용하여 오라클 서버 에러를 처리

º 사용자 정의 에러를 선언하고 Exception 처리 루틴으로 처리

◈ TRIGGER 처리

º 특정 사건(event) 이전 또는 이후에 지정한 일련의 특정 명령이 자동 실행

PL/SQL의 주요한 장점 중에 하나는 함수 프로그램을 작성하여 오라클 개체로 저장시켜 놓고 다른 프로그램에서 명령어처럼 실행할 수 있어서 프로그램을 단순화시킬 수 있고 Client/Server 환경에서는 Client에서는 Server에 PL/SQL로 작성된 개체명만 호출하면 됨으로 네트워크의 통신량을 줄일 수 있다. 또한, DBMS에 의해서 개별적인 개체로 관리되기 때문에 사용 권한에 대한 제어가 용이하다. 그리고, 커서(cursor) 기능을 사용하여 복수개의 행을 읽은 후에 하나씩 패치(fetch)하여 처리할 수 있으며, 예외(Exception) 기능을 이용하여 프로그램 내에서 예외적인 상황이 발생하면 프로그램이 비정상적으로 종료되지 않고 착오 사항을 인지하여 지정한 일련을 명령을 실행시킬 수 있다. Trigger 기능은 테이블에 대해서 변경이 이루어지는 이전 또는 이후를 자동적으로 인지하여 지정한 일련의 명령어들을 처리할 수 있게 한다.

13.2 PL/SQL 블록

PL/SQL 블록 구조는 3개 부분으로 나누어 정의된다. 첫 번째가 선언(declare) 부로 실행부에서 사용할 모든 변수와 상수 등을 선언하는 부분이다. 선언부는 정의하지 않아도 되는 부분이다. 두 번째는 블록의 핵심 부분인 실행부로서 데이터 처리를 위한 SQL 명령문과 PL/SQL 제어문이 포함되는 부분이다. 실행부는 반드시 명시되어야 하는 부분이다. 끝으로 예외(exception)부로 실행부분에서 Error나 비정상적인 조건이 발생했을 때 처리할 내용을 명시하는 부분이다.

부 분

기술 내용

DECLARE

(선언부)

⼘선택적으로 명시

⼘변수, 상수 선언

⼘Cursor, 사용자가 선언할 Exception

BEGIN

(실행부)

⼘반드시 명시

⼘SQL과 PL/SQL 제어문

EXCEPTION

(예외 처리부)

⼘선택적으로 명시

⼘Error 또는 비정상조건 발생시 실행할 내용

END

⼘반드시 명시

표 13- 1 PL/SQL 블록 구성


13.2.1 변수 선언

DECLARE 절에서의 변수 선언은 기본적으로 변수명과 데이터형을 정의하고 문장 끝에 세미콜론(;)으로 종료시켜 주면 된다. 기타 선택적으로 변수에 대한 초기 값을 지정하기 위해서는 데이터형 뒤에 " := 10" 형태로 값을 지정하거나 산술식을 명시하면 된다. 만약 초기 값을 상수로 사용하길 원한다면 변수명 뒤에 CONSTANT라고 기술해주면 된다.

PL/SQL에서 사용하는 데이터형은 기본적으로 테이블 생성시 선언하는 데이터형을 똑같이 사용하고 있으며, 추가적으로 %TYPE 속성, %ROWTYPE 속성을 사용한다. 기본적인 데이터형을 사용하는 예는 표 13-2와 같고 %TYPE 속성, %ROWTYPE 속성을 사용하는 예는 표 13-3과 같다.

DECLARE 절의 변수 선언에 대한 문법은 다음과 같다.

구문법

Identifier [CONSTANT] Datatype [NOT NULL]

[:= Value| DEFAULT 수식];

Identifier

변수나 상수 명

CONSTANT

상수로 사용할 경우 명시

Datatype

데이터 형식

Value

초기 값

DECLARE

h_name CHAR(10) NOT NULL;

rec_count NUMBER(3) := 0;

score_average NUMBER(4,1);

tax_rate CONSTANT NUMBER(3,1) := 10.0;

in_date DATE := SYSDATE + 7;

val_valid BOOLEAN NOT NULL := TRUE;

표 13-2 변수 선언 예

PL/SQL에서는 변수를 선언할 때 이미 선언된 변수나 특정 테이블의 칼럼이 갖는 데이터형을 상속받아 선언할 수 있다. 이렇게 하기 위해 %TYPE 속성을 이용한다.

표 13-3 예에서와 같이 %TYPE 속성은 상속받을 변수나 칼럼명 바로 뒤에 %TYPE라고 명시하게되면 해당 변수나 칼럼의 데이터형을 상속받게 된다. 테이블의 칼럼으로부터 상속을 받을 경우는 컬럼명 앞에 테이블명을 명시하여야 한다. 여기에서 데이터형을 상속받는다는 말은 상속한 변수나 칼럼의 데이터형이 바뀔 경우 상속받은 변수도 함께 바뀐다는 의미이다.

DECLARE

e_name emp.ename%TYPE;

v_name e_name%TYPE;

row_emp emp%ROWTYPE;

표 13-3 %TYPE 속성 사용 예


PL/SQL에서는 변수를 선언할 때 레코드(record) 형태로 선언할 수 있다. 이때 사용하는 속성이 %ROWTYPE 속성으로서 특정 테이블의 row로부터 모든 칼럼명과 데이터형을 상속받는다. 상속받은 레코드 변수는 실행절에서 레코드 변수 전체로 사용하던지 특정 칼럼만을 사용할 수 있다. 특정 칼럼명만을 사용할 때는 표 13-4의 예에서와 같이 변수명.칼럼명 형태로 사용한다.

DECLARE

row_emp emp%ROWTYPE;

BEGIN

SELECT * INTO row_emp FROM emp WHERE empno = 7369;

row_emp.deptno := 10;

표 13-4 %ROWTYPE 속성 사용 예


13.2.2 제어 구조

PL/SQL에서 사용하는 제어 명령어는 IF 문과 LOOP 문이다. LOOP 문은 다시 LOOP 문을 빠져나가는 형태에 따라 기본 LOOP 문과 WHILE LOOP 문, FOR LOOP 문이 각각 존재한다.

LOOP

LOOP 문은 LOOP와 END LOOP 사이에 반복되는 문장을 배치하는 구조로 구성된다. LOOP와 END LOOP 사이에 EXIT 문 또는 [EXIT WHEN 조건]을 삽입함으로서 LOOP 문을 빠져나갈 수 있게 된다.

구문법

LOOP

Statement-1;

Statement-2;

......

EXIT [WHEN Condition];

END LOOP;


실습을 위해 다음과 같이 test_loop 테이블을 생성하자.

SQL> CREATE TABLE test_table (

record_number INT NOT NULL,

current_date DATE,

CONSTRAINT pk_recnum PRIMARY KEY (record_number));

⼧실습예제: 위에서 생성한 test_table 테이블에 LOOP 문을 사용하여 10개의 행을 생성(insert) 해보자.

SQL

DECLARE

max_records CONSTANT INT := 10;

i INT := 0;

BEGIN

LOOP

i := i + 1;

INSERT INTO test_table (record_number, current_date)

VALUES(i, sysdate);

EXIT WHEN i >= max_records;

END LOOP;

COMMIT;

END;

/

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


PL/SQL 블록은 SQL*Plus에서 SQL과 같이 입력하고 편집, 실행이 가능하다.

위의 실습문제를 입력하고 실행할 때는 맨 끝에 /를 해주면 SQL*Plus의 버퍼에 저장되면서 실행이 된다.

실습을 해보면 알겠지만 위 실습문제를 실행시켰을 때 "PL/SQL 처리가 정상적으로 완료되었습니다." 라고 메시지가 뿌려짐을 알 수 있다. 따라서 실행 결과를 확인하기 위해서는 test_table 테이블을 직접 select해 보아야 한다.

⼆ WHILE ... LOOP

WHILE LOOP 문은 LOOP 문에서 loop를 빠져나가는 조건을 LOOP 절 안에 EXIT 문을 쓰는 것 대신에 LOOP 문을 "WHILE 조건 LOOP"문으로 명시하는 점이 다르다. 그러므로 WHILE LOOP을 진입하기 전에 조건을 비교하여 조건이 맞으면 LOOP절 안에 있는 문장들을 수행한다.

구문법

WHILE Exit_Condition LOOP

Statement-1;

Statement-2;

......

END LOOP;


아래 실습예제의 결과를 보기 위해서는 다음 SQL*Plus 명령문을 먼저 실행시켜야 한다.

SQL> SET SERVEROUTPUT ON

위에서 SQL*Plus의 SERVEROUTPUT 환경변수를 활성화시키게 되면 오라클 서버가 실행한 결과를 모니터를 통해서 볼 수 있게 된다.

⼧실습예제: WHILE LOOP 문을 사용하여 다음 실습을 하여보자.

SQL

DECLARE

i INT := 0;

sel_row test_table%ROWTYPE;

output_line VARCHAR2(80);

BEGIN

DBMS_OUTPUT.ENABLE;

WHILE i < 10 LOOP

i := i + 1;

SELECT * INTO sel_row FROM test_table

WHERE record_number = i;

output_line := to_char(sel_row.record_number, '99')

|| ' ' || to_char(sel_row.current_date,'yy/mm/dd');

DBMS_OUTPUT.PUT_LINE(output_line);

END LOOP;

END;

/

1 00/08/19

2 00/08/19

3 00/08/19

4 00/08/19

5 00/08/19

6 00/08/19

7 00/08/19

8 00/08/19

9 00/08/19

10 00/08/19

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

위의 실습 예제에서 DBMS_OUTPUT.PUT_LINE(output_line); 문장은 output_line 변수에 저장된 내용을 모니터를 통해 출력하라는 명령이고, 그 이전에 DBMS_OUTPUT.ENABLE; 문장은 DBMS_OUTPUT명령을 사용할 수 있도록 활성화시키는 명령이다.

⼆ FOR ... LOOP

FOR LOOP 문은 LOOP 실행 조건을 변수와 함께 하한 값과 상한 값을 지정해주는 방법이다. FOR LOOP 문을 사용할 때는 다른 LOOP 문과 달리 선언부에 조건문에서 사용할 변수를 선언할 필요가 없다. 조건문에 REVERSE라고 선언하면 Index 값은 End_value 값을 Start_value 값이 될 때까지 1씩 감소시키면서 실행된다.

구문법

FOR Index IN [REVERSE] Start_value .. End_value LOOP

Statement-1;

Statement-2;

......

END LOOP;



⼧실습예제: 이전에 WHILE LOOP로 실습한 내용을 FOR LOOP 문으로 바꾸어 실습하여 보자.

SQL

DECLARE

sel_row test_table%rowtype;

output_line varchar2(80);

BEGIN

DBMS_OUTPUT.ENABLE;

FOR i IN 1..10 LOOP

SELECT * INTO sel_row FROM test_table

WHERE record_number = i;

output_line := to_char(sel_row.record_number, '99')

|| ' ' || to_char(sel_row.current_date,'yy/mm/dd');

DBMS_OUTPUT.PUT_LINE(output_line);

END LOOP;

END;

/


⼆ IF

PL/SQL에서의 IF 문은 절차적인 언어에서의 IF 문과 유사하다.

구문법

IF Condition THEN

Statement-1;

......

[ELSIF Condition THEN

Statement-2;

......]

[ELSE

Statement-3;

......]

END IF;

IF문 다음에 또 다른 조건을 사용하고자 할 때는 IF 대신에 ELSIF를 반복해서 사용하면 되고 마지막에는 ELSE를 사용하면 된다. 이때 주의할 점은 ELSEIF가 아니라 ELSIF란 점과 마지막에 ELSE는 단 한 번밖에 사용할 수 없다는 점이다.

13.2.3 예외사항 처리

문법적으로 이상이 없는 PL/SQL 블록을 실행하는 중에 착오가 발생하면 실행이 중단되는 경우가 있는데 이러한 경우에 상황별로 조치할 수 있는 예외처리부분을 작성해 넣을 수 가 있다.

예외처리부에서 사용할 수 있는 예외 상황은 크게 오라클 서버가 자동적으로 인지하는 미리 정의된 Oracle Server 예외와 사용자가 정의 해놓고 사용하는 사용자 정의 예외가 있다.

(1) 미리 정의된 Oracle Server 예외

미리 정의된 예외란 오라클 서버가 인지한 착오 상황으로서 우리가 자주 접하게 되는 오라클 착오 번호 ORA_00001과 같은 착오에 해당한다.

미리 정의된 Oracle Server 예외 유형은 표 13-5와 같다.

예외 이름

발생 상황

DUP_VAL_ON_INDEX

유일값 중복 Error

INVALID_NUMBER

Data type 불일치 Error

NO_DATA_FOUND

데이터를 반환하지 않은 SELECT 문

TOO_MANY_ROWS

두 행 이상을 반환한 SELECT 문

VALUE_ERROR

대입되는 값이 길이 초과

ZERO_DEVIDE

0으로 나누기 시도

표 13-5 미리 정의된 Oracle Server 예외

구문법

EXCEPTION

WHEN Exception_name_1 [OR Exception_name_2 ... ] THEN

Statement-1;

......

[WHEN Exception_name_3 [OR Exception_name_4 ... ] THEN

Statement-2;

......]

[WHEN OTHERS THEN

Statement-3;

......]


여기에서 Exception_name 부분에는 미리 정의된 오라클 서버 예외나 사용자가 정의한 예외 명을 써주면 된다.

WHEN OTHERS THEN 다음에는 명시적으로 선언되지 않은 모든 예외에 대한 처리 루틴을 포함시키면 된다.

만약, 예외가 발생하면 오라클 서버는 블록을 벗어나기 전에 하나의 예외 처리만 수행한다. 다시 말해 복수 개의 예외가 발생할 수 있는 블록이라면 가장 먼저 발생된 예외 상황에 대해서만 예외 처리가 된다는 의미이다.

⼧실습예제: 다음의 예외처리에 대한 예제를 실행시켰을 때 어떤 메시지가 출력될 지 예측하여 보시오.

Ⰱ, Ⰲ, Ⰳ를 차례로 활성화시키면서 실행시킨 결과를 예측하여 보시오

SQL

DECLARE

I int :=1;

sel_row test_table%rowtype;

BEGIN

--Ⰱ UPDATE test_table SET record_number = 'A'

-- WHERE record_number = 1;

--Ⰲ INSERT INTO test_table VALUES(1, SYSDATE);

--Ⰳ SELECT * INTO sel_row FROM test_table

-- WHERE record_number = 21;

SELECT * INTO sel_row FROM test_table;

EXCEPTION

when DUP_VAL_ON_INDEX then

dbms_output.put_line('유일성 중복!');

when INVALID_NUMBER then

dbms_output.put_line('Data type 불일치!');

when NO_DATA_FOUND then

dbms_output.put_line('일치 자료 없음!');

when TOO_MANY_ROWS then

dbms_output.put_line('둘 이상 검색!');

when OTHERS then

dbms_output.put_line('!!!');

END;

/



(2) 사용자 정의 예외

사용자 정의 예외처리는 오라클 서버에 의해 발생될 수 없는 예외 상황에 대해서 사용자가 임의로 정의하여 강제로 발생되게 하는 방법이다.

예외명은 DECLARE절에서 선언하고, 예외가 발생해야할 조건은 실행부분에 정의하면 된다. 예외 상황 발생시 처리해야할 루틴은 미리 정의된 Oracle Server 예외에서와 같이 명시해주면 된다. 단지 사용자가 정의한 예외명을 적어주면 된다.

구문법

DECLARE

Exception_name EXCEPTION;

......

BEGIN

......

RAISE Exception_name;

......

EXCEPTION

WHEN Exception_name THEN

Statement-1;

......


⼧실습예제: test_table의 행(Row)의 수를 검사한 다음 행의 수가 5이상이면 예외 처리하는 블록을 작성하시오.

SQL

DECLARE

row_cnt int :=0;

output_line varchar2(80);

exception_row_over_5 EXCEPTION;

BEGIN

SELECT COUNT(*) INTO row_cnt FROM test_table;

IF row_cnt > 5 THEN

RAISE exception_row_over_5;

END IF;

EXCEPTION

WHEN exception_row_over_5 THEN

output_line := 'Row_Count= ' || row_cnt;

dbms_output.put_line(output_line);

END;

/

13.2.4 커서

위의 예외처리에서 보았듯이 PL/SQL에서는 복수개의 행을 SELECT할 경우에 TOO_MANY_ROWS 예외가 발생하므로 한 행씩 차례대로 읽어들여 처리하지 못하게 된다. PL/SQL에서 이와 같은 문제를 해결할 수 있는 기능을 갖는 것이 커서(cursor) 이다.

커서는 질의에 의해 반환된 행들을 한 행씩 차례대로 이동시켜 가며 처리할 수 있게 한다.

기본적으로 커서는 PL/SQL 블록 내에서 DECLARE절에서 커서를 선언하고, BEGIN절에서 커서를 열어서, 커서를 이용하여 데이터 행을 추출하여 처리하고, 마지막으로 커서 닫기 순으로 사용한다.

커서 선언

DECLARE

CURSOR Cursor_name IS;

Select_statement;

커서 선언은 DECLARE절에 CURSOR문 다음에 커서 명을 명시하고 그 뒤에 IS와 함께 SELECT절을 명시하면 된다. 이 때의 SELECT절은 WHERE절이나 BY절을 사용할 수 있다.

BEGIN절에서 커서를 사용할 때는 먼저 커서를 OPEN 하고, FETCH문을 이용하여 한 행씩 변수로 읽어들여 처리를 하면 된다. 이 때 계속해서 다음 행을 읽어들이기 위해서 LOOP절 안에 FETCH문을 위치시키게 된다.

FETCH 작업이 모두 끝나면 커서를 닫으면 된다.

커서 사용 (1)

BEGIN

......

OPEN Cursor_name;

LOOP

FETCH Cursor_name INTO variable1 [,variable2 ... ];

EXIT WHEN Cursor_name%NOTFOUND;

......

END LOOP

CLOSE Cursor_name;

......


⼧실습예제: test_table을 읽어서 "1 : yyyy/mm/dd" 형태로 출력되도록 PL/SQL 블록을 작성하시오.

SQL

DECLARE

row_test test_table%ROWTYPE;

output_line varchar2(80);

CURSOR test_cursor IS

SELECT * FROM test_table;

BEGIN

OPEN test_cursor;

LOOP

FETCH test_cursor INTO row_test;

EXIT WHEN test_cursor%NOTFOUND;

output_line := row_test.record_number || ' : '

|| TO_CHAR(row_test.current_date, 'yyyy/mm/dd');

dbms_output.put_line(output_line);

END LOOP;

CLOSE test_cursor;

END;

/


1 : 2000/08/19

2 : 2000/08/19

3 : 2000/08/19

4 : 2000/08/19

5 : 2000/08/19

6 : 2000/08/19

7 : 2000/08/19

8 : 2000/08/19

9 : 2000/08/19

10 : 2000/08/19

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

위 예제에서 test_cursor%NOTFOUND는 커서의 상태를 구하는 방법으로서 결과는 True 또는 False로 반환된다. 커서의 속성들은 표 13-6과 같다.

속 성

반환 유형

반환 내용

%ISOPEN

Boolean

커서가 open되어 있으면 True

%NOTFOUND

Boolean

더 이상 fetch할 레코드가 없으면 True

%FOUND

Boolean

더 이상 fetch할 레코드가 있으면 True

%ROWCOUNT

Number

현재까지 fetch한 행의 수

표 13-6 커서 속성

앞에 예제에서는 FETCH를 반복하기 위해서 LOOP문을 사용하였는데 이것을 다음과 같이 FOR LOOP 문으로 대치할 수 있다.

커서 사용 (2)

BEGIN

......

FOR variable IN Cursor_name LOOP

......

END LOOP;

......


이와 같이 FOR LOOP 문에 커서명을 명시하게 되면 Fetch하여 저장할 변수나 레코드 변수를 별도로 DECLARE절에서 선언할 필요가 없고, BEGIN절에서 커서를 OPEN하고 CLOSE할 필요가 없으므로 PL/SQL 블록이 아주 단순해진다.

⼧실습예제: 앞의 실습예제를 FOR LOOP를 사용하는 방법으로 PL/SQL 블록을 바꾸어보시오.

SQL

DECLARE

output_line varchar2(80);

CURSOR test_cursor IS

SELECT * FROM test_table;

BEGIN

FOR row_test IN test_cursor LOOP

output_line := row_test.record_number || ' : '

|| TO_CHAR(row_test.current_date, 'yyyy/mm/dd');

dbms_output.put_line(output_line);

END LOOP;

END;

/


⼆ WHERE절에서의 커서 사용

PL/SQL에서는 WHERE절에서도 커서를 사용할 수 있다.

WHERE절에서 커서를 사용하기 위해서는 커서를 선언할 때 SELECT절 뒤에 FOR UPDATE문을 추가하여야 한다.

WHERE절에서 커서 사용

DECLARE

CURSOR Cursor_name IS;

Select_statement

FOR UPDATE;

BEGIN

......

WHERE CURRENT OF Cursor_name;

......


WHERE절에서의 커서 사용은 현재 커서가 가리키고 있는 행에 대해 UPDATE나 DELETE 작업을 할 때 유용하다.

⼧실습예제: test_table에서 record_number가 5보다 큰 행의 current_date에 7일을 더하여 갱신하는 PL/SQL 블록을 커서를 이용하여 작성하시오.

SQL

DECLARE

output_line varchar2(80);

CURSOR test_cursor IS

SELECT * FROM test_table

WHERE record_number > 5

FOR UPDATE;

BEGIN

FOR row_test IN test_cursor LOOP

UPDATE test_table SET current_date = current_date + 7

WHERE CURRENT OF test_cursor;

END LOOP;

END;

/


⼆ Parameter가 있는 커서 사용

PL/SQL에서는 커서의 매개변수를 통해서 커서에 값을 전달할 수 있다.

Parameter가 있는 커서 사용

DECLARE

CURSOR Cursor_name (parameter_name Data_type) IS;

Select_statement;

BEGIN

......

OPEN Cursor_name(parameter_name);

......

CLOSE Cursor_name;

커서 parameter로의 값 전달은 커서를 OPEN할 때이다.

커서는 parameter를 통해 전달받은 값을 이용해서 SELECT절을 수행하게 되므로 커서를 융통성 있게 사용할 수 있는 효과가 있다.

⼧실습예제: test_table에서 parameter가 있는 커서를 이용해서 current_date가 SYSDATE보다 큰 행만을 출력하는 PL/SQL 블록을 작성하시오.

SQL

DECLARE

output_line varchar2(80);

CURSOR test_cursor(p_date DATE) IS

SELECT * FROM test_table

WHERE current_date > p_date;

BEGIN

FOR row_test IN test_cursor(SYSDATE) LOOP

output_line := row_test.record_number || ' : '

|| TO_CHAR(row_test.current_date, 'yyyy/mm/dd');

dbms_output.put_line(output_line);

END LOOP;

END;

/

6 : 2000/08/26

7 : 2000/08/26

8 : 2000/08/26

9 : 2000/08/26

10 : 2000/08/26

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







13.3 서브프로그램

PL/SQL에서는 프로시저(Procedure)나 함수(Function)를 선언하여 사용할 수 있다. 선언되는 함수나 프로시저는 PL/SQL 블록 내에 위치할 수도 있고, 별도의 이름을 부여하여 오라클 개체로 저장하여 관리 될 수도 있다.

다음은 프로시저를 오라클 개체로 저장하여 다른 프로그램에서 호출하여 실행하는 예를 소개한다.

⼆ Stored Procedure

Stored Procedure는 오라클 개체로 관리되므로 프로시저 생성시 CREATE 구문을 사용하고 한다. CREATE문 다음에 PL/SQL 블록이 위치하게 되고 프로시저 작성이 끝나면 컴파일 되어 프로시저 이름으로 저장된다. 이 때의 PL/SQL 블록은 DECLEAR 문을 생략하고 바로 변수만 하여야 한다.

Stored Procedure

CREATE [OR REPLACE] PROCEDURE Procedure_name

[(Parameter, ........)]

IS

PL/SQL_Block;


프로시저가 컴파일 될 때 에러가 발생하면 에러를 수정한 후 다시 컴파일 해야 하는데 이때 CREATE 문에 OR REPLACE를 명시하지 않으면 프로시저 이름 중복 에러가 발생한다. 이것은 컴파일 에러가 발생하더라도 프로시저가 등록되기 때문으로 다시 컴파일 할 때는 OR REPLACE문을 포함시켜야 한다. 그렇지 않으면 해당 프로시저를 삭제하고 다시 생성해야 한다.

프로시저를 삭제하는 명령은 다음과 같다.

DROP PROCEDURE Procedure_name

프로시저를 생성할 때 데이터를 전달받고 반환할 parameter를 선언할 수도 있고 선언하지 않아도 된다. parameter 선언 방법은 다음과 같다.

Parameter 선언

[(Parameter_name [IN | OUT | IN OUT] Datatype, ........)]

여기에서 IN은 실행 환경으로부터 프로시저로 값을 전달받는 parameter이고 OUT은 프로시저로부터 실행 환경으로 값을 전달하는 parameter이다. IN OUT은 IN과 OUT 기능을 모두 포함하는 parameter이다.

⼧실습예제: test_table의 행의 수를 출력해주는 proc_test_count 프로시저를 생성해보자.

SQL

CREATE OR REPLACE PROCEDURE proc_test_count IS

output_line varchar2(80);

rec_cnt int := 0;

BEGIN

SELECT COUNT(*) INTO rec_cnt FROM test_table;

output_line := 'Record Count = ' || rec_cnt;

dbms_output.put_line(output_line);

END;

/

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

Procedure를 실행한 후에 컴파일시 발생한 착오사항을 확인하려면 다음과 같이 SQL*Plus 명령을 사용하면 된다.

SQL> SHOW ERRORS

프로시저가 정상적으로 컴파일 되었을 때 "프로시저가 생성되었습니다." 라는 메시지만 반환된다. 이것은 프로시저가 정상적으로 컴파일 되어 오라클 서버에 의해 Procedure_name으로 관리된다는 의미이다.

따라서 생성된 프로시저를 실행시키기 위해서는 다음과 같이 프로시저를 실행시켜야 한다.

SQL> EXECUTE proc_test_count

Record Count = 10

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

생성된 프로시저를 확인하는 명령은 다음과 같다.

SQL> SELECT text FROM user_source

WHERE name = 'PROC_TEST_COUNT';

이 飁 Procedure_name은 반드시 대문자로 명시해 주어야 한다.

⼧실습예제: 앞의 실습예제에서 생성된 proc_test_count 프로시저를 호출하는 PL/SQL 블록을 작성하시오.

SQL

DECLARE

BEGIN

proc_test_count;

END;

/

Record Count = 10

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



⼆ Stored Function

Function는 다음 두 가지 점에서 Procedure와 다르다.

⼘Function은 값을 반드시 반환하고 프로시저는 선택적이다.

⼘Function은 식의 일부로서 사용된다.

Function의 생성 방법은 Procedure와 유사하고 반드시 반환될 데이터형을 명시해야 한다는 점이 다르다.

Function은 한 가지 값만을 반환함으로 parameter 선언부에는 OUT, IN OUT은 사용하면 안되고 IN만을 선택적으로 사용해야 한다.

Stored Function

CREATE [OR REPLACE] FUNCTION Function_name

[(Parameter, ........)]

RETURN datatype

IS

PL/SQL_Block;


Function의 PL/SQL_Block문에는 값을 반환하기 위해서 다음과 같이 RETURN()문을 사용하여야 한다.

RETURN( {Variable | Expression});

RETURN되는 parameter는 변수나 수식 또는 상수 중에 하나를 사용할 수 있다.

⼧실습예제: 함수로 전달된 값과 test_table의 record_number 값을 비교하여 record_number가 큰 행의 수를 반환하는 func_test_count 함수를 작성하시오.

SQL

CREATE OR REPLACE FUNCTION func_test_count

(rec_num IN INT) RETURN INT

IS

rec_cnt int := 0;

BEGIN

SELECT COUNT(*) INTO rec_cnt FROM test_table

WHERE record_number > rec_num;

RETURN(rec_cnt);

END;

/

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


⼧실습예제: func_test_count함수의 실행 결과를 출력하는 PL/SQL 블록을 작성하시오.

SQL

DECLARE

output_line varchar2(80);

check_num int := 5;

BEGIN

output_line := 'Check Count = '

|| func_test_count(check_num);

dbms_output.put_line(output_line);

END;

/

Check Count = 5

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


'프로그래밍 > DB' 카테고리의 다른 글

[펌] Oracle - PL/SQL  (0) 2007.11.27
[펌] PL/SQL 조건문 만들기  (0) 2007.11.27
[펌] PL-SQL 정리  (0) 2007.11.27
select * from user_catalog;  (0) 2007.11.27
[펌] SQL*Plus에서 꼭 알아두어야 할 활용 매뉴얼~!  (0) 2007.11.27