실습


# 테이블 수정 삭제

1. department 테이블에서 location_id 열에 FOREIGN KEY 제약 조건 추가하시오.

더보기
ALTER TABLE department ADD CONSTRAINT FOREIGN KEY(location_id)
REFERENCES location(local_code);

 

2. location 테이블에서 national_code 열에 FOREIGN KEY 제약 조건 추가하시오.

더보기
ALTER TABLE location ADD CONSTRAINT FOREIGN KEY(national_code)
REFERENCES national(national_code);

 

# 조인

1. 테이블을 다중 JOIN 하여 사번, 직원명, 부서명, 지역명을 조회하시오.

더보기
SELECT e.emp_id AS '사번',
	e.emp_name AS '직원명',
	d.dept_title AS '부서명',
	l.local_name AS '지역명'

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
ORDER BY emp_name
;

2. 테이블을 다중 JOIN 하여 사번, 직원명, 부서명, 지역명, 국가명 조회하시오.

더보기
-- 부서가 있는 사원만
SELECT e.emp_id AS '사번',
	e.emp_name AS '직원명',
	d.dept_title AS '부서명',
	l.local_name AS '지역명',
	n.national_name AS '국가명'
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
;


-- 부서가 없는 사원도
SELECT e.emp_id AS '사번',
	e.emp_name AS '직원명',
	d.dept_title AS '부서명',
	l.local_name AS '지역명',
	n.national_name AS '국가명'
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
;

 

부서가 존재하는 사원만
부서가 존재하지 않는 사원

3. 테이블을 다중 JOIN 하여 사번, 직원명, 부서명, 지역명, 국가명, 급여 등급 조회하시오.

더보기
SELECT e.emp_id AS '사번',
	e.emp_name AS '직원명',
	d.dept_title AS '부서명',
	l.local_name AS '지역명',
	n.national_name AS '국가명',
	g.sal_level AS '급여 등급'
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
INNER JOIN sal_grade g ON e.salary BETWEEN g.min_sal AND g.max_sal
;

4. 부서가 있는 직원들의 직원명, 직급명, 부서명, 근무 지역명을 조회하시오.

더보기
SELECT e.emp_name AS '직원명',
	j.job_name AS '직급명',
	d.dept_title AS '부서명',
	l.local_name AS '근무 지역'
FROM employee e
LEFT OUTER JOIN job j ON e.job_code = j.job_code
LEFT OUTER JOIN department d ON e.dept_code = d.dept_id
LEFT OUTER JOIN location l ON d.location_id = l.local_code
;

5. 직급이 대리이면서 ASIA 지역에서 근무하는 직원들의 사번, 직원명, 직급명, 부서명, 근무지역, 급여를 조회하시오.

더보기
SELECT e.emp_id AS '사번',
	e.emp_name AS '직원명',
	j.job_name AS '직급명',
	d.dept_title AS '부서명',
	l.local_name AS '근무 지역',
	e.salary AS '급여'
FROM employee e
INNER JOIN job j ON e.job_code = j.job_code
INNER JOIN department d ON e.dept_code = d.dept_id
INNER JOIN location l ON d.location_id = l.local_code
WHERE l.local_name LIKE 'ASIA%' AND
	j.job_name = '대리'
;

6. 보너스를 받는 직원들의 직원명, 보너스, 연봉, 부서명, 근무지역을 조회하시오. (단, 부서 코드가 없는 사원도 출력될 수 있게 OUTER JOIN 사용)

더보기
SELECT e.emp_name AS '직원명',
	e.bonus AS '보너스',
	e.salary * 12 AS '연봉',
	d.dept_title AS '부서명',
	l.local_name AS '근무 지역'
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
WHERE bonus IS NOT NULL
;

7. 한국과 일본에서 근무하는 직원들의 직원명, 부서명, 근무지역, 근무 국가를 조회하시오.

더보기
SELECT e.emp_name AS '직원명',
	d.dept_title AS '부서명',
	l.local_name AS '근무 지역',
	n.national_name AS '근무 국가'
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 ('한국', '일본')
;

 

# DDL

0. 데이터베이스 생성

더보기
$ sudo mariadb -u root -p
# password 입력
$ create database university;
$ show databases;
$ grant all privileges on university.* to 'beyond'@'%';
$ show grants for 'beyond'@'%';

 

1. 학과(tb_department) 테이블을 생성하시오.

더보기
CREATE TABLE tb_department(
	department_no VARCHAR(10) PRIMARY KEY,
	department_name VARCHAR(20) NOT NULL,
	category VARCHAR(20) NULL,
	open_yn CHAR(1) NULL,
	capacity INT NULL
);

2. 학생(tb_student) 테이블을 생성하시오.

더보기
CREATE TABLE tb_student(
	student_no VARCHAR(10) PRIMARY KEY,
	department_no VARCHAR(10) NOT NULL REFERENCES tb_department(department_no),
	student_noame VARCHAR(30) NOT NULL,
	student_ssn VARCHAR(14) NULL,
	student_address VARCHAR(100) NULL,
	entrance_date DATE NULL,
	absence_yn CHAR(1) NULL,
	coach_professor_no VARCHAR(10) NULL
);
ALTER TABLE tb_student ADD CONSTRAINT FOREIGN KEY(coach_professor_no)
REFERENCES tb_professor(professor_no);

3. 과목(tb_class) 테이블을 생성하시오.

더보기
-- 과목 테이블(tb_class)
CREATE TABLE tb_class(
	class_no VARCHAR(10) PRIMARY KEY,
	department_no VARCHAR(10) NOT NULL REFERENCES tb_department(department_no),
	preattending_class_no VARCHAR(10), -- 외부
	class_name VARCHAR(30) NOT NULL,
	class_type VARCHAR(10)
);
ALTER TABLE tb_class ADD CONSTRAINT FOREIGN KEY(preattending_class_no)
REFERENCES tb_class(class_no);

4. 교수(tb_professor) 테이블을 생성하시오.

더보기
CREATE TABLE tb_professor(
	professor_no VARCHAR(10) PRIMARY KEY,
	professor_name VARCHAR(30) NOT NULL,
	professor_ssn VARCHAR(14),
	professor_address VARCHAR(100),
	department_no VARCHAR(10) REFERENCES tb_department(department_no)
);

5. 과목 교수(tb_class_professor) 테이블을 생성하시오.

더보기
-- 과목 교수 테이블(tb_class_professor)
CREATE TABLE tb_class_professor(
	class_no VARCHAR(10) REFERENCES tb_class(class_no),
	professor_no VARCHAR(10) REFERENCES tb_professor(professor_no),
	PRIMARY KEY(class_no, professor_no)
);

6. 학점( tb_grade) 테이블을 생성하시오.

더보기
CREATE TABLE tb_grade(
	tern_no VARCHAR(10) NOT NULL,
	class_no VARCHAR(10) NOT NULL REFERENCES tb_class(class_no),
	student_no VARCHAR(10) NOT NULL REFERENCES tb_student(student_no),
	`point` DECIMAL(3,2),
	 PRIMARY KEY(tern_no, class_no, student_no)
);

실습준비


# 실습 준비1

--  DDL for Table DEPARTMENT
CREATE TABLE department (	
    dept_id CHAR(2) PRIMARY KEY, 
    dept_title VARCHAR(35) NOT NULL
);

INSERT INTO department(dept_id,dept_title) VALUES ('D1','인사관리부');
INSERT INTO department(dept_id,dept_title) VALUES ('D2','회계관리부');
INSERT INTO department(dept_id,dept_title) VALUES ('D3','마케팅부');
INSERT INTO department(dept_id,dept_title) VALUES ('D4','국내영업부');
INSERT INTO department(dept_id,dept_title) VALUES ('D5','해외영업1부');
INSERT INTO department(dept_id,dept_title) VALUES ('D6','해외영업2부');
INSERT INTO department(dept_id,dept_title) VALUES ('D7','해외영업3부');
INSERT INTO department(dept_id,dept_title) VALUES ('D8','기술지원부');
INSERT INTO department(dept_id,dept_title) VALUES ('D9','총무부');

--  DDL for Table JOB
CREATE TABLE job (	
    job_code CHAR(2) PRIMARY KEY, 
	job_name VARCHAR(35) NOT NULL
);

INSERT INTO job(job_code,job_name) VALUES ('J1','대표');
INSERT INTO job(job_code,job_name) VALUES ('J2','부사장');
INSERT INTO job(job_code,job_name) VALUES ('J3','부장');
INSERT INTO job(job_code,job_name) VALUES ('J4','차장');
INSERT INTO job(job_code,job_name) VALUES ('J5','과장');
INSERT INTO job(job_code,job_name) VALUES ('J6','대리');
INSERT INTO job(job_code,job_name) VALUES ('J7','사원');


--  DDL for Table EMPLOYEE
CREATE TABLE employee (	
    emp_id CHAR(3) PRIMARY KEY, 
	emp_name VARCHAR(20) NOT NULL, 
	emp_no CHAR(14) NOT NULL, 
	email VARCHAR(25), 
	phone VARCHAR(12), 
	dept_code CHAR(2), 
	job_code CHAR(2), 
	salary INT, 
	bonus DOUBLE, 
	manager_id VARCHAR(3), 
	hire_date DATE, 
	ent_date DATE, 
	ent_yn CHAR(1) DEFAULT 'N'
) ;

INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('200','선동일','621235-1985634','sun_di@kh.or.kr','01099546325','D9','J1',8000000,0.3,null,'90/02/06',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('201','송종기','631156-1548654','song_jk@kh.or.kr','01045686656','D9','J2',6000000,null,'200','01/09/01',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('202','노옹철','861015-1356452','no_hc@kh.or.kr','01066656263','D9','J2',3700000,null,'201','01/01/01',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('203','송은희','631010-2653546','song_eh@kh.or.kr','01077607879','D6','J4',2800000,null,'204','96/05/03',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('204','유재식','660508-1342154','yoo_js@kh.or.kr','01099999129','D6','J3',3400000,0.2,'200','00/12/29',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('205','정중하','770102-1357951','jung_jh@kh.or.kr','01036654875','D6','J3',3900000,null,'204','99/09/09',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('206','박나라','630709-2054321','pack_nr@kh.or.kr','01096935222','D5','J7',1800000,null,'207','08/04/02',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('207','하이유','690402-2040612','ha_iy@kh.or.kr','01036654488','D5','J5',2200000,0.1,'200','94/07/07',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('208','김해술','870927-1313564','kim_hs@kh.or.kr','01078634444','D5','J5',2500000,null,'207','04/04/30',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('209','심봉선','750206-1325546','sim_bs@kh.or.kr','0113654485','D5','J3',3500000,0.15,'207','11/11/11',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('210','윤은해','650505-2356985','youn_eh@kh.or.kr','0179964233','D5','J7',2000000,null,'207','01/02/03',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('211','전형돈','830807-1121321','jun_hd@kh.or.kr','01044432222','D8','J6',2000000,null,'200','12/12/12',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('212','장쯔위','780923-2234542','jang_zw@kh.or.kr','01066682224','D8','J6',2550000,0.25,'211','15/06/17',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('213','하동운','621111-1785463','ha_dh@kh.or.kr','01158456632',null,'J6',2320000,0.1,null,'99/12/31',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('214','방명수','856795-1313513','bang_ms@kh.or.kr','01074127545','D1','J7',1380000,null,'200','10/04/04',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('215','대북혼','881130-1050911','dae_bh@kh.or.kr','01088808584','D5','J5',3760000,null,null,'17/06/19',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('216','차태연','770808-1364897','cha_ty@kh.or.kr','01064643212','D1','J6',2780000,0.2,'214','13/03/01',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('217','전지연','770808-2665412','jun_jy@kh.or.kr','01033624442','D1','J6',3660000,0.3,'214','07/03/20',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('218','이오리','870427-2232123','loo_or@kh.or.kr','01022306545',null,'J7',2890000,null,null,'16/11/28',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('219','임시환','660712-1212123','im_sw@kh.or.kr',null,'D2','J4',1550000,null,null,'99/09/09',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('220','이중석','770823-1113111','lee_js@kh.or.kr',null,'D2','J4',2490000,null,null,'14/09/18',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('221','유하진','800808-1123341','yoo_hj@kh.or.kr',null,'D2','J4',2480000,null,null,'94/01/20',null,'N');
INSERT INTO employee(emp_id,emp_name,emp_no,email,phone,dept_code,job_code,salary,bonus,manager_id,hire_date,ent_date,ent_yn) VALUES ('222','이태림','760918-2854697','lee_tr@kh.or.kr','01033000002','D8','J6',2436240,0.35,'100','97/09/12','17/09/12','Y');

COMMIT;

실습 테이블


# 실습 준비2

CREATE TABLE location(					
	local_code CHAR(2) PRIMARY KEY,
	national_code CHAR(2),
	local_name VARCHAR(40) NOT NULL
);

INSERT INTO location VALUES
('L1', 'KO', 'ASIA1'),
('L2', 'JP', 'ASIA2'),
('L3', 'CH', 'ASIA3'),
('L4', 'US', 'AMERICA'),
('L5', 'RU', 'EU');

SELECT * FROM location;

location 테이블 생성
location 테이블에 데이터 삽입


# 실습 준비3

CREATE TABLE `national`(
	national_code CHAR(2) PRIMARY KEY,
	national_name VARCHAR(35) NOT NULL
);

INSERT INTO national VALUES
('KO', '한국'),
('JP', '일본'),
('CH', '중국'),
('US', '미국'),
('RU', '러시아');

SELECT * FROM national;​

national 테이블 생성
national 테이블에 데이터 삽입


# 실습준비4

CREATE TABLE sal_grade(
	sal_level CHAR(2) PRIMARY KEY,
	min_sal INT,
	max_sal INT
);

INSERT INTO sal_grade VALUES
('S1', 6000000, 10000000),
('S2', 5000000, 5999999),
('S3', 4000000, 4999999),
('S4', 3000000, 3999999),
('S5', 2000000, 2999999),
('S6', 1000000, 1999999);

sal_grade 테이블 생성
sal_grade 테이블에 데이터 삽입

 

 

 

NON EQUAL JOIN


1. NON EQUAL JOIN (비등가 조인)

  • 다른 JOIN은 같은 지를 비교하지만 NON EQUAL JOIN은 조인 조건에 등호(=)를 사용하지 않는 조인
-- NON EQUAL JOIN 실습
-- 조인 조건에 등호(=)를 사용하지 않는 조인문을 비등가 조인이라고 한다.
-- employee 테이블과 sal_grade 테이블을 비등가 조인하여 직원명, 급여, 급여 등급 조회
-- INNER JOIN이기 때문에 조인 조건을 만족하는 경우가 아니면 출력이 안됨

SELECT e.emp_name,
		e.salary,
		s.sal_level
FROM employee e
INNER JOIN sal_grade s ON e.salary >= s.min_sal AND e.salary <= s.max_sal
# INNER JOIN sal_grade s ON e.salary between s.min_sal AND s.mas_sal과 결과 동일
;

급여를 기준으로 등급 출력

 

mariaDB 리눅스에 설치


1. 패키지 매니저 update/ upgrade

# 패키지 매니저 update
# 패키지 매니저의 목록을 update

sudo apt update

apt update

# update된 패키지들을 upgrade
sudp apt upgrade

apt upgrade

2. mariadb 설치

# mariad-server 설치
sudo apt install mariadb-server

mariadb-server 설치

# maraidb-client 설치
sudo apt install maraidb-client

mariadb-client 설치

# mariadb 버전 확인
mariaddb --version

 

 

3. mariadb 설치 확인

# maraidb 설치 확인

#1
netsstat -tnl | grep 3306
#2
systemctl status mariadb

#1
#2

sudo mariadb-secure-installation

4. 로그인 테스트

# mariadb 로그인 테스트
sudo mariadb -u root -p

로그인

# mariadb 버전 확인
select version();

# databases 확인
show databases;

버전과 데이터베이스 확인

# mysql 데이터베이스 접근
use mysql;

# 테이블 목록 확인
show tables;

mysql 데이터베이스에 접근하여 테이블 목록 확

5. 데이터베이스 생성

# 데이터 베이스 생성
create datavase tesetDB;

show tables;

testDB 생성

# testDB 접근
use testDB;

testDB 접근

# beyond 유저 생성
create user 'beyond'@'%' identified by 'beyond'

select host, user, password from mysql.user;

beyond 계정 생성 후 확인

# beyond 계정에게 testDB 모든 접근 권한 부여
grant all privileges on testDB.* to 'beyond'@'%';

show grants for 'beyond'@'%'

flush privileges;

권한 부여
권한 확인

5. mariadb 설정

# mariadb 설정

sudo vim /etc/mysql/mariadb.conf.d/50-server.cnf

bind-address 수정
외부 아이에서 접근이 가능하도록 수정

# mariadb 재부팅
sudo systemctl restart mariadb

6. HeidiSQL로 접근

mariadb 포트 포워딩 설정
세션 추가

 

뷰(View)


1. 뷰(View)

  • 뷰는 테이블과 똑같은 모습을 하고 있는 가상의 테이블
  • 뷰는 기본적으로 읽기 전용으로 많이 사용하지만, 뷰를 통해서 테이블의 데이터를 수정 가

2. 뷰의 생성

  • 뷰를 생성할 때는 CREATE VIEW 구문을 사용
CREATE [OR REPLACE] VIEW 뷰 이름 
AS 서브 쿼리
[WITH CHECK OPTION];
-- VIEW 실습
#1 뷰 생성
CREATE VIEW v_employee
AS SELECT e.emp_id,
	e.emp_name,
	d.dept_title,
	j.job_name,
	e.hire_date
FROM employee e
LEFT OUTER JOIN department d ON e.dept_code = d.dept_id
LEFT OUTER JOIN job j ON e.job_code = j.job_code;

#1


-- 사원의 사번, 직원명, 성별, 급여를 조회할 수 있는 뷰를 생성
#2
# OR REPLACE : 같은 이름의 뷰가 존재한다면 해당 뷰의 내용을 해당 쿼리문으로 덮어씀
# 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;

SELECT * FROM v_employee;

3. 뷰의 수정

  • 뷰를 수정할 때는 ALTER VIEW 구문을 사용
ALTER VIEW 뷰 이름 AS 서브 쿼리;
-- 뷰 수정

ALTER VIEW v_userbuy
AS SELECT u.userid,
	u.NAME,
	b.prodName,
	u.addr,
	CONCAT(u.mobile1, u.mobile2) AS 'mobile'
FROM usertbl u
INNER JOIN buytbl b ON u.userId = b.userID
;

4. 뷰의 삭제

  • 뷰를 삭제할 때는 DROP VIEW 구문을 사용
DROP VIEW 뷰 이름;

 

5. 뷰의 DML 작업

  • DML 작업이 가능한 경우
-- 뷰를 이용해서 DML(INSERT, UPDATE, DELETE) 사용

#1 v_job 생성

CREATE OR REPLACE VIEW v_job
AS SELECT *
	FROM job
;

 

#1 v_job 뷰 생


#2 VIEW INSERT (job_code = 'J8', job_name = '알바' 데이터 삽입)
INSERT INTO v_job VALUES('J8', '알바');

SELECT * FROM v_job;
SELECT * FROM job

#2 view에 데이터가 삽입됨

 

#3 원본 테이블에도 데이터가 삽입


#3 VIEW UPDATE (J8 직급명을 인턴으로 변경)

UPDATE v_job
SET job_name = '인턴'
WHERE job_code = 'J8'
;

#3 데이터의 내용이 변경됨
#3 원본 테이블 값도 함께 변경


#4 VIEW DELETE (J8 직급명인 데이터를 삭제)

DELETE
FROM v_job
WHERE job_code = 'J8'
;

#4 데이터가 삭제됨
#4 원본 테이블의 데이터도 함께 삭제

  • DML 조작이 불가능한 경우

1. 뷰 정의에 포함되지 않는 열을 조작하는 경우

#1 v_job 뷰 생성

CREATE OR REPLACE VIEW v_job
AS SELECT job_code
	FROM job
;
-- 1.INSERT
#1 VIEW에 정의된 열에 값을 삽입하는 것은 불가
#  제약 조건에 위배되는 값을 넣어도 에러 발생
INSERT INTO v_job VALUES('J8', '인턴');

#2 데이터 삽입이 가능
INSERT INTO v_job VALUES('J8');

-- 2.UPDATE
#1 v_job 뷰는 job_code만 접근할 수 있기 때문에 에러발생
UPDATE v_job
SET job_name = '인턴'
WHERE job_code = 'J8'
;

#2 데이터 수정이 가능
UPDATE v_job
SET job_code = 'J0'
WHERE job_code = 'J8'
;

-- 3.DELETE
#1 v_job 뷰는 job_code만 접근할 수 있기 때문에 에러발생
DELETE
FROM v_job
WHERE job_name IS NULL
;

#2
DELETE 
FROM v_job
WHERE job_code = 'J0'
;

 

 

2. 산술 표현법으로 정의 된 경우

#1 v_emp_salary 뷰 생성

-- 사원의 연봉 정보를 조회하는 뷰
CREATE VIEW v_emp_salary
AS SELECT emp_id,
			emp_name,
			emp_no,
			salary * 12 AS 'salary'
	FROM employee
;

-- 1.INSERT
#1 산술 연산으로 정의된 컬럼은 데이터 삽입이 불가능
INSERT INTO v_emp_salary
VALUES ('100', '홍길동', '940523-1111111', '30000000');

#2 산술 연산과 무관한 컬럼은 데이터 삽입 가능
INSERT INTO v_emp_salary(emp_id, emp_name, emp_no)
VALUES ('100', '홍길동', '940523-1111111');

-- 2.UPDATE
#1. 산술 연산으로 정의된 열은 데이터 변경 불가능
UPDATE v_emp_salary
SET salary = 30000000
WHERE emp_id = '100'
;
#2. 산술 연산과 무관한 열은 데이터 변경 가능
UPDATE v_emp_salary
SET emp_name = '고길동'
WHERE emp_id = '100'
;

-- 3.DELETE

# 가상으로 설정된 값을 입력하고 변경하는 것이 아닌
# 조회해서 삭제하는 것은 가능
DELETE
FROM v_emp_salary
WHERE salary = 36000000;

 

 

3. 그룹 함수나 GROUP BY  절을 포함한 경우(DISTINCT도 동일)

#1 v_emp_salary 뷰 생성
CREATE OR REPLACE VIEW v_emp_salary
	AS SELECT dept_code,
				FLOOR(SUM(salary)) AS 'sum',
				floor(AVG(salary)) AS 'avg'
		FROM employee
		GROUP BY dept_code
;

-- 1.INSERT
#1 그룹 함수로 생성된 열은 가상의 열이기때문에 삽입이 불가능
INSERT INTO v_emp_salary VALUES('D7', 8000000, 4000000);
#2 GROUP BY로 생성된 테이블의 dept_code열에는 삽입이 불가능
INSERT INTO v_emp_salary(dept_code) VALUES('D7');

-- 2.UPDATE
#1 그룹 함수로 생성된 열은 가상의 열이기때문에 수정이 불가능
UPDATE v_emp_salary
SET SUM = 6000000
WHERE dept_code = 'D2'
;

#2 GROUP BY로 생성된 테이블의 dept_code 열은 수정이 불가능
#	원본 테이블의 해당 dept_code인 열이 모두 수정 되기 때문
UPDATE v_emp_salary
SET dept_code = 'D7'
WHERE dept_code = 'D2'
;

-- 3.DELETE
#1 GROUP BY로 생성된 테이블의 dept_code 열은 삭제가 불가능
#	원본 테이블의 해당 dept_code인 열이 모두 삭제 되기 때문

DELETE
FROM v_emp_salary
WHERE dept_code = 'D2'
;

 

 

4. JOIN을 이용해 여러 테이블을 연결한 경우

-- 1.INSERT
#1. 에러발생
INSERT INTO v_emp_dept VALUES
(100, '홍길동', '000523-3123456',  '해외영업3부');

#2 에러발생
INSERT INTO v_emp_dept VALUES
(100, '홍길동', '000523-3123456',  '총무부');

#3 에러발생
INSERT INTO v_emp_dept(emp_id, emp_name, emp_no) VALUES
(100, '홍길동', '000523-3123456');

-- 2.UPDATE
#1 실행 가능
UPDATE v_emp_dept
SET dept_title = '총무1팀'
WHERE emp_id = '200'
;

#2 실행 가능
UPDATE v_emp_dept
SET emp_name = '서동일'
WHERE emp_id = '200'
;

-- 3.DELETE
#1 오류 발생
DELETE
FROM v_emp_dept
WHERE emp_id = '202'
;

DB 모델링

 


1. 데이터 모델링

  • 데이터 모델링은 업무보다는 데이터를 중심으로 모델링을 진행하는 방법
  • 데이터 모델링은 일반적으로 개념적 모델링, 논리적 모델링, 물리적 모델링 3가지 과정으로 분류

 

2. 개념적 모델링 

  • 요구분석 단계에서 정의된 핵심 개체와 그들 간의 관계를 바탕으로 ERD를 생성하는 단계
    • ERD :  Entity Relationship Diagram

2.1. 엔티티(Entity)

  • 엔티티란 실제로 관리해야 할 구체적인 대상을 의미
    • 유형 엔티티 : 물리적인 형태가 있는 엔티티
    • 무형 엔티티 : 물리적인 형태는 없지만 개념적으로 존재

 

2.2. 속성(Attribute)

  • 속성이란 엔티티가 가지고 있는 특징을 의미 (최소 데이터 단위)

2.2.1. 주 식별자
  • 엔티티에 있는 데이터들을 서로 구분을 해 줄 수 있는 속성을 의미  (기본키에 해당)
    • 반드시 값이 존재
    • 값이 중복되어서는 안됨
2.2.2. 보조 식별자
  • 보조 식별자는 주 식별자를 대체할 수 있는 또 다른 속성을 의미
2.2.3. 외래 식별자 (외래키에 해당)
  • 엔티티와의 관계를 연결해 주는 식별자를 외부 식별자라고 함
    • 값이 없어도 가능
    • 주 식별자 or 보조 식별자와 연결

 

2.3. 관계(Relationship)

2.3.1. 일대일(1:1, One To One) 관계
  • A 엔티티에 존재하는 1건의 데이터와 대응되는 B 엔티티의 데이터도 1건일 경우

학생tbl 학번 / 교수tbl번호 > 주 시별자 학생tbl > 외래 식별자

2.3.2. 일대다(1:M, One To Many) 관계
  • A 엔티티에 존재하는 1건의 데이터와 대응되는 B 엔티티의 데이터가 여러 건일 경우

2.3.3. 다대다(M:M, Many To Many) 관계
  • A 엔티티에 존재하는 여러 건의 데이터와 대응되는 B 엔티티의 데이터가 여러 건일 경우 M:M 관계

수강과목 tbl 학번, 학과번호 > 식별관계


식별관계 > 학번과 과목번호 모두 값이 존재해야지 데이터가 저장
비식별관계 > 학번과 과목번호 값이 존재하지 않아도 데이터가 저장

2.3.4. 식별, 비 식별 관계
  • 식별 관계는 부모 엔티티의 주 식별자를 자식 엔티티의 주 식별자, 외래 식별자로 사용하는 관계
  • 비 식별 관계는 부모 엔티티의 주 식별자를 자식 엔티티의 외래 식별자로만 사용하는 관계
  • 비 식별자로 설계하여 작업을 하다가 오류가 발생하면 식 별자로 사용

 

 

3. 논리적 모델링

 
  • 개념적 모델링 과정에서 추상화된 데이터를 구체화하여 개체, 속성을 테이블화하고 상세화 하는 과정

3.1. 이상 (Anomaly)

  • 정규화를 진행하지 않은 엔티티를 대상으로 삽입, 갱신, 삭제 시 발생할 수 있는 예기치 못한 현상을 이상이라고 함
  • 이상에는 삽입 이상, 갱신 이상, 삭제 이상이 존재

3.1.1. 삽입 이상

 

  • 주문에 대한 정보만 입력해도 제품에 대한 정보도 같이 입력해야 함
  • 제품에 대한 정보만 입력하고 싶을 때 주문에 대한 정보는 NULL 값을 가짐

3.1.2. 갱신이상

  • 동일한 제품명을 중복되어 있는 수만큼 수정해야함
  • 한번만 수정하거나 일부만 수정할 경우 일관성이 맞지 않는 문제가 발생

3.1.3. 삭제 이상

  • 주문에 대한 정보(주문 번호, 주문 수량)를 삭제할 때 제품 번호, 제품명, 단가도 같이 삭제

3.2. 제 1 정규화

  • 하나의 속성이 하나의 값을 갖도록 함

제 1 정규화 예시

3.3. 제 2 정규화

  • 주 식별자 전체에 종속적이지 않는 속성을 분리 (주 식별자가 복합 식별자일 경우)
    • 제 2정규화 이전에 제 1 정규화를 만족해야함

제 2 정규화 예시

 

3.4. 제 3 정규화

    • 주 식별자에 종속적이지 않고 다른 속성에 종속적인 속성을 분리

제 3 정규화 예시

 

 

4. 물리적 모델링

4.1. 물리적 모델링

  • 논리적 모델링 과정에서 표현된 데이터를 실제 데이터베이스에 맞도록 구현하는 과정

  • 엔티티의 속성은 모두 테이블의 컬럼으로 생성
  • 주 식별자는 모두 테이블의 Primary Key로 지정
  • 보조 식별자는 Unique Key로 지정
  • 외부 식별자는 Foreign Key로 지정
  • 추가로 필요한 객체(뷰, 인덱스 등)를 생성
더보기

<테이블 정의서 작성>

 

 

4.2. 반 정규화

  • 반 정규화란 이름 그대로 정규화의 반대
  • A 테이블과 B 테이블이 있을 때 두 테이블을 따로 분리해도 되고 합쳐도 되는 상황이라면 분리하는 것보다 합치는 것이 성능에 더 좋을 수 있음음

 

 

 

 

 

 

실습 문제

Q1. usertbl 테이블과 buytbl 테이블을 조인하여 JPY라는 아이디를 가진 회원의 이름, 주소, 연락처, 주문 상품 이름을 조회하시오.

더보기
SELECT u.NAME AS '이름',
	u.addr AS '주소',
	CONCAT(u.mobile1, u.mobile2) AS '연락처',
	b.prodName AS '주문 상품'
FROM usertbl u
INNER JOIN buytbl b ON u.userID = b.userID
WHERE u.userID = 'JYP';

 

Q2. employee 테이블과 department 테이블을 조인하여 보너스를 받는 사원들의 사번, 직원명, 보너스, 부서명을 조회하시오.

더보기
SELECT e.emp_id AS '사번',
	e.emp_name AS '직원명',
	e.bonus AS '보너스',
	d.dept_title AS '부서명'
FROM employee e
INNER JOIN department d ON e.dept_code = d.dept_id
WHERE e.bonus IS NOT NULL;

 

Q3. employee 테이블과 department 테이블을 조인하여 인사관리부가 아닌 사원들의 직원명, 부서명, 급여를 조회하시오.

더보기
SELECT e.emp_name AS '직원명',
	d.dept_title AS '부서명',
	e.salary AS '급여'
FROM employee e
INNER JOIN department d ON e.dept_code = d.dept_id
WHERE d.dept_title != '인사관리부';

 

Q4. employee 테이블과 department 테이블, job 테이블을 조인하여 사번, 직원명, 부서명, 직급명 조회하시오.

더보기
# ['employee' JOIN 'department'] JOIN 'job'
# 순서가 잘못되면 JOIN이 안될 수도 있음

SELECT e.emp_id AS '사번',
	e.emp_name AS '직원명',
	d.dept_title AS '부서명',
	j.job_name AS '직급명'
FROM employee e
INNER JOIN department d ON e.dept_code = d.dept_id
INNER JOIN job j ON e.job_code = j.job_code;

 

Q5. 70년대생 이면서 여자이고, 성이 전 씨인 직원들의 직원명, 주민번호, 부서명, 직급명을 조회하시요.

더보기
SELECT e.emp_name AS '직원명',
	e.emp_no AS '주민번호',
	d.dept_title AS '부서명',
	j.job_name AS '직급명'
FROM employee e
INNER JOIN department d ON e.dept_code = d.dept_id
INNER JOIN job j ON e.job_code = j.job_code
WHERE e.emp_no LIKE '7%'
	AND SUBSTRING(e.emp_no, 8, 1) = 2
	AND e.emp_name LIKE '전%'
;

 

Q6. 각 부서별 평균 급여를 조회하여 부서명, 평균 급여를 조회 단 부서 배치가 안된 사원들의 평균도 같이 나오게끔 조회하시오.

더보기
SELECT IFNULL(d.dept_title, '부서없음')AS '부서명',
	FLOOR(AVG(e.salary)) AS '평균 급여'
FROM employee e
LEFT OUTER JOIN department d ON e.dept_code = d.dept_ID
GROUP BY d.dept_title
ORDER BY d.dept_title
;

 

Q7. 각 부서별 총 급여의 합이 1000만원 이상인 부서명, 급여의 합을 조회하시오.

더보기
SELECT d.dept_title AS '부서명',
	SUM(e.salary) AS '급여의 합'
FROM employee e
INNER JOIN department d ON e.dept_code = d.dept_ID
GROUP BY d.dept_title
HAVING SUM(e.salary) >= 10000000
ORDER BY SUM(e.salary) DESC
;

 

Q8. 이름에 '형'자가 들어있는 직원들의 사번, 직원명, 직급명을 조회하시오.

더보기
SELECT e.emp_id AS '사번',
	e.emp_name AS '직원명',
	j.job_name AS '직급명'
FROM employee e
INNER JOIN job j ON e.job_code = j.job_code
WHERE e.emp_name LIKE '%형%';

 

Q9.  해외영업팀에 근무하는 직원들의 직원명, 직급명, 부서 코드, 부서명을 조회하시오.

더보기
SELECT e.emp_name AS '직원명',
	j.job_name AS '직급명',
	e.dept_code AS '부서코드',
	d.dept_title AS '부서명'
FROM employee e
INNER JOIN department d ON e.dept_code = d.dept_id 
INNER JOIN job j ON e.job_code = j.job_code
WHERE d.dept_title LIKE '해외영업%'
ORDER BY d.dept_title
;

 

Q10.  각 부서별 최소 급여를 받는 사원들의 사번, 이름, 부서코드, 급여를 조회하시오.

더보기
SELECT emp_id AS '사번',
	emp_name AS '사원명',
	IFNULL(dept_code, '부서없음') AS '부서코드',
	salary AS '급여'
FROM employee
WHERE (salary, IFNULL(dept_code, '부서없음')) IN (
	SELECT MIN(salary), IFNULL(dept_code, '부서없음')
	FROM employee
	GROUP BY dept_code
)
ORDER BY dept_code
;

 

Q11. 각 직급별로 최소 급여를 받는 사원들의 사번, 이름, 직급코드, 급여 조회하시오.

더보기
SELECT emp_id AS '사번',
	emp_name AS '사원명',
	job_code AS '직급코드',
	salary AS '급여'
FROM employee
WHERE (salary, job_code) IN (
	SELECT MIN(salary), job_code
	FROM employee
	GROUP BY job_code
)
ORDER BY job_code
;

조인(JOIN)과 UNION 연산자


1. 조인

  • 조인이란 두 개 이상의 테이블을 서로 묶어서 하나의 결과 집합으로 만들어 내는 것

 

2. 내부 조인(INNER JOIN)

  • 조인 중에서 가장 많이 사용되는 조인으로 일반적으로 조인이라고 이야기하는 것이 이 내부 조인(INNER JOIN)을 지칭하는 것
  • FROM 절 다음에 INNER JOIN 구문을 통해 조인에 사용할 테이블을 기술하고 ON 절에 조인 조건을 작성
SELECT *
FROM employees e
INNER JOIN departments d ON e.dept_no = d.dept_no;
<ex1>
-- INNER JOIN 실습
-- 각 사원들의 사번, 직원명, 부서코드, 부서명을 조회
# e와 d는 각각 employee 테이블과 department 테이블의 별칭

SELECT emp_id AS '사번',
	emp_name AS '직원명',
	dept_code AS '부서코드',
	dept_title AS '부서명'
FROM employee e
INNER JOIN department d ON dept_code = d.dept_id;


<ex2-1>
-- 각 사원들의 사번, 직원명, 직급 코드, 직급명을 조회
# job_code가 컬럼 이름이 두개의 테이블에서 모두 동일하기 때문에
# 에러가 발생

SELECT emp_id AS '사번',
	emp_name AS '직원명',
	job_code AS '직급 코드',
	job_name AS '직급명'
FROM employee
INNER JOIN job ON job_code = job_code;

 

에러 발생


<ex2-2>
-- 각 사원들의 사번, 직원명, 직급 코드, 직급명을 조회
-- 방법 1) 테이블명 언급

SELECT employee.emp_id AS '사번',
	employee.emp_name AS '직원명',
	employee.job_code AS '직급 코드',
	job.job_name AS '직급명'
FROM employee
INNER JOIN job ON employee.job_code = job.job_code;

방법 1


<ex2-3>
-- 각 사원들의 사번, 직원명, 직급 코드, 직급명을 조회
-- 방법 2) 별칭 사용

SELECT e.emp_id AS '사번',
	e.emp_name AS '직원명',
	e.job_code AS '직급 코드',
	j.job_name AS '직급명'
FROM employee e
INNER JOIN job j ON e.job_code = j.job_code;

방법 2


<ex2-4>
-- 각 사원들의 사번, 직원명, 직급 코드, 직급명을 조회
-- NATURAL JOIN : 동일한 컬럼명을 가지고 알아서 JOIN
-- 방법3) NATURAL JOIN > 하지만 잘 사용하지 않음

SELECT emp_id,
	emp_name,
	job_code,
	job_name
FROM employee
NATURAL JOIN job;

<ex2-4>


<ex3>
-- join 조건을 where 절에서도 사용이 가능
-- 하지만 join 조건은 INNER JOIN에, 검색 조건은 WHERE절에 기입하는걸 추천

SELECT e.emp_id,
	e.emp_name,
	j.*
FROM employee e
INNER JOIN job j
WHERE e.job_code = j.job_code;

<ex3>

더보기
#1
SELECT *
FROM employee e;

#2
SELECT *
FROM employee e
INNER JOIN department d ON e.dept_code = d.dept_id;
#1
#2

 

- #1의 실행결과는 23행이 조회되고 #2는 21행이 조회된다. 이유는 JOIN 조건에서 조건값이 NULL값인 행들은 JOIN이 되지 않기때문이다.

 

3. 외부 조인(OUTER JOIN)

  • 외부 조인(OUTER JOIN)은 조인의 조건에 만족되지 않는 행까지도 조회하기 위해서 사용되는 조인
  • LEFT OUTER JOIN은 왼쪽 테이블의 데이터는 모두 조회하려고 할 때 사용
SELECT *
FROM employees e
LEFT OUTER JOIN departments d ON e.dept_no = d.dept_no;
#1
-- OUTER JOIN과 비교할 INNER JOIN 구문
-- 부서가 지정되지 않은 사원 2명에 대한 정보가 조회되지 않음
-- 부서가 지정되어 있어도 부서에 속해있는 사원이 없으면 
-- 부서에 대한 정보가 조회되지 않는다.
SELECT e.emp_name,
	d.dept_title,
	e.dept_code,
	e.salary
FROM employee e
INNER JOIN department d ON e.dept_code = d.dept_id
ORDER BY e.dept_code;


#2 LEFT OUTER JOIN
-- employee테이블을 기준으로 조인
-- JOIN 조건에 부합되지 않아도 employee테이블은 모두 나옴
-- 부서 코드가 없던 사원의 정보가 출력된다.
SELECT e.emp_name,
	d.dept_title,
	e.dept_code,
	e.salary
FROM employee e
LEFT OUTER JOIN department d ON e.dept_code = d.dept_id
ORDER BY e.dept_code;

#1 부서가 지정되지 않은 사원의 정보는 조회되지 않음
#2 부서가 지정되지 않은 사원의 정보도 조회가 가능


  • RIGHT OUTER JOIN은 오른쪽 테이블의 데이터는 모두 조회하려고 할 때 사용
SELECT *
FROM employees e
RIGHT OUTER JOIN departments d ON e.dept_no = d.dept_no;
#1
-- OUTER JOIN과 비교할 INNER JOIN 구문
-- 부서가 지정되지 않은 사원 2명에 대한 정보가 조회되지 않음
-- 부서가 지정되어 있어도 부서에 속해있는 사원이 없으면 
-- 부서에 대한 정보가 조회되지 않는다.
SELECT e.emp_name,
	d.dept_title,
	e.dept_code,
	e.salary
FROM employee e
INNER 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_title,
	e.dept_code,
	e.salary
FROM employee e
RIGHT OUTER JOIN department d ON e.dept_code = d.dept_id
ORDER BY e.dept_code;

#1 부서가 지정되지 않은 사원의 정보는 조회되지 않음
#2 부서에 속해있는 사원이 없어도 부서에 대한 정보가 출력

 

4. 상호 조인(CROSS JOIN)

  • 상호 조인(CROSS JOIN)은 한쪽 테이블의 모든 행들과 다른 쪽 테이블의 모든 행을 조인
    • 거의 사용되지 않음
SELECT *
FROM employees e
CROSS JOIN departments d;
-- CROSS JOIN 실습
# 3개의 행을 가지고 있는 테이블A와 
# 3개의 행을 가지고 있는 테이블B를 CROSS JOIN을 하면
# 총 9개의 행이 나옴
# 즉, 테이블A 행 개수 * 테이블B 행 개수

# employee 테이블은 23행이고
# department 테이블은 9행이므로
# 207행이 출력
SELECT e.emp_name,
	d.dept_title
FROM employee e
CROSS JOIN department d
ORDER BY e.emp_name;

CROSS JOIN 결과 207행이 출

 

5. 자체 조인(SELF JOIN)

  • 자체 조인(SELF JOIN)은 동일한 테이블을 가지고 조인하여 데이터를 조회
    • 자기 자신과 JOIN
    • 테이블 별칭을 각각 다르게 부여하여 두개의 테이블을 구별하여 JOIN
SELECT *
FROM employees e
INNER JOIN employees M ON e.manager_no = m.emp_no;

 

<ex1>
-- SELF JOIN

#1
# manager_id = 사수의  emp_id
SELECT *
FROM employee;

-- employee 테이블을 SELF JOIN하여 사번, 사원 이름, 부서 코드,
-- 사수 사번, 사수 이름 조회
#2

SELECT e.emp_id AS '사번',
	e.emp_name AS '사원명',
	e.dept_code AS '부서코드',
	m.emp_id AS '사수 사번',		-- e.manager_id와 동일
	m.emp_name AS '사수명'
FROM employee e
INNER JOIN employee m ON e.manager_id = m.emp_id
;

#1 emp_id 와 manager_id 를 조건으로 JOIN할 것임
#2 R: 직원정보 B: 사수정보


<ex2>
-- 사수가 없는 사원의 정보도 출력할 수 있도록
-- LEFT OUTER JOIN과 SELF JOIN 함께 사용

SELECT e.emp_id AS '사번',
	e.emp_name AS '사원명',
	e.dept_code AS '부서코드',
	m.emp_id AS '사수 사번',
	m.emp_name AS '사수명'
FROM employee e
LEFT OUTER JOIN employee m ON e.manager_id = m.emp_id
;

사수가 없는 사원의 정보도 LEFT OUTER JOIN을 사용하여 조회

 

6. UNION/UNION ALL 연산자

  • UNION 연산자는 두 쿼리의 결과를 하나로 합치는 연산자
    • 쿼리 A에서 2개의 행, 쿼리 B에서 3개의 행이 조회되면 총 5개의 행을 출력
    • 두개의 쿼리문의 SELECT 절에서 사용한 컬럼의 개수가 동일하지 않으면 오류발생
  • UNION 연산자는 중복된 열은 제거되고 데이터가 정렬되어 조회
  • UNION ALL 연산자는 UNION 연산자와 다르게 중복된 열까지 모두 출력
SELECT *
FROM employees
WHERE gender = '남자'

UNION -- 또는 UNION ALL

SELECT *
FROM employees
WHERE salary > 3000000;
#0 UNION 연산자에 사용할 쿼리문

#0-1
-- employee 테이블에서 부서 코드가 D5인 사원들의 사번, 직원명, 부서코드,
-- 급여를 조회
SELECT emp_id AS '사번',
	emp_name AS '직원명',
	dept_code AS '부서코드',
	salary AS '급여'
FROM employee
WHERE dept_code = 'D5'
;

#0-2
-- employee 테이블에서 급여가 300만원 초과인 사원들의 사번, 직원명,
-- 부서 코드, 급여를 조회
SELECT emp_id AS '사번',
	emp_name AS '직원명',
	dept_code AS '부서코드',
	salary AS '급여'
FROM employee
WHERE salary > 3000000
;

# 0-1
# 0-2


#1 UNION 연산자
<ex 1>
SELECT emp_id AS '사번',
	emp_name AS '직원명',
	dept_code AS '부서코드',
	salary AS '급여'
FROM employee
WHERE dept_code = 'D5'


UNION

SELECT emp_id AS '사번',
	emp_name AS '직원명',
	dept_code AS '부서코드',
	salary AS '급여'
FROM employee
WHERE salary > 3000000
;


-- WHERE 절로 변경
-- UNION 연산자를 사용하여 조회한 것과 결과 동일
SELECT emp_id AS '사번',
	emp_name AS '직원명',
	dept_code AS '부서코드',
	salary AS '급여'
FROM employee
WHERE dept_code = 'D5' OR salary > 3000000

사번 209, 215인 정보는 두 쿼리문에 모두 동일하게 조회되기 때문에 중복은 제거하고 출


#2 UNION ALL
SELECT emp_id AS '사번',
	emp_name AS '직원명',
	dept_code AS '부서코드',
	salary AS '급여'
FROM employee
WHERE dept_code = 'D5'

UNION

SELECT emp_id AS '사번',
	emp_name AS '직원명',
	dept_code AS '부서코드',
	salary AS '급여'
FROM employee
WHERE salary > 3000000
;

#2 UNION ALL은 중복된 값도 제거하지 않고 출력

 

서브쿼리(SubQuery )


1. 서브쿼리(SubQuery)

  • 하나의  SQL문 안에 포함된 또 다른 SQL 문
-- 서브 쿼리 실습

#1. 노옹철 사원과 같은 부서원들을 조회

#1-1 노옹철 사원의 부서를 조회
SELECT emp_name, 
	dept_code
FROM employee
WHERE emp_name = '노옹철';


#1-2 부서 코드가 노옹철 사원의 부서 코드와 동일한 사원들을 조회
SELECT emp_name, 
	dept_code
FROM employee
WHERE dept_code = 'D9';


#1-3 위의 2단계를 서브 쿼리를 사용하여 하나의 쿼리문을 ㅗ작성
SELECT emp_name, 
	dept_code
FROM employee
WHERE dept_code = (
	SELECT dept_code
	FROM employee
	WHERE emp_name = '노옹철'
);

#1-1

 

#1-2
#1-3


#2
-- 전 직원의 평균 급여보다 더 많은 급여를 받고 있는 직원들의 사번, 
-- 직원명, 직급 코드, 급여를 조회
#2-1 전 직원의 평균 급여 조회
SELECT FLOOR(AVG(salary))
FROM employee
;

#2-2 평균 급여보다 더 많은 급여를 받고 있는 직원들을 조회
SELECT emp_id AS '사번',
	emp_name AS '사원명',
	job_code AS '직급 코드',
	salary AS '급여'
FROM employee
WHERE salary > 3047662
;

#2-3 위의 2단계를 서브 쿼리를 사용하여 하나의 쿼리문으로 작성
SELECT emp_id AS '사번',
	emp_name AS '사원명',
	job_code AS '직급 코드',
	salary AS '급여'
FROM employee
WHERE salary > (
	SELECT FLOOR(AVG(salary))
	FROM employee
);

#2-1
#2-2
#2-3

2. 서브 쿼리 구분

  • 서브 쿼리는 서브 쿼리를 수행한 결과값의 행과 열의 개수에 따라서 분류 가능
  • 단일행 서브쿼리 : 서브 쿼리의 조회 결과 값의 개수가 1개일 경우
#1
-- 노옹철 사원의 급여보다 더 많이 받는 사원의 사번, 직원명,
-- 부서명, 급여 조회

#1-1 노옹철 사원의 급여
SELECT salary
FROM employee
WHERE emp_name = '노옹철'
;

#1-2 노옹철 사원의 급여보다 더 ㅁ낳이 받는 사원을 조회
SELECT e.emp_id AS '사번',
	e.emp_name AS '사원명',
	d.dept_title AS '부서명',
	e.salary AS '급여'
FROM employee e
INNER JOIN department d ON e.dept_code = d.dept_id
WHERE salary > 3700000
;


#1-3 서브쿼리를 활용하여 #1-1과 #1-2를 취합
SELECT e.emp_id AS '사번',
	e.emp_name AS '사원명',
	d.dept_title AS '부서명',
	e.salary AS '급여'
FROM employee e
INNER JOIN department d ON e.dept_code = d.dept_id
WHERE salary > (
	SELECT salary
	FROM employee
	WHERE emp_name = '노옹철'
)
;

#1-1
#1-2
#1-3


  • 다중행 서브쿼리 : 서브 쿼리의 조회 결과 값의 개수가 여러 행일 경우
    • 다중행 서브쿼리는 조회 결과값이 여러개이기 때문에 단순비교 연산자를 사용 불가능(에러 발생)
    • ANY  연산자
    • ALL 연산자
#1
-- 각 부서별 최고 급여를 받는 직원의 이름, 직급 코드, 부서코드, 급여 조회

#1
-- 각 부서별 최고 급여를 받는 직원의 이름, 직급 코드, 부서코드, 급여 조회

#1-1 부서별 최고 급여 조회
SELECT MAX(salary) AS '해당 부서의 최고 급여'
FROM employee
GROUP BY dept_code
;


#1-2 위 급여를 받는 사원들을 조회
SELECT emp_name AS '사원명',
	job_code AS '직급코드',
	dept_code AS '부서 코드',
	salary AS '급여'
FROM employee
WHERE salary IN (2890000, 3660000, 2490000, 3760000, 3900000, 2550000, 8000000)
ORDER BY dept_code
;


#1-3 #1-1과 #1-2를 서브쿼리를 사용하여 취합
SELECT emp_name AS '사원명',
	job_code AS '직급코드',
	dept_code AS '부서 코드',
	salary AS '급여'
FROM employee
WHERE salary IN (
	SELECT MAX(salary) AS '해당 부서의 최고 급여'
	FROM employee
	GROUP BY dept_code
)
ORDER BY dept_code
;

#1-1
#1-2
#1-3


#2
-- 직원에 대해 사번, 이름, 부서코드, 구분(사원/사수) 조회

#2-1 사수에 해당하는 사번을 조회
SELECT DISTINCT manager_id AS '사수번호'
FROM employee
WHERE manager_id IS NOT NULL
;


#2-2 사수/ 사원을 구분하여 조회
SELECT emp_id AS '사번',
	emp_name AS '사원명',
	dept_code AS '부서코드',
	CASE WHEN emp_id IN (200, 201, 204, 207, 211, 214,100) THEN '사수'
		ELSE '사원'
	END AS '구분'
FROM employee
;

#2-3 SELECT 절에 서브 쿼리를 사용
SELECT emp_id AS '사번',
	emp_name AS '사원명',
	dept_code AS '부서코드',
	CASE WHEN emp_id IN (
		SELECT DISTINCT manager_id AS '사수번호'
		FROM employee
		WHERE manager_id IS NOT NULL
		) THEN '사수'
		ELSE '사원'
	END AS '구분'
FROM employee
;

#2-1
#2-2
#2-3 #2-2와 결과 동일


  • ANY 연산자는 서브쿼리의 결과 중 하나라도 조건을 만족하면 TRUE를 반환 
#1
-- 대리 직금임에도 과장 직급들의 최소 급여보다
-- 많이 맏는 직원의 사번, 이름, 직급코드, 급여 조회


#1-1 과장 직급들의 급여 조회
SELECT salary AS '급여'
FROM employee
WHERE job_code = 'J5'
;



#1-2 WHERE 절에 ANY를 이용하여 서브 쿼리를 사용
SELECT emp_id AS '사번',
	emp_name AS '이름',
	job_code AS '직급코드',
	salary AS '급여'
FROM employee
WHERE job_code = 'J6' AND 
	-- AND salary > 2200만 OR salary > 250만 OR salary > 376만 과 동일
    salary > ANY (
		SELECT salary AS '급여'
		FROM employee
		WHERE job_code = 'J5'
)
;

#1-1
#1-2


  • ALL 연산자는  서브쿼리의 결과 모두가 조건을 만족하면 TRUE를 반환한다.
    • = 기호는 논리적 오류가 있으므로 사용이 불가
#1
-- 과장 직급임에도 차장 직급의 최대 급여보다 더 많이 받는ㄴ
-- 직원들의 사번, 이름, 직급, 급여 조회

#1-1 차장 직급들의 급여 조회
SELECT salary
FROM employee e
INNER JOIN job j ON e.job_code = j.job_code
WHERE j.job_name = '차장'
;


#1-2 WHERE 절에 ANY를 이용하여 서브 쿼리를 사용
SELECT e.emp_id AS '사번',
	e.emp_name AS '사원명',
	j.job_name AS '직급명',
	e.salary AS '급여'
FROM employee e
INNER JOIN job j ON e.job_code = j.job_code
WHERE j.job_name = '과장'
	AND salary > ALL (			-- salary > 280만 AND salary > 155만 AND salary > 249만 AND salary > 248만
		SELECT salary
		FROM employee e
		INNER JOIN job j ON e.job_code = j.job_code
		WHERE j.job_name = '차장'
	)
;

#1-1
#1-2


  • 다중열 서브쿼리 : 서브 쿼리의 조회 결과 값은 한 행이지만 열의 수가 여러개일 경우
#1
-- 하이유 사원과 같은 부서코드, 직급 코드에 해당하는 사원들을 조회


#1-1 하이유 사원의 부서 코드와 직급 코드 조회
SELECT dept_code AS '부서코드',
	job_code AS '직급 코드'
FROM employee
WHERE emp_name = '하이유'
;


#1-2 부서 코드가 D5이면서 직급 코드가 J5인 사람들을 조회
SELECT emp_name AS '사원명',
	dept_code AS '부서코드',
	job_code AS '직급 코드'
FROM employee
WHERE dept_code = 'D5' AND job_code = 'J5'
;


#1-3 2개의 서브 쿼리를 사용하여 부서 코드가 D5이면서 직급 코드가 J5인 사람들을 조회
SELECT emp_name AS '사원명',
	dept_code AS '부서코드',
	job_code AS '직급 코드'
FROM employee
WHERE dept_code = (
	SELECT dept_code
	FROM employee
	WHERE emp_name = '하이유'
	) 
	AND job_code = (
		SELECT job_code
		FROM employee
		WHERE emp_name = '하이유'
)
;

#1-4 1개의 서브 쿼리를 사용하여 부서 코드가 D5이면서 직급 코드가 J5인 사람들을 조회
# 쌍 비교 방식 : (,) = (,)형식으로 여러개의 컬럼을 동시에 비교
SELECT emp_name AS '사원명',
	dept_code AS '부서코드',
	job_code AS '직급 코드'
FROM employee
WHERE (dept_code, job_code) = (				
				SELECT dept_code AS '부서코드',
						job_code AS '직급 코드'
				FROM employee
				WHERE emp_name = '하이유'
				)
;

#1-1
#1-2
#1-3
#1-4


#2
-- 박나라 사원과 직급 코드가 일치하면서 같은 사수를 가지고 있는
-- 사원의 사번, 이름, 직급 코드, 사수 사번 조회

#2-1 박나라 사원의 직급 코드와 사수의 사번을 조회
SELECT job_code AS '직급 코드',
	manager_id AS '사수 사번'
FROM employee
WHERE emp_name = '박나라'
;

#2-2 서브쿼리사용하여 작성
SELECT emp_id AS '사번',
	emp_name AS '사원명',
	job_code AS '직급 코드',
	manager_id AS '사수 사번'
FROM employee
WHERE (job_code, manager_id) = (
				SELECT job_code,
					manager_id
				FROM employee
				WHERE emp_name = '박나라'
				)
;

#2-1
#2-2


  • 다중행 다중열 서브쿼리 : 서브 쿼리의 조회 결과값이 여러 행, 여러 열일 경우
#1
-- 각 부서별 최고 급여를 받는 직원의 사번, 이름, 직급 코드, 부서코드, 급여 조회

#1-1 부서별 최고 급여 조회
SELECT dept_code AS '부서코드',
	MAX(salary) AS '최고급여'
FROM employee
GROUP BY dept_code;


#1-2 각 부서별 최고 급여를 받는 직원들의 정보 조회
# 비효울적
SELECT emp_id AS '사번',
	emp_name AS '사원명',
	job_code AS '직급코드',
	dept_code AS '부서코드',
	salary AS '급여'
FROM employee
WHERE dept_code = 'D1' AND salary = 3660000
	OR dept_code = 'D2' AND salary = 2490000
	OR dept_code = 'D5' AND salary = 3760000
	OR dept_code = 'D6' AND salary = 3900000
	OR dept_code = 'D8' AND salary = 2550000
	OR dept_code = 'D9' AND salary = 8000000
	OR dept_code IS NULL AND salary = 2890000
;

#1-3 다중행, 다중열 서브쿼리를 사용하여 각 부서별 최고 급여를 받는 직원들의 정보 조회
SELECT emp_id AS '사번',
	emp_name AS '사원명',
	job_code AS '직급코드',
	IFNULL(dept_code, '부서없음') AS '부서코드',
	salary AS '급여'
FROM employee
WHERE ( IFNULL(dept_code, '부서없음'), salary) IN (
				SELECT IFNULL(dept_code, '부서없음'),
					MAX(salary)
				FROM employee
				GROUP BY dept_code
				)
ORDER BY dept_code
;

#1-1
#1-2
#1-3

 


  • 인라인 뷰 : FROM절에 서브 쿼리를 제시하고, 서브 쿼리를 수행한 결과를 테이블 대신에 사용
#1
SELECT emp_id AS '사번',
	emp_name AS '사원명',
	salary AS '급여',
	salary * 12 AS '연봉'
FROM employee
;


SELECT e.사번,
	e.사원명,
	e.급여,
	e.연봉
FROM (
	SELECT emp_id AS '사번',
		emp_name AS '사원명',
		salary AS '급여',
		salary * 12 AS '연봉'
	FROM employee
)e;

#1 결과


-- employee 테이블에서 급여로 순위를 사원의 정보 출력

#2-1 employee 테이블에서 급여로 순위를 매겨서 출력
SELECT ROW_NUMBER() OVER (ORDER BY salary DESC) AS '순위',  
	emp_name AS '사원명',
	salary AS '급여'
FROM employee
;

#2-2 서브쿼리(인라인 뷰)를 사용하여 급여 순위 5위인 사원의 정보 출력
SELECT e.순위,
	e.사원명,
	e.급여
FROM (
	SELECT ROW_NUMBER() OVER (ORDER BY salary DESC) AS '순위',  
		emp_name AS '사원명',
		salary AS '급여'
	FROM employee
	)e
WHERE e.순위 = 5
;


#2-3 서브쿼리(인라인 뷰)를 사용하여 급여 순위 5위에서 10위인 사원의 정보 출력
SELECT e.순위,
	e.사원명,
	e.급여
FROM (
	SELECT ROW_NUMBER() OVER (ORDER BY salary DESC) AS '순위',  
		emp_name AS '사원명',
		salary AS '급여'
	FROM employee
	)e
WHERE e.순위 BETWEEN 5 AND 10
;

#2-1
#2-2
#2-3

'Database' 카테고리의 다른 글

[Database] 뷰(View)  (0) 2024.05.23
[Database] DB 모델링  (0) 2024.05.22
[Database] 데이터 형식(Data Type) & 함수(2)  (0) 2024.05.20
[Database] 데이터 형식(Data Type) & 함수 (1)  (0) 2024.05.17
[Database] SQL 기본 (2)  (0) 2024.05.17

실습 문제

Q1. 아래의 쿼리가 정상적으로 연산되도록 쿼리문을 작성하시오.

잘못된 결과 값

더보기

 

-- 아래의 쿼리가 정상적으로 연산되도록 쿼리문을 작성하시오.
SELECT '1,000,000' - '500,000'
-- 명시적 형변환
#1
SELECT CONVERT(REPLACE('1,000,000', ',', ''), INT) - CONVERT(REPLACE('500,000', ',', ''), INT)
#2
SELECT REPLACE('1,000,000', ',', '') - REPLACE('500,000', ',', '')

-- 묵시적 형변환
-- ,(콤마)를 제거하면 -연산자로 인해 값 그대로 형변환 가능
#3
SELECT '1000000' - '500000'
#1
#2
#3

 

Q2. employee 테이블에서 emp_id를 숫자 형식으로 변환해서 조회

더보기
-- employee 테이블에서 emp_id를 숫자 형식으로 변환해서 조회
SELECT CONVERT(emp_id, INT),
	emp_name
FROM employee;

+ Recent posts