- DATA TYPE 정리
- 형변환함수
- 제어함수
- 문자열함수
- 수학함수
- 날짜-시간함수
- 순위-분석함수
> 기본 사용법
SELECT CONVERT(123456789, CHAR);
SELECT CAST(123456789 AS CHAR);
> REPLACE 함수와의 활용
SELECT CONVERT(REPLACE('10,000,000',',',''), INT);
> usertbl 테이블에서 mobile1의 데이터를 숫자 데이터로 형 변환 후 조회
SELECT NAME, convert(mobile1, INT), mobile1
FROM usertbl;
> maria db에서 convert나 cast는 매우 유용하지만 모든 데이터타입 변환이 가능한 것은
아니다.
select CONVERT(2024,smallint);
> LITERAL 연산
SELECT '100' + '200'; -- 정수로 변환되어 연산된다.
SELECT CONCAT('100','200'); -- 문자와 문자를 연결한다.
SELECT CONCAT(100,'200'); -- 정수가 문자로 변환되어 연결된다.
SELECT 1 > '2mega'; -- 산술 연산 -> '2mega' -> 2
SELECT 0 = 'mega'; -- 'mega' 변환 -> 문자로만 구성 -> 0으로 변환;
> ifnull과 NVL 함수
SELECT IFNULL(NULL,'값이 없음'), IFNULL(100, '값이 없음');
SELECT nvl(NULL, '값이 없음'), nvl(100,'값이 없음');
> NVL2 함수 -> NVL2('EXP', IF TRUE, IF FALSE);
SELECT nvl2(NULL, 100, 200), nvl2(300,100,200);
> case 연산자
SELECT case 10
when 1 then '일'
when 5 then '오'
when 10 then '십'
ELSE '모름'
END AS '결과';
SELECT case
when 10 > 20 then '10 > 20'
when 10 = 20 then '10 = 20'
ELSE '모름'
END AS '결과';
> concat, concat_ws 함수
SELECT CONCAT('2024', '12', '16'),
CONCAT_ws('/','2024', '12', '16');
> elt, field, find_in_set, instr, locate 함수
SELECT ELT(5,'하나', '둘', '셋', '넷','다섯'); // n번째 문자열을 출력한다.
SELECT FIELD('하나', '하나', '둘', '셋'); // 문자열들에서 해당 문자열의 위치를 출력
SELECT FIND_IN_SET('둘', '하나,둘,셋'); // 문자열에서 문자열의 위치를 출력
SELECT INSTR('하나둘셋', '하나'); // 문자열에서 문자열의 위치를 확인
SELECT LOCATE('구옥', '주악구옥주희'); // 문자열의 위치를 문자열에서 확인
> format 함수
SELECT FORMAT(1234567.89, 1); // 자릿수 표시와 함께 소숫점 반올림까지
> insert,left,right 함수
SELECT INSERT('abcdefghi', 3, 4, '####'); -> ab####ghi
SELECT LEFT('naver', 5); -> naver
SELECT RIGHT('baemin', 6); -> baemin
> lpad, rpad 함수
SELECT LPAD('hello',10); SELECT RPAD('hello',10,'#');
> ltrim, rtrim, trim, substring
SELECT LTRIM(' hello ');
SELECT RTRIM(' hello ');
SELECT TRIM(' hello ');
SELECT SUBSTRING('대한민국만세', 3 ,3);
SELECT SUBSTRING('대한민국만세' FROM 3);
SELECT SUBSTRING('대한민국만세' FROM 3 FOR 2);
SELECT SUBSTRING('대한밈ㄴ국만세' FROM -2 FOR 2);
> SUBSTRING_INDEX 함수
SELECT SUBSTRING_INDEX('cafe.naver.com', '.', 2),
SUBSTRING_INDEX('cafe.naver.com', '.', -2);
> ceilng, floor 함수
SELECT CEILING(4.3), FLOOR(4.7), ROUND(4.4), ROUND(4.5), ROUND(4.355, 2);
SELECT TRUNCATE(123.456, 2), TRUNCATE(123.45,-1); -> 123.45, 120
SELECT ADDDATE('2025-01-01', INTERVAL 10 DAY), ADDDATE('2025-01-01', INTERVAL 1 MONTH); -> '2025-01-11', '2025-02-01'
SELECT DATEDIFF(CURDATE(), '2024-05-25'),
TIMEDIFF(CURTIME(), '09:00:00');
> dayofweek, monthname, dayofyear, last_day 함수
SELECT DAYOFWEEK(CURDATE()),
MONTHNAME('2024-05-25'),
DAYOFYEAR(CURDATE()),
LAST_DAY(CURDATE());
> makedate, maketime, period_add, period_diff 함수
SELECT MAKEDATE(2025,100),
MAKETIME(22,58,59),
PERIOD_ADD(202402,12),
PERIOD_DIFF(202405,202505),
PERIOD_DIFF('2025-05', '2024-05');
> 순위 관련 함수
> order by height desc key로 내림차순 정렬 한 후에 row_number() 함수로 순번을 매긴다.
SELECT ROW_NUMBER() OVER(ORDER BY height DESC) AS 'num', NAME, addr, height
FROM usertbl
ORDER BY height DESC;
> 지역 별로 순위를 매겨서 주소, 순위, 이름, 키를 조회
SELECT addr, ROW_NUMBER() OVER(partition by addr order by height DESC) AS 'num', NAME, height
FROM usertbl;
> 동순위 발생 시 다음 등 수 생략
SELECT RANK() over(order by height DESC), NAME, height
FROM usertbl;
> 동순위 발생하여도 다음 등 수 이어감
SELECT dense_RANK() over(order by height DESC), NAME, height
FROM usertbl;
> 전체 인원을 키 순서로 세운 후에 4개의 그룹으로 분할
SELECT NTILE(4) OVER(ORDER BY height DESC) 'num', NAME, height
FROM usertbl;
> 분석 윈도우 함수
> usertbl 테이블에서 키 순서대로 정렬 후 다음 사람과 키 차이를 조회
SELECT NAME, addr, height,
height - LEAD(height, 1) OVER(ORDER BY height DESC) AS 'lead'
FROM usertbl
ORDER BY height DESC;
> usertbl 테이블에서 키 순서를 정렬 후 이전 사람과 키 차이를 조회
SELECT NAME, addr, height,LAG(height,1) OVER(ORDER BY height DESC) - height AS '키차이'
FROM usertbl;
> usertbl 지역별로 가장 키 큰 사람과 차이를 조회
SELECT addr, NAME, height, FIRST_VALUE(height) OVER(partition by addr ORDER BY height DESC) - height
FROM usertbl;
DB 공부하면서 제일 힘들었던 파트
- INNER JOIN
- OUTER-JOIN
- CROSS-JOIN/SELF-JOIN
- UNION-ALL
> 특정 조건을 만족하는 행들 끼리 하나의 행을 만든다.
> 23개의 employees -> 21개의 join (employee에서 dept_code 가 null인 행)
SELECT * FROM employee e INNER JOIN department d ON e.dept_code = d.dept_id;
> NATURAL JOIN: 동일한 이름의 열이 더 있을 때 -> join 조건이 이상해지고 원하지 않는 결과가 나올 수 있다.
SELECT emp_id, emp_name, job_code, job_name
FROM employee
NATURAL JOIN job;
> INNER JOIN에 WHERE 절 사용
SELECT emp_no, emp_name, bonus, d.dept_title
FROM employee e
INNER JOIN department d ON e.dept_code = d.dept_id
WHERE bonus IS not NULL;
> outer join 실습
> 1) left outer join (왼쪽의 테이블은 모두 포함)
SELECT * FROM employee e
LEFT /*OUTER*/ JOIN department d ON e.dept_code = d.dept_id
ORDER BY e.dept_code;
> 2) right outer join (부서에 속해있는 사원이 없어도 부서에 대한 정보가 출력된다.)
SELECT e.emp_name, d.dept_id, d.dept_title, e.salary FROM employee e
right /*OUTER*/ JOIN department d ON e.dept_code = d.dept_id
ORDER BY e.dept_code;
> cross join 23rows * 9rows -> 207rows
SELECT * FROM employee
CROSS JOIN department;
> self join -> inner join을 자기 자신( 하나의 테이블)로 진행
SELECT e.emp_id, e.emp_name, e.dept_code, m.emp_id '사수id', m.emp_name '사수 이름'
FROM employee e
> none equal join (비등가 조인)
> sal_grade에서 조건에 맞는 행이 mapping 된다.
SELECT employee.emp_id AS 'id', employee.emp_name AS 'name', sal_grade.sal_level AS 'salary_level'
FROM employee
INNER JOIN sal_grade ON employee.salary BETWEEN sal_grade.min_sal AND sal_grade.max_sal;
> 실습문제
-- 테이블을 다중 JOIN 하여 사번, 직원명, 부서명, 지역명, 국가명 조회
SELECT e.emp_id,
e.emp_name,
d.dept_title,
l.local_name,
n.national_name
FROM employee e
LEFT OUTER JOIN department d ON e.dept_code = d.dept_id
LEFT OUTER JOIN location l ON d.location_id = l.local_code
LEFT OUTER JOIN national n ON l.national_code = n.national_code;
-- 한국과 일본에서 근무하는 직원들의 직원명, 부서명, 지역명, 근무 국가를 조회하세요.
SELECT e.emp_name,
d.dept_title,
l.local_name,
n.national_name
FROM employee e
INNER JOIN department d ON e.dept_code = d.dept_id
INNER JOIN location l ON d.location_id = l.local_code
INNER JOIN national n ON l.national_code = n.national_code
WHERE n.national_name IN ('한국', '일본');
-- 1) UNION 연산자
SELECT emp_id,
emp_name,
dept_code,
salary
FROM employee
WHERE dept_code = 'D5'
UNION
SELECT emp_id,
emp_name,
dept_code,
salary
FROM employee
WHERE salary > 3000000;
-- 위 쿼리문 대신에 WHERE 절에 OR 연산자를 사용해서 처리가 가능하다.
SELECT emp_id,
emp_name,
dept_code,
salary
FROM employee
WHERE dept_code = 'D5' OR salary > 3000000;
-- 2) UNION ALL 연산자
SELECT emp_id,
emp_name,
dept_code,
salary
FROM employee
WHERE dept_code = 'D5'
UNION ALL
SELECT emp_id,
emp_name,
dept_code,
salary
FROM employee
WHERE salary > 3000000;
DB를 잘하자.
- SUB QUERY
- 단일행 서브쿼리
- 다중행 서브쿼리
- 다중열 서브쿼리
- 다중행 다중열 서브쿼리
- 인라인 뷰
> 서브 쿼리 실습
-- 하나의 sql 문 안에 또 다른 sql 문을 서브 쿼리라 한다.
-- 서브 쿼리 예시
-- 노웅철 사원과 같은 부서원들을 조회
-- 1) 노웅철 사원의 부서 코드를 조회
SELECT emp_name, dept_code
FROM employee
WHERE emp_name = '노옹철';
-- 2) 부서코드가 노옹철 사원의 부서 코드와 동일한 사원들을 조회
SELECT emp_name, dept_code
FROM employee
WHERE dept_code = 'D9';
SELECT emp_name, dept_code
FROM employee
WHERE dept_code = (
SELECT dept_code
FROM employee
WHERE emp_name = '노옹철');
-- 1) 단일행 서브 쿼리 서브쿼리 조회 결과 값의 개수가 1일 떄
-- 전 직원의 평균 급여 보다 더 많은 급여를 받고 있는 직원들의 사번, 직원명, 직급 코드, 급여를 조회
SELECT AVG(salary)
FROM employee;
SELECT emp_id, emp_name, job_code, salary
FROM employee
WHERE salary >= (SELECT AVG(salary) FROM employee);
-- 2) 다중행 서브 쿼리 서브 쿼리 조회 결과 값의 개수가 여러 행 일 떄
-- 각 부서별 최고 급여를 받는 직원의 이름, 직급 코드, 부서 코드, 급여 조회
-- 부서별 최고 급여 조회
SELECT max(salary), dept_code FROM employee
GROUP BY dept_code;
SELECT emp_name, job_code, dept_code, salary
FROM employee
WHERE salary IN (SELECT MAX(salary) FROM employee GROUP BY dept_code)
ORDER BY salary desc;
-- 직원들의 사번, 직원명, 부서 코드, 구분(사원/사수) 조회
SELECT DISTINCT manager_id
FROM employee
WHERE manager_id IS NOT NULL;
SELECT emp_id, emp_name, dept_code
, case
when emp_id IN (SELECT DISTINCT manager_id
FROM employee
WHERE manager_id IS NOT NULL) then '사수'
ELSE '사원'
END AS 구분
FROM employee;
-- 대리 직급에도 과장 직급들의 최소 급여보다 많이 받는
-- 직원의 사번, 이름, 직급 코드, 급여 조회
SELECT salary
FROM employee
WHERE job_code = 'J5';
SELECT min(salary)
FROM employee e
INNER JOIN job j ON e.job_code = j.job_code
WHERE j.job_name = '과장';
-- any는 서브 쿼리의 결과 값의 목록 중 하나라도 조건을 만족하면 참이 된다. (ALL은 모든 조건이 만족되어야 한다)
SELECT emp_id, emp_name, job_code, salary
FROM employee
WHERE job_code = 'J6' AND salary > ANY(
SELECT min(salary)
FROM employee e
INNER JOIN job j ON e.job_code = j.job_code
WHERE j.job_name = '과장');
-- 3) 다중열 서브 쿼리 서브 쿼리 조회 결과 1행에 여러 개의 열의 값일 때
-- 부서 코드가 D5이면서 직급 코드가 j5인 사원들을 조회
SELECT emp_name, dept_code, job_code
FROM employee
WHERE (dept_code, job_code) = (
SELECT dept_code, job_code
FROM employee
WHERE emp_name = '하이유'
);
-- 4) 다중행 다중열 서브 쿼리 서브 쿼리 조회 결과 여러 행 여러 열의 값이 조회 될 때
-- 각 부서별 최고 급여를 받는 직원의 사번, 직원명, 부서 코드, 급여 조회
-- 부서별 최고 급여 조회
SELECT dept_code, MAX(salary)
FROM employee
GROUP BY dept_code;
SELECT emp_no, emp_name, IFNULL(dept_code,'부서없음'), salary
FROM employee
WHERE (IFNULL(dept_code, '부서없음'), salary) IN (
SELECT IFNULL(dept_code, '부서없음'), MAX(salary)
FROM employee
GROUP BY dept_code
)
ORDER BY dept_code desc;
-- 인라인 뷰
-- from 절에 서브 쿼리 작성 및 서브 쿼리 수행 결과를 마치 테이블 처럼 사용
SELECT e.*
FROM (
SELECT emp_id AS '사번',
emp_name AS '이름',
salary AS '급여',
salary*12 AS '연봉'
FROM employee
) e;
-- employee 테이블에서 급여로 순위를 매겨서 출력
SELECT e.*
FROM (
SELECT ROW_NUMBER() OVER(ORDER BY salary DESC) AS 'num',
emp_name,
salary
FROM employee
) e
WHERE e.num BETWEEN 6 AND 10;
서브쿼리 까지 남일;ㅣ마ㅓㅇㄻ;ㅏㅣㄴㅇ럼ㅇ;라ㅣㅓ
- DB모델링
- 개념적모델링
- 논리적모델링
- 물리적모델링
1. 기본키는 릴레이션의 다른 모든 속성을 결정할 수 있어야 한다.
2. 기본키가 아닌 속성이 결정자일 때, 이상현상이 발생한다.
정규화는 이상현상이 있는 릴레이션을 분해하여 이상현상을 없애는 과정이다.
제1정규형
릴레이션의 모든 속성값을 원자값으로 한다.
제2정규형
제 1정규형을 만족하고, 릴레이션의 모든 속성이 기본키에 완전 함수 종속이 되게 한다.- 이때 기본키의 부분집합이 결정자 (부분 함수 종속) 가 되어서는 안된다.
제3정규형
제 2정규형을 만족하고, 릴레이션 내 속성 간 이행적 종속을 제거하고 분해하여 참조하도록 한다.- 기본키가 아닌 속성은 기본키에만 종속하도록 한다.
BCNF정규형
제 3정규형을 만족하고, 모든 결정자는 후보키여야 한다.
폼성일
- 테이블생성과 제약조건
- 테이블수정/삭제
- 뷰
- 인덱스
> DROP TABLE <-> CREATE TABLE
> DEFAULT KEYWORD를 통해 DEFAULT 값을 지정해 줄 수 있다
DROP TABLE `tb_member`;
CREATE TABLE `tb_member` (
`mem_no` INT NOT NULL,
`mem_id` VARCHAR(20) NOT NULL,
`mem_pass` VARCHAR(20) NOT NULL,
`mem_name` VARCHAR(15) NOT NULL,
`enroll_date` DATE DEFAULT CURDATE()
);
- NOT NULL : NULL 값을 입력 받지 않는다
- PRIMARY KEY: 유일성 + NOT NULL (UNIQUE + NOT NULL)
- UNIQUE: 유일성 (중복값 입력불가)
- CHECK: 입력 값 범위 또는 조건 지정
- REFERENCES: FOREIGN KEY 제약
- 제약조건에 이름을 설정할 수 있다. (컬럼 제약조건도 내부적으로는 ALIAS 참조 가능)
- CONSTRAINT
CON_NAME
UNIQUE(FIELD_NAME
);
> PRIMARY KEY 제약 조건의 경우 여러 개의 열을 묶어서 하나의 기본 키를 생성할 수 있다. 이 때, 한 열의 중복값은 허용된다.
> UNIQUE 제약 조건도 여러 개의 열을 묶어서 하나의 제약 조건으로 생성할 수 있다.
CREATE TABLE `tb_member` (
`mem_no` INT AUTO_INCREMENT, --> INT AUTO_INCREMENT 를 하나의 데이터타입처럼 사용
`mem_id` VARCHAR(20) NOT NULL,
`mem_pass` VARCHAR(20) NOT NULL,
`mem_name` VARCHAR(15) NOT NULL,
`enroll_date` DATE DEFAULT CURDATE(),
/* CONSTRAINT */ PRIMARY KEY(mem_no),
-- PRIMARY KEY(mem_no, mem_id),
CONSTRAINT uq_tb_member_mem_id UNIQUE(mem_id)
-- UNIQUE(mem_id, mem_id)
);
> ALTER TABLE : 열 또는 제약조건의 추가, 수정, 삭제, 이름 변경의 작업 진행
> DROP TABLE: TABLE을 삭제
-- usertbl 테이블에 gender 열을 추가 (단, 기본값을 남자로 지정)
ALTER TABLE usertbl ADD gender CHAR(10) DEFAULT 'male' NOT NULL;
-- usertbl 테이블에 age 열을 추가 (단, 기본값은 0으로, birthYear 뒤에 생성, 맨 앞은 first keyword)
ALTER TABLE usertbl ADD age TINYINT DEFAULT 0 AFTER birthyear;
-- usertbl 테이블에서 name 열의 데이터 유형을 CHAR(15)로 변경
ALTER TABLE usertbl MODIFY NAME CHAR(15) NULL;
-- 열의 이름을 변경
ALTER TABLE usertbl RENAME COLUMN `NAME` TO uname;
-- 위 내용을 한 번에 변경
ALTER TABLE usertbl CHANGE COLUMN NAME uname VARCHAR(20) DEFAULT '없음' NOT NULL;
ALTER TABLE usertbl DROP column age;
ALTER TABLE usertbl ADD CONSTRAINT `CONS_NAME` PRIMARY KEY('FIELD_NAME');
ALTER TABLE usertbl ADD CONSTRAINT 'CONS_NAME' FOREIGN KEY('FIELD NAME') REFERENCES 'TABLE_NAME'('FIELD_`NAME`');
ALTER TABLE tb_member ADD CONSTRAINT CHECK(gender = '남자' OR gender = '여자');
ALTER TABLE usertbl DROP CONSTRAINT PRIMARY KEY;
ALTER TABLE usertbl DROP CONSTRAINT `CONS_NAME`;
-- 테이블 삭제
DROP TABLE tb_member;
DROP TABLE tb_member_grade;
DROP TABLE tb_member, tb_member_grade;
-- 테이블 이름 변경
RENAME TABLE salary_grade TO sal_grade;
CREATE VIEW v1
AS SELECT emp_name, phone, d.dept_title FROM employee
LEFT OUTER JOIN department d ON employee.dept_code = d.dept_id;
SELECT * FROM v1;
> SELECT 절에 함수나 산술 연산이 기술되어 있는 경우 별칭을 지정해야 한다.
CREATE OR REPLACE VIEW v_employee
AS SELECT emp_id,
emp_name,
IF(SUBSTRING(emp_no, 8, 1) = '1', '남자', '여자') AS 'gender',
salary
FROM employee;
> 뷰를 이용해서 DML(INSERT, UPDATE, DELETE) 사용
CREATE VIEW v_job
AS SELECT *
FROM job;
SELECT job_code, job_name FROM v_job;
INSERT INTO v_job VALUES ('J8', '알바');
UPDATE v_job
SET job_name = '인턴'
WHERE job_code = 'J8';
DELETE
FROM v_job
WHERE job_code = 'J8';
> 산술 연산이 정의된 뷰의 데이터 수정 작업은 실행 되지 않는다.
CREATE VIEW v_emp_salary
AS SELECT emp_id,
emp_name,
emp_no,
salary * 12 AS 'salary'
FROM employee;
INSERT INTO v_emp_salary
VALUES ('300', '홍길동', '950525-1234567', 30000000);
UPDATE v_emp_salary
SET salary = 30000000
WHERE emp_id = '200';
SELECT * FROM v_emp_salary;
DROP VIEW v1;
추가적인 쓰기 작업과 저장공간을 활용하여 검색속도를 향상시키는 자료구조
인덱스를 사용하지 않는 컬럼은 조회 시 full scan을 진행한다.
인덱스를 사용하면 select 외에도 update,delete 작업에서 속도가 향상한다(조회가 포함되기 때문에)
또한 insert 작업 시, 새로운 데이터에 인덱스 추가, delete 시, 해당 데이터의 인덱스 사용하지 않음, update 시, 기존 인덱스 사용하지 않음/ 새로운 인덱스 사용과 같은 작업으로 부하가 추가 될 수 있다.
PRIMARY KEY에 CLUSTERD INDEX 하나가 기본으로 생성, 이외에는 보조 인덱스 (UNIQUE 제약 조건 하에는 보조 인덱스가 기본으로 생성)
인덱스의 장점
- 조회 속도로부터 성능 향샹
- 시스템의 부하를 줄여줌
인덱스의 단점- db 전체 저장공간의 10% 가량을 사용
- 인덱스 관리를 위한 추가적이 작업이 필요
- 잘못 사용 시, 오히려 성능이 저하
- CREATE, DELETE, UPDATE가 빈번한 테이블의 경우에는 인덱스 사용이 부적합하다, 이러한 쿼리가 잦아질 경우 인덱스가 비대해져 데이터보다 많은 공간을 차지 하게 될 수 있다.
인덱스를 사용하면 좋은 경우- 규모가 큰 테이블
- JOJN/WHERE/ORDERBY 절이 잦은 경우
- INSERT/UPDATE/DELETE 절이 적은 테이블
- 컬럼 내 중복 데이터가 적은 경우(B+TREE의 KEY/VALUE 구조 탐색이기 때문)
인덱스설계
CREATE OR REPLACE INDEX idx_firstname ON employees(first_name);
CREATE OR REPLACE INDEX idx_lastname ON employees(last_name);
ANALYZE TABLE employees;
SELECT * FROM employees WHERE first_name = 'moon';
EXPLAIN SELECT * FROM employees WHERE first_name = 'moon';
> index 를 사용했을 때, 조회하는 행의 개수가 대폭 줄어든다.
CREATE OR REPLACE INDEX idx_firstlast ON employees(first_name, last_name);
> 두 개 이상의 컬럼을 인덱스로써 활용할 수 있다.
EXPLAIN SELECT * FROM employees WHERE emp_no = 100000;
EXPLAIN SELECT * FROM employees WHERE emp_no * 1 = 100000;
> emp_no = 100000으로 조회 했을 때, 한 개 행을 조회한 반면, emp_no * 1과 같이 where 절에 이항 연산자를 사용했을 때, 모든 행을 조회하였다.
뷰르르