스토어드 프로그램 장단점 장점 데이터베이스 보안향상 1.프로그램 단위로 실행 권한 부여가능 조합하면 칼럼단위의 권한 조절 가능 2.SQL-injection 등에 강함 ? 기능의 추상화 여러 프로그램에서 사용하는 일련번호 생성기 네트워크 소요 시간 절감 절차적 기능 구현 개발 업무 구분 단점 낮은 처리 성능 문자열 조작이나 숫자 계산등의 연산을 할경우 엄청 느리다 시간당 처리 횟수의 배수 c/c++ 88.6배 java 60.9배 oracle 2.1배 mySql 1.0 애플리케이션 코드의 조각화 문법 헤더 정의부 이름 입출력값 보안 작동방식 옵션 본문 실행되는 부분 *기본설정 함수와 괄호 사이의 공백 무시 IGNORE_SPACE thrad_stack = 512K 스토어드 프로시저 데이터를 주고 받아야 하는 여러쿼리를 하나의 묶음으로 사용하는것 *스토어드 프로시저는 반드시 독립적으로 생성 select or update문에서 프로시져 참조 불가 프로시저는 내용은 변경 못한다. drop 후 crate 해야한다. ALTER 프로시저의 경우 속성 변경에 사용 레이블 신택스 [begin_label:] BEGIN [statement_list] END [end_label] DELIMITER 프로시저의 시작과 종료를 알리기 위해 ';' 대신 사용 사용후 다시 바꿔야 한다. EX) DELIMITER $$ 프로시저.. END$$ DELIMITER ; ex) DELIMITER $$ CREATE PROCEDURE `test`.`sp_num` (IN param1 Integer, IN param2 Integer, OUT param3 Integer) BEGIN SET param3 = param1 + param2; END 실행 SET @result :=0; SELECT @result; CALL test.sp_num(1,2,@result); SELECT @result; *OUT, INOUT 파라미터는 세션변수를 만들어 넘겨주어야 한다. *프로그램 언어의 경우 세션 변수 없이 받아 올수 있음 *커서는 그냥 SELECT 할 경우 CLINET 언트에 전송된다(출력된다) 1.디버깅 용으로 사용 할 수 있음 2.두개 이상의 결과 셋을 반환 할수 도 있다 . ex1) CREATE PROCEDURE `employees`.`sp_selectEmps` (IN in_emp_no INTEGER) BEGIN SELECT * FROM employees WHERE emp_no = in_emp_no; END ex2) 실행 SET @result := 0; CALL sp_sum(1,2,@result); SELECT @result; 프로시저 CREATE PROCEDURE `employees`.`sp_sum` (IN in_int1 INTEGER, IN in_int2 INTEGER, OUT out_result INTEGER) BEGIN SELECT '> START' AS debug_message; SELECT CONCAT(' > P1', in_int1) AS debug_message; SELECT CONCAT(' > P2', in_int2) AS debug_message; SET out_result = in_int1 + in_int2; SELECT CONCAT(' > result', out_result) AS debug_message; SELECT '> END' AS debug_message; END 스토어드 함수 sql문장 일부에서 참고 가능 문법 1.입력 파람은 모두 읽기 전용이라 IN, OUT 을 선언 할수 없다. 2.return 값을 선언 해야 한다. 3.함수 본문에서 return 을 해야 한다. 함수에서 사용못하는 문법 1.prepare, execute 명령을 이용한 프리페어 문 2.명시적, 묵시적 RollBack/commit 3.재귀호출 4.함수 안에서 프로시져 호출 5.결과 셋을 반환하는 SQL안됨 *SELECT 를 사용 하면 클라이언트에 결과 셋이 반영되므로 INTO 를 꼭 쓰자 ex) CREATE FUNCTION `employees`.`sf_sum` (num1 Integer, num2 Integer) RETURNS INTEGER BEGIN DECLARE result INTEGER DEFAULT 0; SET result = num1 + num2; RETURN result; RETURN 1; 트리거 성능에 부하 걸림 5.16 이전 버전 슈퍼만 생성가능 5.x 하나의 이벤트에 최대 2개의 트리거 P659 이벤트(스케쥴러) 아래와 같이 하면 start 시간에 맞춰 실행된다. DELIMITER $$ ALTER EVENT `EVENT_DAILY_ARENA_RANK_USER_INFO_C` ON SCHEDULE EVERY 1 DAY STARTS '2013-04-03 17:36:00' DO BEGIN SET @o_sp_rtn := -1; CALL USP_BatchOperation_DAILY_ARENA_RANK_USER_INFO_C( @o_sp_rtn ); END$$ DELIMITER ; *이벤트를 inable 시켜줘야 된다. SELECT @@event_scheduler; SET GLOBAL event_scheduler = 1; 주어진 특성 시간에 스토어드 프로그램을 실행 스케쥴 처리 스래드를 뛰어 스래드가 처리함 파라미터 설정파일 event_scheduler = ON (or 1) *따로 로그를 만들지 않고 가장 최근 정보만 기록함 INFORMATION_SCHEMA.EVENTS 시작시간 확인 SELECT * FROM information_schema.`EVENTS` *실행 이력이 필요한 경우 테이블 생성 후 이벤트 처리 로직에서 기록 기록하는게 좋다 생성 반복성 이벤트 매일 01:00:00에 처리됨 CREATE EVENT daily_ranking ON SCHEDULE EVERY 1 DAY STARTS '2013-01-22 01:00:00' ENDS '2013-01-25 12:59:59' DO INSERT INTO test1 VALUE(1,201); 일회성 이벤트 * 현재 시간으로 부터 1시간뒤에 처리됨 CREATE EVENT one_time_job ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR DO INSERT INTO test1 VALUE(1,201); 본문 DO 단순한 SQL문 실행시 아니면 DO BEGIN 내용.. END;; ON COMPLETION 완정이 종료되면 삭제됨(DEFAULT) ON COMPLETION NOT PRESERVE 로 하면 삭제 안됨 확인 등록된 INFORMATION_SCHEMA.events에서 확인가능 마지막 실행된 mysql.event last_executed 마지막으로 실행된 *information_schema.event 와 mysql.event 는 관리되는 시간의 TIME_ZONE 이 다르기 때문에 시간이 조금 다르다. 같게 하려면 타임존 변환을 하면됨 P666확인 본문 문법 BEGIN..END 로 시작하며 BEGIN..END 가 중첩 될수 있다 mysql에서 트랜잭션 시작방법 BEGIN START TRANSACTION *하지만 프로시저류에서는 BEGIN 문법은 안된다. 스토어드 프로시저나 이벤트 본문에서만 사용가능 한 문법 *트랜잭션이 중일 경우 라도 프로시저에서 내부에서 COMMIT;을 하게되면 외부 트랜잭션까지 같이 종료됨 *즉 PROPERGATION 이 없음(전파 되지 않음) 트랜잭션 예제 CREATE PROCEDURE `employees`.`sp_hello` () BEGIN START TRANSACTION; INSERT INTO test value(1,2); COMMIT; END *단 프로시저에서 트랜잭션을 완료 할경우 APP에서의 트랜잭션 시작도 완료 되어 버린다.:) APP에서 할지 PROCEDURE에서 할지 확실이 결정 하자 * 많은 사용자가 프로시저에서 트랜잭션을 완료 할거라고 생각하지 않는다 고려하자!! 변수 선언(BEGIN..END 에서 사용하는) 스토어드 프로그램 로컬 변수 -> 로컬 변수 프리페어 스테이트 먼트를 사용하려면 반드시 사용자 변수를 사용 *사용자 변수보다 로컬 변수가 빠르다 사용자 변수를 너무 많이 쓰면 느려질 수 있다. 선언 DECARE v_name 자료형 DEFAULT 'value' 값 넣기 SET v_name = 'value', v_email = 'value@' SELECT emp_no, first_name, last_name INTO v_empno, v_firstname, v_lastname FROM employees WHERE emp_no = 10001 LIMIT 1; SELECT..INTO 는 반드시 하나의 레코드에만 사용 할 수 있다 없다거나 두개 이상이면 에러 발생 IF..ELSE IF ..ELSE ..END IF CREATE FUNCTION EMPLOYEES.`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..TEHN..ELSE.. END CASE 동등비교 에서 CASE 변수 WHEN 비교1 THEN 처리내용1 WHEN 비교2 THEN 처리내용2 ELSE 처리내용3 END CASE 일반 CASE WHEN 비교조건식1 THEN 처리내용1 WHEN 비교조건식2 THEN 처리내용2 ELSE 처리내용3 END CASE *BREAK 문 필요 없음 ex) CREATE FUNCTION `employees`.`sf_greatest1` (p_value1 INT, p_value2 INT) RETURNS INT BEGIN CASE WHEN p_value1 IS NULL THEN RETURN p_value2; WHEN p_value2 IS NULL THEN RETURN p_value1; WHEN p_value1 >= p_value2 THEN RETURN p_value1; ELSE RETURN p_value2; END CASE; END 루프문 WHILE LOOP REPEAT LOOP문 예제 CREATE FUNCTION `employees`.`sf_loop` (p_max INT) RETURNS INTEGER BEGIN DECLARE v_factorial INT DEFAULT 1; factorial_loop : LOOP SET v_factorial = v_factorial * p_max; SET p_max = p_max -1; IF p_max <= 1 THEN LEAVE factorial_loop; END IF; END LOOP; RETURN v_factorial; END REPEAT문 예제 CREATE FUNCTION `employees`.`sf_repeat` (p_max INT) RETURNS INTEGER BEGIN DECLARE v_factorial INT DEFAULT 1; REPEAT SET v_factorial = v_factorial * p_max; SET p_max = p_max -1; UNTIL p_max <= 1 END REPEAT; RETURN v_factorial; END WHILE문 예제 CREATE FUNCTION `employees`.`sf_while` (p_max INT) RETURNS INTEGER BEGIN DECLARE v_factorial INT DEFAULT 1; WHILE p_max > 1 DO SET v_factorial = v_factorial * p_max; SET p_max = p_max -1; END WHILE; RETURN v_factorial; END 핸들러와 컨디션을 통한 에러 핸들링 에러No ,sql state 에러 No Mysql에러 번호 sql state ansi 기준 에러 번호 *sql state 가 좀더 추상화 되어있다 그러므로 핸들링 할때 sql state 로 하는게 좋다. P679 핸들러 문법 DECLARE handler_type HANDLER FOR condtion_value [, condtion_value] .. handler_statements handler_type CONTINUE 핸들러 구문 실행 후 프로그램의 다음 구문 실행 EXIT 핸들러 구문 실행 후 선언된 BEGIN..END 종료 *반드시 함수의 반환 타입에 맞는 코드를 반환 하는 코드가 있어야함 condtion_value SQLSTATE *error no 는 버젼마다 다르니 sqlstat를 사용하자 00 정상처리 01 경고 02 not found SQLEXCEPTION NOT FOUND 와 SQLWARRING을(00,01,02) 제외한 으로 시작하는 모든값 *00000 은 사용말자 handler_statements 핸들러로 플로우가 넘어올 경우 처리 로직 작성 컨디션 ? 에러 번호는 해석이 힘드니까 에러 번호를 문자에 맵핑 하는것 문법 DECLARE condition_name CONDITION FOR condition_value condtion_value mysql error 번호일 경우 바로 기록 SQLSTATE 일경우 SQLSTATE를 입력하고 값을 입력 ex) DECLARE dup_key CONDTION FOR 1062; ex) CREATE FUNCTION sf_testfucn() RETURNS BIGINT BEGIN DECLARE dup_key CONDITION FOR 1062 DECLARE EXIT HANDLER FOR dup_key BEGIN RETURN -1; END; INSERT INTO tb_test VALUES(1); RETURN 1; END;; 시그널 사용자가 에러 생성 후 던짐(throw) 5.5미만 버전은 해당 문법이 없어 존재하지 않는 테이블을 일부로 셀렉트해서 에러 던지는 꽁수 사용 P685 주의 사항 1.SQLSTATE 사용 2.SQLSTATE 는 정수가 아닌 5자리 문자열이다 '' 사용하자 3.경고도 발생 시킬수 있다. 4.에러 코드 00은 정상이므로 쓰면안된다. 01은 경고 이므로 종료안시킴 그외의 값 에러 발생 5.일반적으로 유저 에러임으로 45로 시작하는 값을 사용하자 ex)p686 BEGIN DECLARE null_divisor CONDTION FOR SQLSTATE '45000' IF p_divisor IS NULL THEN SIGNAL null_divisor SET MESSAGE_TEXT = 'divisor can not be null', MYSQL_ERRNO=9999; ELSEIF p_divisor = 0 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'divisor can not be null', MYSQL_ERRNO=9999; ELSE SIGNAL SQLSTATE '01000' SET MESSAGE_TEXT = 'divisor can not be null', MYSQL_ERRNO=9997; RETUNR 0; END IF; *핸들러에서 에러를 받아 다시 집어 던지는것도 가능하다.p689 커서 JDBC의 리절트셋 과 비슷 커서 제한사항 1.스토어드 프로그램의 커서는 전방향(전진) 읽기만 가능 2.칼럼을 바로 업데이트하는것이 안됨 DBMS 커서의 종류 insensitive 레코드를 임시테이블로 복사해서 가지고 있는 형태 (복사해야 되기때문에 느리다) 2.복사했기 때문에 변경이나 삭제가 불가능하다 sensitive 레코드에 대한 포인터를 가지고 있다 변경할경우 바로 적용된다. insensitive + sensitive = Asensitive MySql은 두개를 복합적으로 사용함 하지만 유저는 어느 것인지 알 수 없기 때문에 커서를 사용해 레코드에 대한 변경이나 삭제는 불가능 하다. 사용 순서 1.커서 정의 후 2.오픈한면 실제로 실행되고 3.페치 명령으로 레코드를 읽을수 있으며 4.클로우즈로 닫는다. ex) DELIMITER ;; CREATE FUNCTION sf_emp_count(p_dept_no VARCHAR(10)) RETURNS BIGINT BEGIN /* 사원 번호가 20000보다 큰 사원의 수를 누적하기 위한 변수 */ DECLARE v_total_count INT DEFAULT 0; /* 커서에 더 읽어야 할 레코드가 남아 있는지 없는지 여부를 위한 플래그 변수 */ DECLARE v_no_more_data TINYINT DEFAULT 0; /* 커서를 통해서 SELECT된 사원번호를 임시로 담아 둘 변수 */ DECLARE v_emp_no INTEGER; /* 커서를 통해서 SELECT된 사원의 입사 일자를 임시로 담아 둘 변수 */ DECLARE v_from_date DATE; /* v_emp_list 라는 이름으로 커서 정의 */ DECLARE v_emp_list CURSOR FOR SELECT emp_no, from_date FROM dept_emp WHERE dept_no=p_dept_no; /* 커서로부터 더 읽을 데이터가 있는지 없는지 플래그 변경을 위한 핸들러 */ DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_no_more_data = 1; /* 정의된 v_emp_list 커서를 오픈 */ OPEN v_emp_list; REPEAT /* 커서로부터 레코드 한 개씩 읽어서 변수에 저장 */ FETCH v_emp_list INTO v_emp_no, v_from_date; IF v_emp_no > 20000 THEN SET v_total_count = v_total_count + 1; END IF; UNTIL v_no_more_data END REPEAT; /* v_emp_list 커서를 닫고 관련 자원을 반납 */ CLOSE v_emp_list; RETURN v_total_count; END ;; DELIMITER ; * 핸들러 사용 커서를 통해 읽다가 더이상 없으면 NOT FOUND 에러가 발생 하고 핸들러로 에서 no_more_date =1로 셋하고 continue 하기 때문에 REPEAT 문을 빠져나옴 *반드시 선언은 아래의 순서로 되어야 한다. 1.로컬변수와 컨디션 2.CURSOR 3.HANDLER 스토어드 프로그램의 권한과 옵션 DEFINDER = 소유자 SECUERTITY = DEFINDER, INVOKER DEFINDER 해당 프로시저를 생성한 사람의 권한으로 실행 (호출자가 테이블에 권한이 없어도 돌아감) IVCOKER 해당 프로시저를 호출 하는 자의 권한으로 실행 (호출자가 테이블에 권한 없으면 에러) *정의 하지 않으면 SECUERTITY = DEFINDER 로 default로 설됨 스토어드 프로시저(함수) 권한 SUPER 생성, 실행 가능 CREATE ROUTINE 함수 생성 가능 ALTER ROUTINE 조회, 삭제 가능 EXECUTEE 실행 가능 *권한은 DB나 OBJECT 단위로 부여가능 DETERMINISTIC, NOT DETERMINISTIC 함수에 정의 DETERMINISTIC 입력값이 같은면 결과는 늘 같다. NOT DETERMINISTIC 입력값이 같아도 시간에 따라 결과는 다르다. DEFAULT NOT DETERMINISTIC * 디펄트가 늘 변함 임으로 속도가 느려질수 있다 꼭 구분해서 사용하자 DETERMINISTIC 쿼리가 실행되면 그때 한번 호출하고 그 쿼리 안에서 상수값을 돌려줌 다시 쿼리가 호출되면 다시 실행됨 스토어드 프로그램에서 문자 깨지는 오류가 생기면 P700확인 사용자 변수 1.세션단위로 생김 2.로컬변수보다 느리다. 프리페어문이 아니라면 로컬 변수를 쓰자 *3.사용하기전에 초기화 해주자 세션 변수임으로 덮어 쓸 수 있다 4.타입이 없어서 위험 할수있다. 로컬변수 1.프로그램 실행시 생성 재귀호출 설정 max_sp_recursing_depth = 0 으로 되어있음 재귀 호출 쓸수 없다. 중첩커서 쓸때 블락안에서 블락을 만들어서 처리하자 그게 더 깔금하다