MySQL 의 명령어 중 JOIN에 대한 설명과 예제코드에 대한 내용이다.
사용할 db 정보는 글 아랫부분에 있다.
JOIN
JOIN은 두개 이상의 테이블을 관련 있는 컬럼을 통해 결합하는데 사용된다. 두개 이상의 테이블은 반드시 연관 있는 컬럼이 존재해야 하며 이를 통해 JOIN된 테이블들의 컬럼을 모두 활용할 수 있다.
ALIAS
- SQL문의 컬럼 또는 테이블에 별칭을 달아줄 수 있다. 이러한 별칭을 ALIAS라고 한다.
- 컬럼 별칭
- resultSet의 컬럼명이 별칭으로 바뀜
- 별칭에 띄어쓰기나 특수 기호가 없다면 홑 따옴표(')와 AS는 생략 가능하다.
SELECT
menu_code AS 'code',
menu_name AS name,
menu_price 'price'
FROM
tbl_menu
ORDER BY price;
- 테이블 별칭
- 테이블에 별칭을 작성할 수 있으며 어떤 테이블 소속 인지를 쉽게 알 수 있게 한다.
- 테이블 별칭은 AS를 써도 되고 생략도 가능하다.
SELECT
a.category_code,
a.menu_name
FROM
-- tbl_menu AS a
tbl_menu a
ORDER BY
a.category_code,
a.menu_name;
JOIN의 종류
INNER JOIN
- 두 테이블의 교집합을 반환하는 SQL JOIN 유형
- INNER JOIN에서 INNER 키워드는 생략이 가능하다.
- ON을 활용한 JOIN
- 컬럼명이 같거나 다를 경우 ON으로 서로 연관 있는 컬럼에 대한 조건을 작성하여 JOIN하는 경우
- ON을 활용한 JOIN
SELECT
a.menu_name,
b.category_name
FROM
tbl_menu a
-- INNER JOIN tbl_category b ON a.category_code = b.category_code;
JOIN tbl_category b ON a.category_code = b.category_code;
- USING을 활용한 JOIN
- 컬럼명이 같을 경우 USING으로 서로 연관 있는 컬럼에 대한 조건을 작성하여 JOIN하는 경우
SELECT
a.menu_name,
b.category_name
FROM
tbl_menu a
INNER JOIN tbl_category b USING (category_code);
LEFT JOIN
- 첫 번째(왼쪽) 테이블의 모든 레코드와 두 번째(오른쪽) 테이블에서 일치하는 레코드를 반환하는 SQL JOIN 유형
SELECT
a.category_name,
b.menu_name
FROM
tbl_category a
LEFT JOIN tbl_menu b ON a.category_code = b.category_code;
RIGHT JOIN
- 두 번째(오른쪽) 테이블의 모든 레코드와 첫 번째(왼쪽) 테이블에서 일치하는 레코드를 반환하는 SQL JOIN 유형
SELECT
a.menu_name,
b.category_name
FROM
tbl_menu a
RIGHT JOIN tbl_category b ON a.category_code = b.category_code;
CROSS JOIN
- 두 테이블의 모든 가능한 조합을 반환하는 SQL JOIN 유형
SELECT
a.menu_name,
b.category_name
FROM
tbl_menu a
CROSS JOIN tbl_category b;
SELF JOIN
- 같은 테이블 내에서 행과 행 사이의 관계를 찾기 위해 사용되는 SQL JOIN 유형
- 카테고리별 대분류 확인을 위한 SELF JOIN 조회
SELECT
a.category_name,
b.category_name
FROM
tbl_category a
JOIN tbl_category b ON a.ref_category_code = b.category_code
WHERE a.ref_category_code IS NOT NULL;
JOIN 알고리즘
NESTED LOOP JOIN
- MySQL은 기본적으로 NESTED LOOP JOIN을 사용한다.
- 두 개 이상의 테이블에서 하나의 집합을 기준으로 순차적으로 상대방 Row를 결합하여 조합하는 방식
- 중첩 반복문처럼 첫 번째 테이블의 Row와 관련된 두 번째 테이블에 대한 Row를 검색하고 이후 첫 번째 테이블의 다음 Row에 대해 두 번쨰 테이블에 대한 것을 검색하며 이후 이와 같은 방식을 반복한다.
- MySQL은 기본적으로 NESTED LOOP JOIN을 사용하지만 이를 강제하려면 힌트절을 작성해 HASH JOIN을 사용하지 않도록 강제할 수 있다.
SELECT /*+ NO_HASH_JOIN(a) */
a.menu_name,
b.category_name
FROM
tbl_menu a
JOIN tbl_category b ON a.category_code = b.category_code;
HASH JOIN
- MySQL 8.0.18버전 이후 지원하게 되었다.
- 대규모 데이터 세트에 대한 조인 연산을 효과적으로 진행할 수 있다.
- 해싱 단계에서 조인을 수행하는 두 테이블 중 작은 쪽을 선택하여 해시 테이블을 만들어 메모리에 저장하고 해시 함수를 사용해서 각 행을 특정 "해시 버킷"에 할당한다.
- 조인 단계에서 다른 테이블을 순회하며 각 행에 대해 동일한 해시 함수를 사용하여 해당 행이 어떤 버킷에 속하는지 결정하고 이 버킷의 모든 행과 해당 행을 비교하여 조인 조건을 만족한다.
- 이 방법은 조인할 테이블 중 하나가 메모리에 적합할 만큼 충분히 작아야 한다. 그렇지 않으면 해시 테이블이 메모리를 넘어서 디스크로까지 넘어가고 이는 성능 저하를 초래한다.
- HASH JOIN은 등가 조인('=' 연산자를 사용하는 조인)에만 사용할 수 있고 비등가 조인에는 사용할 수 없다.
SELECT /*+ HASH_JOIN(a) */
a.menu_name,
b.category_name
FROM
tbl_menu a
JOIN tbl_category b ON a.category_code = b.category_code;
아래의 menu db와 employee db를 사용한 코드를 사용할 것 이다. ERD로 나타내면 아래와 같고 실습 전에 그림 밑에 MySQL에서 사용할 db의 script를 넣고 진행하면 된다.
MENU DB
- ERD (Entity Relationship Diagram)
- 논리모델
- 물리모델
use menudb;
-- 테이블 삭제
DROP TABLE IF EXISTS tbl_payment_order CASCADE;
DROP TABLE IF EXISTS tbl_payment CASCADE;
DROP TABLE IF EXISTS tbl_order_menu CASCADE;
DROP TABLE IF EXISTS tbl_order CASCADE;
DROP TABLE IF EXISTS tbl_menu CASCADE;
DROP TABLE IF EXISTS tbl_category CASCADE;
-- 테이블 생성
-- category 테이블 생성
CREATE TABLE IF NOT EXISTS tbl_category
(
category_code INT AUTO_INCREMENT COMMENT '카테고리코드',
category_name VARCHAR(30) NOT NULL COMMENT '카테고리명',
ref_category_code INT COMMENT '상위카테고리코드',
CONSTRAINT pk_category_code PRIMARY KEY (category_code),
CONSTRAINT fk_ref_category_code FOREIGN KEY (ref_category_code) REFERENCES tbl_category (category_code)
) ENGINE=INNODB COMMENT '카테고리';
CREATE TABLE IF NOT EXISTS tbl_menu
(
menu_code INT AUTO_INCREMENT COMMENT '메뉴코드',
menu_name VARCHAR(30) NOT NULL COMMENT '메뉴명',
menu_price INT NOT NULL COMMENT '메뉴가격',
category_code INT NOT NULL COMMENT '카테고리코드',
orderable_status CHAR(1) NOT NULL COMMENT '주문가능상태',
CONSTRAINT pk_menu_code PRIMARY KEY (menu_code),
CONSTRAINT fk_category_code FOREIGN KEY (category_code) REFERENCES tbl_category (category_code)
) ENGINE=INNODB COMMENT '메뉴';
CREATE TABLE IF NOT EXISTS tbl_order
(
order_code INT AUTO_INCREMENT COMMENT '주문코드',
order_date VARCHAR(8) NOT NULL COMMENT '주문일자',
order_time VARCHAR(8) NOT NULL COMMENT '주문시간',
total_order_price INT NOT NULL COMMENT '총주문금액',
CONSTRAINT pk_order_code PRIMARY KEY (order_code)
) ENGINE=INNODB COMMENT '주문';
CREATE TABLE IF NOT EXISTS tbl_order_menu
(
order_code INT NOT NULL COMMENT '주문코드',
menu_code INT NOT NULL COMMENT '메뉴코드',
order_amount INT NOT NULL COMMENT '주문수량',
CONSTRAINT pk_comp_order_menu_code PRIMARY KEY (order_code, menu_code),
CONSTRAINT fk_order_menu_order_code FOREIGN KEY (order_code) REFERENCES tbl_order (order_code),
CONSTRAINT fk_order_menu_menu_code FOREIGN KEY (menu_code) REFERENCES tbl_menu (menu_code)
) ENGINE=INNODB COMMENT '주문별메뉴';
CREATE TABLE IF NOT EXISTS tbl_payment
(
payment_code INT AUTO_INCREMENT COMMENT '결제코드',
payment_date VARCHAR(8) NOT NULL COMMENT '결제일',
payment_time VARCHAR(8) NOT NULL COMMENT '결제시간',
payment_price INT NOT NULL COMMENT '결제금액',
payment_type VARCHAR(6) NOT NULL COMMENT '결제구분',
CONSTRAINT pk_payment_code PRIMARY KEY (payment_code)
) ENGINE=INNODB COMMENT '결제';
CREATE TABLE IF NOT EXISTS tbl_payment_order
(
order_code INT NOT NULL COMMENT '주문코드',
payment_code INT NOT NULL COMMENT '결제코드',
CONSTRAINT pk_comp_payment_order_code PRIMARY KEY (payment_code, order_code),
CONSTRAINT fk_payment_order_order_code FOREIGN KEY (order_code) REFERENCES tbl_order (order_code),
CONSTRAINT fk_payment_order_payment_code FOREIGN KEY (order_code) REFERENCES tbl_payment (payment_code)
) ENGINE=INNODB COMMENT '결제별주문';
-- 데이터 삽입
INSERT INTO tbl_category VALUES (null, '식사', null);
INSERT INTO tbl_category VALUES (null, '음료', null);
INSERT INTO tbl_category VALUES (null, '디저트', null);
INSERT INTO tbl_category VALUES (null, '한식', 1);
INSERT INTO tbl_category VALUES (null, '중식', 1);
INSERT INTO tbl_category VALUES (null, '일식', 1);
INSERT INTO tbl_category VALUES (null, '퓨전', 1);
INSERT INTO tbl_category VALUES (null, '커피', 2);
INSERT INTO tbl_category VALUES (null, '쥬스', 2);
INSERT INTO tbl_category VALUES (null, '기타', 2);
INSERT INTO tbl_category VALUES (null, '동양', 3);
INSERT INTO tbl_category VALUES (null, '서양', 3);
INSERT INTO tbl_menu VALUES (null, '열무김치라떼', 4500, 8, 'Y');
INSERT INTO tbl_menu VALUES (null, '우럭스무디', 5000, 10, 'Y');
INSERT INTO tbl_menu VALUES (null, '생갈치쉐이크', 6000, 10, 'Y');
INSERT INTO tbl_menu VALUES (null, '갈릭미역파르페', 7000, 10, 'Y');
INSERT INTO tbl_menu VALUES (null, '앙버터김치찜', 13000, 4, 'N');
INSERT INTO tbl_menu VALUES (null, '생마늘샐러드', 12000, 4, 'Y');
INSERT INTO tbl_menu VALUES (null, '민트미역국', 15000, 4, 'Y');
INSERT INTO tbl_menu VALUES (null, '한우딸기국밥', 20000, 4, 'Y');
INSERT INTO tbl_menu VALUES (null, '홍어마카롱', 9000, 12, 'Y');
INSERT INTO tbl_menu VALUES (null, '코다리마늘빵', 7000, 12, 'N');
INSERT INTO tbl_menu VALUES (null, '정어리빙수', 10000, 10, 'Y');
INSERT INTO tbl_menu VALUES (null, '날치알스크류바', 2000, 10, 'Y');
INSERT INTO tbl_menu VALUES (null, '직화구이젤라또', 8000, 12, 'Y');
INSERT INTO tbl_menu VALUES (null, '과메기커틀릿', 13000, 6, 'Y');
INSERT INTO tbl_menu VALUES (null, '죽방멸치튀김우동', 11000, 6, 'N');
INSERT INTO tbl_menu VALUES (null, '흑마늘아메리카노', 9000, 8, 'Y');
INSERT INTO tbl_menu VALUES (null, '아이스가리비관자육수', 6000, 10, 'Y');
INSERT INTO tbl_menu VALUES (null, '붕어빵초밥', 35000, 6, 'Y');
INSERT INTO tbl_menu VALUES (null, '까나리코코넛쥬스', 9000, 9, 'Y');
INSERT INTO tbl_menu VALUES (null, '마라깐쇼한라봉', 22000, 5, 'N');
INSERT INTO tbl_menu VALUES (null, '돌미나리백설기', 5000, 11, 'Y');
COMMIT;
EMPLOYEE DB
- ERD (Entity Relationship Diagram)
- 논리모델
- 물리모델
use employeedb;
DROP TABLE IF EXISTS `EMPLOYEE`;
DROP TABLE IF EXISTS `DEPARTMENT`;
DROP TABLE IF EXISTS `JOB`;
DROP TABLE IF EXISTS `LOCATION`;
DROP TABLE IF EXISTS `NATION`;
DROP TABLE IF EXISTS `SAL_GRADE`;
CREATE TABLE SAL_GRADE
(
`SAL_LEVEL` CHAR(2) NOT NULL COMMENT '급여등급',
`MIN_SAL` DECIMAL COMMENT '최소급여',
`MAX_SAL` DECIMAL COMMENT '최대급여',
PRIMARY KEY ( `SAL_LEVEL` )
)
COMMENT = '급여등급';
INSERT INTO SAL_GRADE (SAL_LEVEL,MIN_SAL,MAX_SAL) VALUES
('S1',6000000,10000000),
('S2',5000000,5999999),
('S3',4000000,4999999),
('S4',3000000,3999999),
('S5',2000000,2999999),
('S6',1000000,1999999);
CREATE TABLE NATION
(
`NATIONAL_CODE` CHAR(2) NOT NULL COMMENT '국가코드',
`NATIONAL_NAME` VARCHAR(35) COMMENT '국가명',
PRIMARY KEY ( `NATIONAL_CODE` )
)
COMMENT = '국가';
INSERT INTO NATION (NATIONAL_CODE,NATIONAL_NAME) VALUES
('KO','한국'),
('JP','일본'),
('CH','중국'),
('US','미국'),
('RU','러시아');
CREATE TABLE `LOCATION`
(
`LOCAL_CODE` CHAR(2) NOT NULL COMMENT '지역코드',
`NATIONAL_CODE` CHAR(2) NOT NULL COMMENT '국가코드',
`LOCAL_NAME` VARCHAR(40) COMMENT '지역명',
PRIMARY KEY ( `LOCAL_CODE` ),
FOREIGN KEY (`NATIONAL_CODE`) REFERENCES NATION (`NATIONAL_CODE`)
)
COMMENT = '지역';
INSERT INTO LOCATION (LOCAL_CODE,NATIONAL_CODE,LOCAL_NAME) VALUES
('L1','KO','ASIA1'),
('L2','JP','ASIA2'),
('L3','CH','ASIA3'),
('L4','US','AMERICA'),
('L5','RU','EU');
CREATE TABLE `JOB`
(
`JOB_CODE` CHAR(2) NOT NULL COMMENT '직급코드',
`JOB_NAME` VARCHAR(35) COMMENT '직급명',
PRIMARY KEY ( `JOB_CODE` )
)
COMMENT = '직급';
INSERT INTO JOB (JOB_CODE,JOB_NAME) VALUES
('J1','대표'),
('J2','부사장'),
('J3','부장'),
('J4','차장'),
('J5','과장'),
('J6','대리'),
('J7','사원');
CREATE TABLE `DEPARTMENT`
(
`DEPT_ID` CHAR(2) NOT NULL COMMENT '부서코드',
`DEPT_TITLE` VARCHAR(35) NOT NULL COMMENT '부서명',
`LOCATION_ID` CHAR(2) NOT NULL COMMENT '지역코드',
PRIMARY KEY ( `DEPT_ID` ),
FOREIGN KEY (`LOCATION_ID`) REFERENCES LOCATION (`LOCAL_CODE`)
)
COMMENT = '부서';
INSERT INTO DEPARTMENT (DEPT_ID,DEPT_TITLE,LOCATION_ID) VALUES
('D1','인사관리부','L1'),
('D2','회계관리부','L1'),
('D3','마케팅부','L1'),
('D4','국내영업부','L1'),
('D5','해외영업1부','L2'),
('D6','해외영업2부','L3'),
('D7','해외영업3부','L4'),
('D8','기술지원부','L5'),
('D9','총무부','L1');
CREATE TABLE `EMPLOYEE`
(
`EMP_ID` VARCHAR(3) NOT NULL COMMENT '사원번호',
`EMP_NAME` VARCHAR(20) NOT NULL COMMENT '직원명',
`EMP_NO` CHAR(14) NOT NULL COMMENT '주민등록번호',
`EMAIL` VARCHAR(35) COMMENT '이메일',
`PHONE` VARCHAR(12) COMMENT '전화번호',
`DEPT_CODE` CHAR(2) COMMENT '부서코드',
`JOB_CODE` CHAR(2) NOT NULL COMMENT '직급코드',
`SAL_LEVEL` CHAR(2) NOT NULL COMMENT '급여등급',
`SALARY` DECIMAL COMMENT '급여',
`BONUS` float COMMENT '보너스율',
`MANAGER_ID` VARCHAR(3) COMMENT '관리자사번',
`HIRE_DATE` DATE COMMENT '입사일',
`ENT_DATE` DATE COMMENT '퇴사일',
`ENT_YN` CHAR(1) DEFAULT 'N' COMMENT '퇴직여부',
PRIMARY KEY ( `EMP_ID` ),
FOREIGN KEY (`DEPT_CODE`) REFERENCES DEPARTMENT (`DEPT_ID`),
FOREIGN KEY (`JOB_CODE`) REFERENCES JOB (`JOB_CODE`),
FOREIGN KEY (`SAL_LEVEL`) REFERENCES SAL_GRADE (`SAL_LEVEL`)
)
COMMENT = '사원';
INSERT INTO EMPLOYEE (EMP_ID,EMP_NAME,EMP_NO,EMAIL,PHONE,DEPT_CODE,JOB_CODE,SAL_LEVEL,SALARY,BONUS,MANAGER_ID,HIRE_DATE,ENT_DATE,ENT_YN) VALUES
('200','선동일','621235-1985634','sun_di@greedy.com','01099546325','D9','J1','S1',8000000,0.3,null,STR_TO_DATE('90/02/06','%y/%m/%d'),null,'N'),
('201','송종기','631156-1548654','song_jk@greedy.com','01045686656','D9','J2','S1',6000000,null,'200',STR_TO_DATE('01/09/01','%y/%m/%d'),null,'N'),
('202','노옹철','861015-1356452','no_oc@greedy.com','01066656263','D9','J2','S4',3700000,null,'201',STR_TO_DATE('01/01/01','%y/%m/%d'),null,'N'),
('203','송은희','631010-2653546','song_eh@greedy.com','01077607879','D6','J4','S5',2800000,null,'204',STR_TO_DATE('96/05/03','%y/%m/%d'),null,'N'),
('204','유재식','660508-1342154','yoo_js@greedy.com','01099999129','D6','J3','S4',3400000,0.2,'200',STR_TO_DATE('00/12/29','%y/%m/%d'),null,'N'),
('205','정중하','770102-1357951','jung_jh@greedy.com','01036654875','D6','J3','S4',3900000,null,'204',STR_TO_DATE('99/09/09','%y/%m/%d'),null,'N'),
('206','박나라','630709-2054321','pack_nr@greedy.com','01096935222','D5','J7','S6',1800000,null,'207',STR_TO_DATE('08/04/02','%y/%m/%d'),null,'N'),
('207','하이유','690402-2040612','ha_iy@greedy.com','01036654488','D5','J5','S5',2200000,0.1,'200',STR_TO_DATE('94/07/07','%y/%m/%d'),null,'N'),
('208','김해술','870927-1313564','kim_hs@greedy.com','01078634444','D5','J5','S5',2500000,null,'207',STR_TO_DATE('04/04/30','%y/%m/%d'),null,'N'),
('209','심봉선','750206-1325546','sim_bs@greedy.com','0113654485','D5','J3','S4',3500000,0.15,'207',STR_TO_DATE('11/11/11','%y/%m/%d'),null,'N'),
('210','윤은해','650505-2356985','youn_eh@greedy.com','0179964233','D5','J7','S5',2000000,null,'207',STR_TO_DATE('01/02/03','%y/%m/%d'),null,'N'),
('211','전형돈','830807-1121321','jun_hd@greedy.com','01044432222','D8','J6','S5',2000000,null,'200',STR_TO_DATE('12/12/12','%y/%m/%d'),null,'N'),
('212','장쯔위','780923-2234542','jang_zw@greedy.com','01066682224','D8','J6','S5',2550000,0.25,'211',STR_TO_DATE('15/06/17','%y/%m/%d'),null,'N'),
('213','하동운','621111-1785463','ha_dh@greedy.com','01158456632',null,'J6','S5',2320000,0.1,null,STR_TO_DATE('99/12/31','%y/%m/%d'),null,'N'),
('214','방명수','856795-1313513','bang_ms@greedy.com','01074127545','D1','J7','S6',1380000,null,'200',STR_TO_DATE('10/04/04','%y/%m/%d'),null,'N'),
('215','대북혼','881130-1050911','dae_bh@greedy.com','01088808584','D5','J5','S4',3760000,null,null,STR_TO_DATE('17/06/19','%y/%m/%d'),null,'N'),
('216','차태연','770808-1364897','cha_ty@greedy.com','01064643212','D1','J6','S5',2780000,0.2,'214',STR_TO_DATE('13/03/01','%y/%m/%d'),null,'N'),
('217','전지연','770808-2665412','jun_jy@greedy.com','01033624442','D1','J6','S4',3660000,0.3,'214',STR_TO_DATE('07/03/20','%y/%m/%d'),null,'N'),
('218','이오리','870427-2232123','lee_or@greedy.com','01022306545',null,'J7','S5',2890000,null,null,STR_TO_DATE('16/11/28','%y/%m/%d'),null,'N'),
('219','임시환','660712-1212123','im_sh@greedy.com',null,'D2','J4','S6',1550000,null,null,STR_TO_DATE('99/09/09','%y/%m/%d'),null,'N'),
('220','이중석','770823-1113111','lee_js@greedy.com',null,'D2','J4','S5',2490000,null,null,STR_TO_DATE('14/09/18','%y/%m/%d'),null,'N'),
('221','유하진','800808-1123341','yoo_hj@greedy.com',null,'D2','J4','S5',2480000,null,null,STR_TO_DATE('94/01/20','%y/%m/%d'),null,'N'),
('222','이태림','760918-2854697','lee_tr@greedy.com','01033000002','D8','J6','S5',2436240,0.35,'100',STR_TO_DATE('97/09/12','%y/%m/%d'),STR_TO_DATE('17/09/12','%y/%m/%d'),'Y');
commit;
ex)
SELECT a.category_name, b.menu_name
FROM tbl_category a
LEFT JOIN tbl_menu b ON a.category_code = b.category_code;
tbl_category a LEFT JOIN tbl_menu
LEFT JOIN 기준으로 '왼쪽'이 '베이스 테이블'.
RIGHT JOIN 기준으로 '오른쪽'이 '베이스 테이블'
베이스 테이블 ?
= 기준이 되는 테이블
'Database > Mysql' 카테고리의 다른 글
MySQL DB python 연동하는 법 (0) | 2025.02.20 |
---|---|
MySQL 명령어 : DML(INSERT, UPDATE, DELETE, REPLACE) (0) | 2025.02.20 |
MySQL 명령어 : LIMIT, GROUPING (0) | 2025.02.20 |
MySQL 명령어 : SELECT, ORDER BY, WHERE, DISTINCT (0) | 2025.02.19 |
Create USER & DATABASE (0) | 2025.02.19 |