쿼리문의 집합으로 어떠한 동작을 일괄 처리하기 위한 용도로 사용
C 또는 Java와 같은 언어를 이용해서 개발하다 보면, 동일한 기능을 하는 코드를 함수로 분리해서 개발한다.
비슷하게 Database에서도 Procedure를 이용해서 함수처럼 만들 수 있고, 보안과 성능 등의 장점이 있다.
https://dev.mysql.com/doc/refman/8.0/en/create-procedure.html
Mysql의 경우 아래와 같은 형식으로 정의할 수 있다.
CREATE
[DEFINER = user]
PROCEDURE [IF NOT EXISTS] sp_name ([proc_parameter[,...]])
[characteristic ...] routine_body
CREATE
[DEFINER = user]
FUNCTION [IF NOT EXISTS] sp_name ([func_parameter[,...]])
RETURNS type
[characteristic ...] routine_body
proc_parameter:
[ IN | OUT | INOUT ] param_name type
func_parameter:
param_name type
type:
Any valid MySQL data type
characteristic: {
COMMENT 'string'
| LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
}
routine_body:
Valid SQL routine statement
조금 더 간단하게 살펴보자
DELIMITER $$
CREATE PROCEDURE 스토어드 프로시져 이름(In 또는 Out 파라미터)
BEGIN
원하는 동작 작성
END$$
DELIMITER ;
CALL 스토어드 프로시저이름();
아래 Procedure는 개인 프로젝트를 사용하면서 작성했던 데이터 중 일부이다.
-- 만약 SP_ONION_GROW라는 이름을 가진 Procedure가 있다면 삭제한다.
DROP PROCEDURE IF EXISTS SP_ONION_GROW;
-- MySQL의 종료문자는 기본적으로 ';' 이지만,
-- CREATE PROCEDURE 내부에서도 ;가 종료문자이므로, 식별이 어렵다. 잠시 $$으로 변경해둔다.
DELIMITER $$
CREATE PROCEDURE SP_ONION_GROW() -- 특정 이름을 가진 프로시저를 만든다.
BEGIN -- BEGIN -> END 사이에 원하는 동작을 작성한다.
INSERT INTO notificate.alarm(sender_id, receiver_id, content, created_at, is_sended, is_read, type)
SELECT a.user_id as sender_id,
a.user_id as "receiver_id",
'content' as content,
now() as created_at,
false as is_sended,
false as is_read,
3 as type
FROM onion.onion a
WHERE 1 = 1
AND a.send_date is null
AND a.is_disabled = false
AND a.grow_due_date <= now();
END$$ -- 프로시저의 작성이 종료되었다.
DELIMITER ; -- 종료문자를 $$ 에서 다시 ;로 변경해주자.
수정의 경우 ALTER PROCEDURE
,
삭제의 경우 DROP PROCEDURE
를 활용한다.
CREATE PROCEDURE SP_SAMPLE
AS
BEGIN
SELECT * FROM TableName;
END;
-- 수정하는 경우
ALTER PROCEDURE SP_SAMPLE
AS
BEGIN
SELECT Column1, Column2 FROM TableName;
END;
-- 삭제하는 경우
DROP PROCEDURE SP_SAMPLE
DECLARE 액션 HANDLER FOR 오류조건 처리할문장
을 활용한다.
액션의 종류
액션의 종류에는 CONTINUE
와 EXIT
가 있다.
CONTINUE
의 경우에는 처리할 문장이 처리된다.
EXIT
의 경우에는 종료된다.
오류조건
MySQL의 경우 숫자로된 오류 코드 또는 SQLSTATE 등이 온다.
처리할 문장
처리할 문장이 하나라면 한 문장으로 작성, 여러 개일 경우에는 BEGIN - END로 묶어줄 수 있다.
DROP PROCEDURE IF EXISTS SP_SAMPLE;
DELIMITER $$
CREATE PROCEDURE SP_SAMPLE()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SELECT '테이블 없음' as '메시지';
SELECT * FROM city;
END $$
DELIMITER ;
CALL SP_SAMPLE();
테이블이 존재하는 경우 해당 테이블의 데이터가 모두 출력되고 테이블이 없는 경우, 테이블없음 이라는 결과가 나온다.
성능향상, 네트워크 비용 감소
긴 코드로 구현된 쿼리 실행 시, 클라이언트->서버로 쿼리의 모든 텍스트가 전송되어야 한다. 하지만 프로시저화 되어있다면, 프로시저 이름과 매개 변수 등 몇 글자만 전송하면 되므로 네트워크 부하의 감소로 이어질 수 있다.
또한 DBMS에 따라 1회만 컴파일 후 메모리에 로딩되어, 이후 실행 시 컴파일을 하지 않고 저장된 내용을 호출하여 성능향상이 발생함. ( MySQL은 해당이 없다..!)
일관성
한 번 작성하면 여러 번 사용가능하다. 동일한 로직이 반복된다면 재사용 및 관리가 편한다.
보안
사용자 별로 테이블에 접근 권한을 주지 않고 프로시저에만 접근 권한을 줄 수 있다. 즉 원하지 않는 데이터에 대한 공개를 막을 수 있다.
위와 같은 장점이 있지만 물론 단점도 있다.
유지보수 와 디버깅의 어려움
스토어드 프로시저의 로직이 복잡해지면 이해하는 과정이 어렵다. 또한 디버깅 과정이 매우 어려워진다.
이식성
각 DBMS마다 조금씩의 문법의 차이가 존재하여, 타 DBMS로 이전이 어려울 수 있음.