1. 절차형 SQL 개요
2. PL/SQL 개요
3. T-SQL 개요
4. Procedure의 생성과 활용
5. User Defined Function의 생성과 활용
6. Trigger 의 생성과 활용
7. 프로시저와 트리거의 차이점
CREATE [OR REPLACE] Procedure [Procedure_name] -- [OR REPLACE] : 같은 이름의 프로시저가 있을 경우, 새로운 내용으로 REPLACE
( argument1 [mode1] data_type1, -- [mode] : IN - 운영체제에서 프로시저에 전달될 변수의 MODE,
-- OUT - 프로시저에서 처리된 결과가 운영체제로 전달되는 MODE
-- INOUT - IN과 OUT 두 가지의 기능 동시 수행
argument2 [mode2] date_type2,
... ... )
IS [AS]
... ...
BEGIN
... ...
EXCEPTION
... ...
END;
/ -- "/" : 데이터베이스에게 프로시저를 컴파일하라는 명령어
* 프로시저 삭제
-- Procedure 삭제
DROP Procedure [Procedure_name];
CREATE Procedure [schema_name.]Procedure_name -- 생성은 CREATE, 변경은 ALTER
@parameter1 data_type1[mode], -- [mode] 1. VARING : 결과 집합이 출력 매개 변수로 사용되도록 지정한 CURSOR 매개변수에만 적용
-- 2. DEFAULT : 지정된 매개변수가 프로시저를 호출할 당시 지정되지 않을 경우, 지정된 기본값으로 처리한다.
-- 3. OUT. OUTPUT : 프로시저에서 치리된 결과 값을 EXECUTE 문 호출 시 반환
-- 4. READONLY : 프로시저 본문 내에서 매개 변수를 업데이트 하거나 수정할수 없음을 의미
@parameter2 data_type2[mode],
... ...
WITH <proc_option> -- [proc_option> 1. RECOMPILE : 현재 프로시저의 계획을 캐시하지 않고 프로시저가 런타임에 컴파일
-- 2. ENCRYPTION : 원문 텍스트가 알아보기 어려운 형식으로 변환
-- 3. EXECUTE AS : 해당 저장 프로시저를 실행할 보안 컴텍스트를 지정 { EXEC } | { CALLER | SELF | OWNER | 'user_name' }
AS
... ...
BEGIN
... ...
ERROR 처리
... ...
END;
* 프로시저 삭제
DROP Procedure [schema_name.]Procedure_name ;
CREATE OR REPLACE Procedure p_DEPT_insert << 1
( v_DEPTNO IN NUMBER,
v_dname IN VARCHAR2,
v_loc IN VARCHAR2,
v_result OUT VARCHAR2 )
IS
cnt number := 0 ;
BEGIN
SELECT COUNT(*) INTO CNT << 2
FROM DEPT
WHERE DEPTNO = v_DEPTNO
AND ROWNUM = 1 ;
IF CNT >0 THEN << 3
v_result :='이미 등록된 부서번호이다.';
else
INSERT INTO DEPT (DEPTNO, DNAME, LOC) << 4
VALUES ( v_DEPTNO, v_dname, v_loc ) ;
COMMIT ; << 5
v_result := '입력완료!!';
END IF ;
EXCEPTION << 6
WHEN OTHERS THEN
ROLLBACK ;
v_result :='Error 발생';
END ;
/
SQL> select * from dept ;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> variable rslt varchar2(30) ;
SQL> exec p_dept_insert(10,'dev','seoul',:rslt);
PL/SQL procedure successfully completed.
SQL> print rslt;
RSLT
--------------------------------
Error 이미 등록된 부서번호이다
SQL> exec p_dept_insert(50,'NewDev','seoul',:rslt);
PL/SQL procedure successfully completed.
SQL> print rslt;
RSLT
--------------------------------
입력완료!!
SQL> select * from dept ;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
50 NewDev seoul
SQL>
PL/SQL 및 T-SQL 작성 시, 주의해야 할 문법적 요소
1. CNT 라는 변수를 SCALAR 변수라고 한다. SCALR 변수는 사용자의 임시 데이터를 하나만 저장할 수 있은 변수이며 거의 모든 형태의 데이터 유형 지정 가능
2. PL/SQL 에서 사용하는 SELECT 문장은 결과값이 반드시 있어야 하며, 그 결과 역시 반드시 하나여야 한다.
조회 결과가 없거나 하나 이상인 경우에는 에러를 발생시킨다.
T-SQL 에서는 결과 값이 없어도 에러가 발생하지 않는다.
3. T-SQL을 비롯하여 일반적으로 대입 연산자는 "="을 사용하지만 PL/SQL에서는 ":="을 사용한다.
4. 에러 처리를 담당하는 EXCEPTION 에는 WHEN ~ THEN 절을 사용하여 에러의 종류별로 적절히 처리 한다.
OTHERS 를 이용하여 모든 에러를 처리할 수 있지만 정확하게 에러를 처리하는 것이 좋다.
* ABS 함수를 사용하여 점수차를 절대값으로 출력
SQL> SELECT SCHE_DATE DA,
HOMETEAM_ID||'-'||AWAYTEAM_ID TEAMS,
HOME_SCORE||'-'||AWAY_SCORE SCORE,
ABS(HOME_SCORE - AWAY_SCORE)
FROM SCHEDULE
WHERE GUBUN='Y'
AND SCHE_DATE BETWEEN '20120801' AND '20120831'
ORDER BY SCHE_DATE
DA TEAMS SCORE ABS(HOME_SCORE-AWAY_SCORE)
-------- ------- -------------------- --------------------------
20120803 K08-K07 1-0 1
20120803 K01-K03 3-0 3
20120803 K06-K09 2-1 1
20120804 K05-K04 2-1 1
20120804 K10-K02 0-3 3
20120811 K09-K05 0-1 1
20120811 K03-K08 2-0 2
20120811 K07-K10 1-1 0
20120811 K01-K06 0-0 0
20120811 K04-K02 0-2 2
20120818 K06-K03 3-1 2
20120818 K02-K09 1-2 1
20120818 K08-K10 3-1 2
20120818 K04-K07 1-0 1
20120818 K05-K01 0-2 2
20120824 K08-K06 4-3 1
20120824 K05-K03 3-3 0
20120824 K02-K01 1-1 0
20120825 K10-K04 1-1 0
20120825 K09-K07 1-1 0
20120828 K03-K02 0-0 0
20120828 K06-K05 1-1 0
20120828 K04-K08 2-3 1
20120828 K09-K10 2-0 2
20120828 K01-K07 0-1 1
25 rows selected.
SQL>
* ABS 함수를 대체할 User Defined Function 생성
CREATE OR REPLACE Function UTIL_ABS
( v_input in number )
return NUMBER
IS
v_return number := 0 ;
BEGIN
IF v_input < 0 then
v_return := v_input* -1 ;
else
v_return := v_input ;
end if;
RETURN V_return ;
END ;
CREATE Function dbo.UTIL_ABS
( @v_input int )
RETURNS int
AS
BEGIN
DECLARE @v_return int
SET @v_return = 0
IF @v_input < 0
SET @v_return = @v_input *-1
ELSE
SET @v_return = @v_input
RETURN @v_return ;
END
/
* 동일한 결과 출력
SQL> SELECT SCHE_DATE DA,
HOMETEAM_ID||'-'||AWAYTEAM_ID TEAMS,
HOME_SCORE||'-'||AWAY_SCORE SCORE,
UTIL_ABS(HOME_SCORE - AWAY_SCORE)
FROM SCHEDULE
WHERE GUBUN='Y'
AND SCHE_DATE BETWEEN '20120801' AND '20120831'
ORDER BY SCHE_DATE ;
DA TEAMS SCORE UTIL_ABS(HOME_SCORE-AWAY_SCORE)
-------- ------- -------------------- -------------------------------
20120803 K08-K07 1-0 1
20120803 K01-K03 3-0 3
20120803 K06-K09 2-1 1
20120804 K05-K04 2-1 1
20120804 K10-K02 0-3 3
20120811 K09-K05 0-1 1
20120811 K03-K08 2-0 2
20120811 K07-K10 1-1 0
20120811 K01-K06 0-0 0
20120811 K04-K02 0-2 2
20120818 K06-K03 3-1 2
20120818 K02-K09 1-2 1
20120818 K08-K10 3-1 2
20120818 K04-K07 1-0 1
20120818 K05-K01 0-2 2
20120824 K08-K06 4-3 1
20120824 K05-K03 3-3 0
20120824 K02-K01 1-1 0
20120825 K10-K04 1-1 0
20120825 K09-K07 1-1 0
20120828 K03-K02 0-0 0
20120828 K06-K05 1-1 0
20120828 K04-K08 2-3 1
20120828 K09-K10 2-0 2
20120828 K01-K07 0-1 1
25 rows selected.
CREATE TABLE ORDER_LIST (
ORDER_DATE CHAR(8) NOT NULL,
PRODUCT VARCHAR2(10) NOT NULL,
QTY NUMBER NOT NULL,
AMOUNT NUMBER NOT NULL ) ;
CREATE TABLE SALES_PER_DATE (
SALE_DATE CHAR(8) NOT NULL,
PRODUCT VARCHAR2(10) NOT NULL,
QTY NUMBER NOT NULL,
AMOUNT NUMBER NOT NULL );
CREATE OR REPLACE Trigger SUMMARY_SALES << 1
AFTER INSERT
ON ORDER_LIST
FOR EACH ROW
DECLARE << 2
O_DATE ORDER_LIST.ORDER_DATE%TYPE;
O_PROD ORDER_LIST.PRODUCT%TYPE ;
BEGIN
O_DATE := :NEW.ORDER_DATE ; -- NEW는 신규로 입력된 레코드의 정보를 가지고 있는 구조체, OLD는 수정, 삭제되지 전의 레코드를 가지는 구조체
O_PROD := :NEW.PRODUCT ; -- MS SQL : DML triggers use the deleted and inserted logical (conceptual) tables.
UPDATE SALES_PER_DATE << 3
SET QTY = QTY + :NEW.QTY, AMOUNT = AMOUNT + :NEW.AMOUNT
WHERE SALE_DATE = O_DATE
AND PRODUCT = O_PROD ;
IF SQL%NOTFOUND THEN << 4
INSERT INTO SALES_PER_DATE
VALUES (O_DATE, O_PROD, :NEW.QTY, :NEW.AMOUNT);
END IF ;
END ;
SQL> SELECT * FROM ORDER_LIST ;
no rows selected
SQL> SELECT * FROM SALES_PER_DATE ;
no rows selected
SQL> INSERT INTO ORDER_LIST VALUES ('20120901','MONOPACK',10,30000 );
1 row created.
SQL> COMMIT ;
Commit complete.
SQL> select * from order_list ;
ORDER_DA PRODUCT QTY AMOUNT
-------- ---------- ---------- ----------
20120901 MONOPACK 10 30000
SQL> select * from sales_per_date ;
SALE_DAT PRODUCT QTY AMOUNT
-------- ---------- ---------- ----------
20120901 MONOPACK 10 30000
* 트리거 테이블에 대한 롤백 확인
SQL> INSERT INTO ORDER_LIST VALUES ('20120901','MONOPACK',10,30000 );
1 row created.
SQL> SELECT * FROM ORDER_LIST ;
ORDER_DA PRODUCT QTY AMOUNT
-------- ---------- ---------- ----------
20120901 MONOPACK 10 30000
20120901 MONOPACK 10 30000
SQL> SELECT * FROM SALES_PER_DATE ;
SALE_DAT PRODUCT QTY AMOUNT
-------- ---------- ---------- ----------
20120901 MONOPACK 20 60000
SQL> rollback ;
Rollback complete.
SQL> SELECT * FROM ORDER_LIST ;
ORDER_DA PRODUCT QTY AMOUNT
-------- ---------- ---------- ----------
20120901 MONOPACK 10 30000
SQL> SELECT * FROM SALES_PER_DATE ;
SALE_DAT PRODUCT QTY AMOUNT
-------- ---------- ---------- ----------
20120901 MONOPACK 10 30000