스토어드 프로그램 장단점, 문법, 보안 옵션 및 주의사항 등을 알아보자
스토어드 프로그램
: 스토어드 루틴
이라고도 하며, 스토어드 프로시저
와 스토어드 함수
, 트리거
와 이벤트
등 모두 아우르는 명칭
뷰
: 스토어드 쿼리
라고 불림
헤더 부분
: 보안
이나 작동 방식
과 관련된 옵션 명시, 이름, 입출력 값
본문 부분
: 실행 내용 작성
서로 데이터를 주고받아야
하는 여러 쿼리를 하나의 그룹
으로 묶어서 독립적으로 실행
=> 독립적
으로 호출
=> SELECT나 UPDATE 같은 SQL 문장에서 참조 불가능
create procedure
명령으로 생성
create procedure sp_sum(in param1 integer, in param2 integer, out param3 integer)
begin
set param3=param1+param2;
end;;
기본 반환값
이 없음
으로, 내부에서 return 명령 사용 X
IN타입
, OUT타입
, INOUT타입
MySQL 서버가 create 명령
의 끝을 정확히 판별할 수 있게 별도의 문자열 구분자
를 설정해야 함
DELIMITER
: 명령의 끝을 알려주는 종료 문자
를 변경
하는 명령어
delimiter ;;
#이후 위 프로시저 실행
#스토어드 프로그램 생성 끝나면 다시 기본 종료 문자로 복구
delimiter ;
ALTER PROCEDURE
명령으로 프로시저 변경
#보안 및 작동 방식 특성 변경 시에만 사용 가능
alter procedure sp_sum SQL SECURITY DEFINER;
DROP PROCEDURE
명령으로 프로시저 삭제
#프로시저 내용 수정시에도 삭제 후 재생성 해야함
drop procedure sp_sum;
SELECT 쿼리에 사용 못하며, 반드시 CALL 명령어
로 실행
#파라미터 제공 중 OUT 이나 INOUT 타입은 무조건 세션 변수 필요함
set @result:=0;
select @result;
call sp_sum(1,2,@result);
select @result;
명시적으로 커서
를 파라미터로 전달받
거나 반환
할 수 없음
=> 커서 오픈하지 않거나
SELECT 쿼리의 결과 셋을 페치(Fetch)
하지 않으면 결과 셋
은 바로 클라이언트로 전송
=> 스토어드 프로시저
내에서 단순히 SELECT 쿼리
만 사용하면 결과를 화면상에서 확인 가능
=> 디버깅
용도로 사용
delimiter ;;
create procedure sp_selectEmployees(in in_empno integer)
begin
select * from employees where emp_no=in_empno;
end;;
delimiter ;
call sp_selectEmployees(10001);
information_schema
데이터베이스의 routines 뷰
를 통해 스토어드 프로시저 정보 조회
select routine_schema, routine_name, routine_type
from information_schema.routines
where routine_schema='employees';
하나의 SQL 문장으로 작성이 불가능한 기능
을 하나의 SQL
문장으로 구현해야 할 때 사용
#사원을 2명씩 가져오는 기능
select dept_no, sf_getRecentEmp(dept_no) #최근 2명의 사원번호만 select하고 문자열로 결합
from dept_emp
group by dept_no;
SQL 문장의 일부
로 사용 가능
CREATE FUNCTION
명령으로 생성
=> 모든 입력 파라미터는 읽기 전용
이라 형식 지정X
=> returns
를 이용해 반환되는 값의 타입 명시
# SET GLOBAL log_bin_trust_function_creators = 1;
create function sf_sum(param1 integer, param2 integer)
returns integer
begin
declare param3 integer default 0;
set param3=param1+param2;
return param3;
end;;
함수 본문
에서 사용 못하는 것들
프리페어 스테이트먼트
사용 불가ROLLBACK/COMMIT
을 유발하는 SQL 문장 사용 불가재귀 호출
사용 불가프로시저 호출 불가
결과 셋
반환하는 SQL 문장 사용 불가ALTER FUNCTION
명령으로 변경
#특성만 변경 가능
alter function sf_sum sql security definer;
DROP FUNCTION
명령으로 삭제
drop function sf_sum;
SELECT 문장
을 이용해 실행
select sf_sum(1,2) as sum;
테이블의 레코드가 저장
되거나 변경
될 때 미리 정의해둔 작업을 자동
으로 실행해주는 프로그램
CREATE TRIGGER
명령으로 실행
#employees 테이블의 레코드가 삭제되기 전에 실행
create trigger on_delete before delete on employees
for each row
begin
delete from salaries where emp_no=OLD.emp_no;
end ;;
#OLD : 변경되기 전 레코드
#NEW : 변경될 레코드
트리거 본문 블록에서 사용 못하는 작업
외래키
관계에 의해 자동으로 변경되는 경우 호출XROLLBACK/COMMIT
을 유발하는 SQL 문장 사용 불가return 사용 불가
, 트리거 종료시 leave 명령
사용레코드 기반의 복제(ROW)
에서는 트리거를 기동X
, 문장 기반의 복제(Statement)
에서는 트리거 기동
mysql
과 information_schema, performance_schema
에 존재하는 테이블은 트리거 생성 불가트리거가 생성된 테이블에 직접 레코드
를 INSERT, UPDATE, DELETE 수행해서 작동 확인
information_schema
의 triggers 뷰
를 통해 조회
주어진 특정한 시간
에 스토어드 프로그램을 실행
할 수 있는 스케줄러 기능
=> MySQL 서버 설정 파일에서 event_scheduler
를 ON
이나 1
로 활성화해야 사용 가능
show global variables like 'event_scheduler';
information_schema
데이터베이스의 events 뷰
에서 확인 가능
일회성 이벤트
와 반복성 이벤트
로 나눌 수 있음
#일회성 이벤트 : on schedule at 명시
create event onetime_job
on schedule at current_timestamp+interval 1 hour
do
insert into daily_rank_log values(now(),'Done');
# 현재로부터 한시간 뒤에 한번 실행됨
#반복성 이벤트
create event daily_ranking
on schedule every 1 day starts '2020-09-07 01:00:00' ends '2021-01-01 00:00:00'
do
insert into daily_rank_log values(now(),'Done');
ON COMPLETION PRESERVE
옵션과 함께 이벤트 등록시 이벤트 실행 완료돼도 삭제 안됨
트리거 처럼 특정한 사건이 발생해야
실행되기 때문에 직접 실행해봐야 함
delimiter ;;
create table daily_rank_log(
exec_dttm datetime,
exec_msg varchar(50)
);;
create event daily_ranking
on schedule at current_timestamp+interval 1 minute
on completion preserve
do begin
insert into daily_rank_log values(now(),'Done');
end ;;
select * from test.daily_rank_log;
information_schema
의 events 뷰
를 통해 메타 정보
와 마지막 실행 이력
을 보여줌
select * from information_schema.events \G
본문(Body)
은 BEGIN
으로 시작해서 END
로 끝나며, 하나의 BEGIN ... END 블록
은 또 다른 여러 개의 BEGIN ... END 블록
을 중첩해서 포함 가능
트랜잭션 시작
을 위해선 start transaction 명령
으로 시작해야 함
=> 스토어드 프로시저
나 이벤트
의 본문
에서만 사용 가능
=> 스토어드 함수
나 트리거
는 프로시저 외부에서 트랜잭션 완료
만 가능
스토어드 프로그램
에서 사용하는 변수는 로컬 변수
라고 표현
=> DECLARE
명령으로 정의
=> 타입
이 함께 명시
=> SET
또는 SELECT ... INTO ... 문장
으로 값 할당
=> 사용자 변수
보다 빠름
#로컬 변수 정의
declare v_name varchar(50) default 'Matt';
declare v_email varchar(50) default 'matt@email.com';
#로컬 변수에 값 할당
set v_name='Kim', v_email='kim@email.com';
입력 파라미터
, 로컬 변수
, 테이블 칼럼명
이 모두 같은 이름일 때 우선순위
1. 로컬 변수
2. 입력 파라미터
3. 테이블의 칼럼
begin ... end
블록 내부에서만 사용 가능
#큰 값 반환 함수
create function sf_greatest(p_value1 int, p_value2 int)
returns int
begin
if p_value1 is null then
return p_value2;
elseif p_value2 is null then
return p_value1;
elseif p_value1>=p_value2 then
return p_value1;
else
return p_value2;
end if;
end;;
case
when 비교조건식 1 then 처리내용1
when 비교조건식 2 then 처리내용2
else 처리내용3
end case;
LOOP
, REPEAT
, WHILE
구문 사용 가능
=> LOOP
: 반복 조건 명시X
, 루프 벗어나기 위해 LEAVE
명령 사용
=> REPEAT
: 본문 처리
후 반복 조건 체크
=> WHILE
: REPEAT과 반대
로 실행
#LOOP
create function sf_fact1(p_max int)
returns int
begin
declare v_fact int default 1;
fact_loop:LOOP #LOOP의 이름(레이블)
set v_fact=v_fact*p_max;
set p_max=p_max-1;
if p_max<=1 then
leave fact_loop;
end if;
end loop;
return v_fact;
end;;
#REPEAT
create function sf_fact2(p_max int)
returns int
begin
declare v_fact int default 1;
repeat
set v_fact=v_fact*p_max;
set p_max=p_max-1;
until p_max<=1 end repeat; #해당 내용이 TRUE인 경우 루프 벗어남
return v_fact;
end;;
#WHILE
create function sf_fact3(p_max int)
returns int
begin
declare v_fact int default 1;
while p_max>1 do #조건이 TRUE 인 동안 실행
set v_fact=v_fact*p_max;
set p_max=p_max-1;
end while;
return v_fact;
end;;
핸들러
: 이미 정의된 컨디션
또는 사용자가 정의한 컨디션을 어떻게 처리
할지 정의하는 기능
컨디션
: SQL 문장의 처리 상태
에 대해 별명
을 붙이는 것
ERROR ERROR-NO (SQL-STATE) : ERROR-MESSAGE
형태
ERROR-NO
: 에러 식별 번호SQL-STATE
: ANSI 표준을 준수하는 상태
를 의미하는 코드00-정상 처리
, 01-경고
, 02-not found
, 그외-DBMS 별 에러케이스
ERROR-MESSAGE
: 에러 메시지https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html
핸들러
를 SQLSTATE
로 정의하는 것이 좋음
#예외 핸들링
declare handler_type handler
for condition_value [, condition_value] ... handler_statements
handler_type
continue
: handler_statements
실행 후 스토어드 프로그램
의 마지막 실행 지점
으로 돌아가서 나머지 코드
처리exit
: handler_statements
처리 후 해당 핸들러가 정의된 begin .. end
벗어남컨디션 값(Condition value)
SQLSTATE
: 해당 이벤트의 SQLSTATE 값
이 일치
할 때 실행SQLWARNING
: SQLSTATE 값
이 01
로 시작하는 이벤트NOT FOUND
: SQLSTATE 값
이 02
로 시작하는 이벤트SQLEXCEPTION
: 경고
, not found
, 00
외의 모든 케이스
에러 코드 값
: 에러 번호 값
을 비교해서 실행사용자 정의 컨디션
handler_statements
처리 코드
declare continue handler for sqlexception set error_flag=1;
핸들러에서 본 sqlwarning
이나 sqlexception
같은 것이 MySQL이 미리 정의해 둔 컨디션
declare condition_name condition for condition_value
condition_value
에러 번호
를 입력하면 되지만, 여러 개 동시에 명시X
SQLSTATE
입력시 SQLSTATE 키워드
입력 후 값
입력declare dup_key condition for 1062;
시그널(SIGNAL)
: 사용자가 직접 예외
나 에러
를 발생시키는 것
create function sf_div(p_dividend int, p_divisor int)
returns int
begin
declare null_divisor condition for sqlstate '45000';
if p_divisor is null then
signal null_divisor
...~~
end;;
SIGNAL 명령
은 SQLSTATE
와 연결
돼야 함
declare exit handler for sqlexception
begin
signal sqlstate '45000'
set message_text='Can not remove user info', mysql_errorno=9999;
end;
JDBC의 결과 셋(ResultSet)
과 비슷하나 제약적
전진
읽기만 가능커서의 칼럼
을 바로 업데이트 하는 것
이 불가능
커서 구분
센서티브 커서
: 일치하는 레코드에 대한 정보를 실제 레코드의 포인터
만으로 유지
=> 칼럼의 데이터 변경
및 삭제
가능
=> 커서 오픈 빠름
인센서티브 커서
: 일치하는 레코드를 별도의 임시 테이블
로 복사
해서 가지고 있는 형태
=> 커서를 통해 변경
및 삭제
불가능
SELECT 문장
으로 커서 정의
하고, 커서를 오픈(OPEN)
하면 실제로 쿼리
가 MySQL에서 실행
되고 결과를 가져오며, 오픈된 커서는 페치(FETCH)
명령으로 레코드 단위로 읽어서
사용, CLOSE 명령
으로 커서 닫음
declare v_emp_list cursor for #커서 정의
select emp_no, from_date
from dept_emp
where dept_no=p_dept_no;
open v_emp_list; #커서 오픈
repeat
fetch v_emp_list into v_emp_no, v_from_date; #커서로부터 레코드를 한 개씩 읽음
...
until v_no_more_date end repeat;
close v_emp_list; #커서 닫음
declare 정의 순서
1. 로컬 변수
와 condition
2. cursor
3. handler
스토어드 프로그램
의 생성 및 변경 권한
은 create routine
과 alter routine
, execute
로 분리
트리거나 이벤트
는 trigger
과 event
권한으로 분리
DEFINER
: 기본 옵션, 해당 스토어드 프로그램의 소유권
과 같은 의미
SQL SECURITY
: 누구의 권한
으로 실행
할지 결정
=> INVOKER
: 스토어드 프로그램을 호출한 사용자
=> DEFINER
: 스토어드 프로그램을 생성한 사용자
관리자 계정
을 definer
로 설정하고, sql security
를 definer
로 설정하면, 스토어드 프로그램을 호출하는 사용자는 관리자 계정의 권한
으로 실행하게 됨
DETERMINISTIC
: 스토어드 프로그램의 입력이 같다
면 시점이나 상황에 관계없이 결과가 항상 같다
NOT DETERMINISTIC
: 입력이 같아도 시점에 따라 결과가 달라질
수 있음
create function sf_getdate1()
returns datetime
not deterministic
begin
return now();
end ;;
explain select * from dept_emp where from_date>sf_getdate1();;
create function sf_getdate2()
returns datetime
deterministic
begin
return now();
end ;;
explain select * from dept_emp where from_date>sf_getdate2();
deterministic
으로 정의된 함수는 쿼리를 실행하기 전 딱 한번만 스토어드 함수 호출
후, 결괏값을 상수화
해서 쿼리 실행
not deterministic
으로 정의된 함수는 where절
이 비교를 수행하는 레코드마다
매번 값이 재호출
됨
=> 인덱스 레인지 스캔 불가능
소스 코드
에 한글 문자열
값을 사용해야 한다면, MySQL 서버에 어떤 문자 집합
으로 접속돼 있는지가 중요
show variables like 'character%';
character_set_connection
과 character_set_client
세션 변수가 스토어드 프로그램
을 생성하는데 관여
로컬 변수
: declare
로 정의
사용자 변수
: @
로 시작
주로 로컬 변수
를 사용하는 것이 좋음
max_sp_recursion_depth
를 통해 재귀 호출
을 최대 몇 번
까지 허용할지 설정
중첩된 루프 안
에서 두 개의 커서
를 동시에 열어서
사용할 때도 있음
declare v_dept_list cursor for select dept_no from departments;
declare v_emp_list cursor for select emp_no
from dept_emp where dept_no=v_dept_no limit 1;
스토어드 프로시저 코드
의 처리 중 발생한 에러
나 예외
는 항상 가장 가까운 블록에 정의된 핸들러
가 사용됨
=> 서로 다른 begin .. end 블록
으로 구분해서 작성