-
(2주차 12일) 인덱스, 프로시저, 트리거수업 내용 정리 2024. 5. 24. 17:04
DB modeling
> 인덱스(Index)
- 테이블의 열 단위에 생성되는 데이터베이스의 개체로 데이터를 조회할 때 빠르게 접근하도록 도와주는 역할
- 클러스터형 인덱스(Clusterd Index): 테이블당 한 개만 생성 가능, 데이터가 정렬되어 저장 -> 검색 속도 빠름, 데이터 저장 느림
* 기본 키 제약조건으로 지정되는 열에 자동으로 생성됨 - 보조 인덱스(Secondary Index): 테이블당 여러 개 생성 가능, 정렬 필요 X -> 검색 속도 느림, 데이터 저장 빠름
* UNIQUE 제약 조건으로 지정되는 열에 자동으로 생성됨
- 클러스터형 인덱스(Clusterd Index): 테이블당 한 개만 생성 가능, 데이터가 정렬되어 저장 -> 검색 속도 빠름, 데이터 저장 느림
- B-트리(B-tree)
- 이진 트리를 확장해 하나의 노드가 가질 수 있는 자식 노드의 최대 숫자가 2보다 큰 트리 구조
- 노드에 저장된 KEY들은 항상 정렬된 상태로 저장됨
/* * 인덱스(Index) */ SELECT * FROM employees; EXPLAIN SELECT * FROM employees; SELECT * FROM employees; EXPLAIN SELECT * FROM employees WHERE emp_no = 499886; CREATE INDEX idx_employees_first_name ON employees(first_name); CREATE INDEX idx_employees_first_last_name ON employees(first_name, last_name); SELECT * FROM employees; EXPLAIN SELECT * FROM employees WHERE first_name = 'moon' AND last_name = 'Yetto'; ANALYZE TABLE employees; -- #테이블에 지정된 인덱스 확인 SHOW INDEX FROM employees; -- 인덱스 삭제 DROP INDEX idx_employees_first_name ON employees; DROP INDEX idx_employees_first_last_name ON employees; -- 인덱스 여부에 따라 쿼리 실행 시간에 유의미한 차이 CREATE TABLE emp_copy ( SELECT * FROM employees ); SELECT * FROM employees WHERE emp_no = 100000; SELECT * FROM emp_copy WHERE emp_no = 100000; EXPLAIN SELECT * FROM employees WHERE emp_no = 100000; EXPLAIN SELECT * FROM emp_copy WHERE emp_no = 100000; EXPLAIN SELECT * FROM employees WHERE emp_no < 11000; EXPLAIN SELECT * FROM emp_copy WHERE emp_no < 11000; -- #인덱스를 사용하지 않는 경우: 데이터 범위의 약 절반 이상 검색하는 경우 SELECT * FROM employees; SELECT * FROM emp_copy; EXPLAIN SELECT * FROM employees; EXPLAIN SELECT * FROM emp_copy; EXPLAIN SELECT * FROM employees WHERE emp_no < 400000; EXPLAIN SELECT * FROM emp_copy WHERE emp_no < 400000; -- #인덱스로 지정된 열 자체에 연산이 적용되는 경우 인덱스로 검색하지 않음 EXPLAIN SELECT * FROM employees WHERE emp_no * 1 = 100000; EXPLAIN SELECT * FROM emp_copy WHERE emp_no * 1 = 100000; -- 인덱스로 지정된 열의 검색값에 연산이 적용되는 경우 정상적으로 인덱스를 사용함 EXPLAIN SELECT * FROM employees WHERE emp_no = 100000 * 1; EXPLAIN SELECT * FROM emp_copy WHERE emp_no = 100000 * 1; -- #중복되는 열이 다량 있는 경우, 인덱스로 검색하지 않음 ALTER TABLE employees ADD INDEX idx_employees_gender (gender); ANALYZE TABLE employees; SHOW INDEX FROM employees; SELECT * FROM employees WHERE gender = 'M'; SELECT * FROM emp_copy WHERE gender = 'M'; EXPLAIN SELECT * FROM employees WHERE gender = 'M'; EXPLAIN SELECT * FROM emp_copy WHERE gender = 'M'; ALTER TABLE employees DROP INDEX idx_employees_gender;
> 스토어드 프로시저(Stored Procedure)
- MariaDB에서 제공되는 프로그래밍 기능으로, 쿼리문을 다시 입력할 필요 없이 호출해서 실행 결과를 얻을 수 있음
/* * 스토어드 프로시저(Stored Procedure) */ -- #프로시저 생성 DELIMITER $$ CREATE PROCEDURE userProc() BEGIN SELECT * FROM usertbl; END$$ DELIMITER ; CALL userProc(); -- #매개변수 사용 DELIMITER $$ CREATE OR REPLACE PROCEDURE userProc ( IN userName VARCHAR(10) ) BEGIN SELECT * FROM usertbl WHERE NAME = userName; END$$ DELIMITER ; CALL userProc('조관우'); -- e.g. 지역, 전화번호 앞자리 3자를 입력받아 usertbl 데이터를 반환 DELIMITER $$ CREATE OR REPLACE PROCEDURE userProc ( IN address VARCHAR(10), mobileFront VARCHAR(10) ) BEGIN SELECT * FROM usertbl WHERE address = addr and mobile1 = mobileFront; END$$ DELIMITER ; CALL userProc('경기', '018'); -- e.g. 사용자의 아이디를 입력받아 이름을 userName 변수에 반환 DELIMITER $$ CREATE OR REPLACE PROCEDURE userProc ( IN id CHAR(8), OUT userName CHAR(20) ) BEGIN SELECT NAME INTO userName FROM usertbl WHERE userid = id; END$$ DELIMITER ; CALL userProc('JYP', @NAME); SELECT @NAME; -- #제어문 -- IF DELIMITER $$ CREATE OR REPLACE PROCEDURE empProc ( IN id CHAR(3) ) BEGIN DECLARE YEAR INT; SELECT YEAR(hire_Date) INTO year FROM employee WHERE emp_id = id; IF YEAR >= 2010 THEN SELECT '2010년도에 입사하셨네요!'; ELSEIF YEAR > 2000 THEN SELECT '2000년도에 입사하셨네요!'; ELSE SELECT '1900년도에 입사하셨네요!'; END IF; END$$ DELIMITER ; CALL empProc('215'); -- CASE DELIMITER $$ CREATE OR REPLACE PROCEDURE gradeProc ( IN score INT ) BEGIN DECLARE grade CHAR(1); CASE WHEN score >= 90 THEN SET grade = 'A'; WHEN score >= 80 THEN SET grade = 'B'; WHEN score >= 70 THEN SET grade = 'C'; WHEN score >= 60 THEN SET grade = 'D'; ELSE SET grade = 'F'; END CASE; SELECT score AS '점수', grade AS '등급'; END$$ DELIMITER ; CALL gradeProc(80); DELIMITER $$ CREATE OR REPLACE PROCEDURE gradeProc ( IN score INT ) BEGIN DECLARE grade CHAR(1); IF score >= 90 THEN SET grade = 'A'; ELSEIF score >= 80 THEN SET grade = 'B'; ELSEIF score >= 70 THEN SET grade = 'C'; ELSEIF score >= 60 THEN SET grade = 'D'; ELSE SET grade = 'F'; END IF; SELECT score AS '점수', grade AS '등급'; END$$ DELIMITER ; CALL gradeProc(65); -- #반복문 -- WHILE DELIMITER $$ CREATE OR REPLACE PROCEDURE sumProc () BEGIN DECLARE i INT; DECLARE SUM INT; SET i = 1; SET SUM = 0; WHILE (i <= 10) DO SET SUM = SUM + i; SET i = i + 1; END WHILE; SELECT SUM; END$$ DELIMITER ; CALL sumProc(); -- e.g. 구구단 출력 DELIMITER $$ CREATE OR REPLACE PROCEDURE multiProc( IN num INT ) BEGIN DECLARE i INT; DECLARE result VARCHAR(100); SET i = 1; SET result = ''; WHILE(i < 10) DO SET result = CONCAT(result, num, ' * ', i, ' = ', num * 1, '\n'); SET i = i + 1; END WHILE; SELECT result; END$$ DELIMITER ; CALL multiProc(2); -- 오류처리 테스트 DELIMITER $$ CREATE OR REPLACE PROCEDURE errorProc() BEGIN DECLARE CONTINUE handler FOR 1146 SELECT '테이블이 존재하지 않습니다.' AS '메시지'; SELECT * FROM noTable; END$$ DELIMITER ; CALL errorProc(); DROP PROCEDURE empProc;
> 트리거(Trigger)
- 테이블에 삽입, 수정, 삭제 등의 작업이 발생할 때 자동으로 작동되는 개체
- AFTER 트리거
- BEFORE 트리거
/* * 트리거 */ -- 트리거 e.g. -- 상품 정보 테이블 생성 CREATE TABLE tb_product ( pcode INT AUTO_INCREMENT PRIMARY KEY, pname VARCHAR(100), brand VARCHAR(100), price INT, stock INT DEFAULT 0 ); INSERT INTO tb_product (pname, brand, price) VALUES ('아이폰12미니', '애플', 950000); INSERT INTO tb_product (pname, brand, price) VALUES ('갤럭시23', '삼성', 1000000); -- 2. 상품 입/출고 상세 이력 테이블 생성 CREATE TABLE tb_prodetail ( dcode INT AUTO_INCREMENT PRIMARY KEY, pcode INT REFERENCES tb_product, STATUS VARCHAR(2) CHECK(STATUS IN ('입고', '출고')), amount INT, ddate DATE DEFAULT CURDATE() ); -- 1번 상품이 2024-05-01 날짜로 10개 입고 INSERT INTO tb_prodetail(pcode, STATUS, amount, ddate) VALUES (1, '입고', 10, '2024-05-01'); -- 1번 상품의 재고 수량도 변경되어야 한다. UPDATE tb_product SET stock = stock + 10 WHERE pcode = 1; -- 1번 상품이 2024-05-02 날짜로 5개 출고 INSERT INTO tb_prodetail(pcode, STATUS, amount, ddate) VALUES (1, '출고', 5, '2024-05-02'); UPDATE tb_product SET stock = stock - 5 WHERE pcode = 1; -- 2번 상품이 2024-05-06 날짜로 20개 입고 INSERT INTO tb_prodetail(pcode, STATUS, amount, ddate) VALUES (2, '입고', 20, '2024-05-06'); -- 2번 상품의 재고 수량도 변경되어야 한다. UPDATE tb_product SET stock = stock + 20 WHERE pcode = 2; -- #tb_prodetail 테이블에 데이터 삽입 시 tb_product 테이블에 재고 수량이 -- 자동으로 업데이트 되도록 트리거를 생성 DELIMITER $$ CREATE OR REPLACE TRIGGER trg_pro_stock AFTER INSERT ON tb_prodetail FOR EACH ROW BEGIN -- 상품이 입고된 경우(재고 증가) IF NEW.status = '입고' THEN UPDATE tb_product SET stock = stock + NEW.amount WHERE pcode = NEW.pcode; END IF; -- 상품이 출고된 경우(재고 감소) IF NEW.status = '출고' THEN UPDATE tb_product SET stock = stock - NEW.amount WHERE pcode = NEW.pcode; END IF; END$$ DELIMITER ; -- 2번 상품이 2024-05-07 날짜로 20개 입고 INSERT INTO tb_prodetail(pcode, STATUS, amount, ddate) VALUES (2, '입고', 20, '2024-05-07'); SELECT * FROM tb_prodetail; SELECT * FROM tb_product; -- 2번 상품이 2024-05-07 날짜로 30개 출고 INSERT INTO tb_prodetail(pcode, STATUS, amount, ddate) VALUES (2, '출고', 30, '2024-05-07'); SELECT * FROM tb_prodetail; SELECT * FROM tb_product; -- 1번 상품이 오늘 날짜로 100개 입고 INSERT INTO tb_prodetail(pcode, STATUS, amount, ddate) VALUES (1, '입고', 100, DEFAULT); SELECT * FROM tb_prodetail; SELECT * FROM tb_product; -- 트리거 삭제 DROP TRIGGER trg_pro_stock; DROP TABLE tb_prodetail, tb_product;
'수업 내용 정리' 카테고리의 다른 글
(3주차 16일) Git, Sourcetree (0) 2024.05.28 (3주차 15일) Ubuntu, MariaDB (0) 2024.05.27 (2주차 11일) 테이블 실습 (0) 2024.05.23 (2주차 10일) DB 모델링, erdcloud, 테이블, 뷰 (0) 2024.05.22 (2주차 9일) SQL 함수, 조인, 서브쿼리 (0) 2024.05.21 - 테이블의 열 단위에 생성되는 데이터베이스의 개체로 데이터를 조회할 때 빠르게 접근하도록 도와주는 역할