정규화된 데이터베이스에서는 데이터가 여러 테이블에 분산되어 있어 관련 정보를 함께 조회해야 하는 경우가 많습니다.
예시 테이블 구조
program 테이블
| id | 프로그램 | 가격 | 강사id |
|---|---|---|---|
| 1 | 스쿼시 | 5000 | 1 |
| 2 | 헬스 | 6000 | 2 |
| 3 | 골프 | 8000 | 3 |
| 4 | 골프중급 | 9000 | 3 |
| 5 | 개인피티 | 15000 | 4 |
teacher 테이블
| id | 강사 | 출신대학 |
|---|---|---|
| 1 | 김을용 | 서울대 |
| 2 | 박덕팔 | 연세대 |
| 3 | 이상구 | 고려대 |
-- 여러 테이블 동시 조회
SELECT 프로그램, 가격, 강사, 출신대학
FROM program, teacher;
-- 테이블명.컬럼명 형태로 명시 (권장)
SELECT program.프로그램, program.가격, teacher.강사, teacher.출신대학
FROM program, teacher;
조건 없이 여러 테이블을 조회하면 카르테시안 곱(Cartesian Product)이 발생합니다:
CROSS JOIN 결과 예시
| 프로그램 | 가격 | 강사id | 강사 | 출신대학 |
|---|---|---|---|---|
| 스쿼시 | 5000 | 1 | 김을용 | 서울대 |
| 스쿼시 | 5000 | 1 | 박덕팔 | 연세대 |
| 스쿼시 | 5000 | 1 | 이상구 | 고려대 |
| 헬스 | 6000 | 2 | 김을용 | 서울대 |
| ... | ... | ... | ... | ... |
SELECT program.프로그램, program.가격, teacher.강사, teacher.출신대학
FROM program, teacher
WHERE program.강사id = teacher.id;
올바른 결과
| 프로그램 | 가격 | 강사 | 출신대학 |
|---|---|---|---|
| 스쿼시 | 5000 | 김을용 | 서울대 |
| 헬스 | 6000 | 박덕팔 | 연세대 |
| 골프 | 8000 | 이상구 | 고려대 |
| 골프중급 | 9000 | 이상구 | 고려대 |
SELECT 출력할컬럼들
FROM 테이블1 INNER JOIN 테이블2
ON 조건문;
SELECT program.프로그램, program.가격, teacher.강사, teacher.출신대학
FROM program INNER JOIN teacher
ON program.강사id = teacher.id;
INNER JOIN의 장점:
-- 방법 1: FROM에 여러 테이블
SELECT *
FROM 테이블1, 테이블2, 테이블3
WHERE 조건1 AND 조건2;
-- 방법 2: 연속된 INNER JOIN (권장)
SELECT *
FROM 테이블1
INNER JOIN 테이블2 ON 조건1
INNER JOIN 테이블3 ON 조건2;
SELECT *
FROM program
LEFT JOIN teacher
ON program.강사id = teacher.id;
LEFT JOIN 결과
| id | 프로그램 | 가격 | 강사id | id | 강사 | 출신대학 |
|---|---|---|---|---|---|---|
| 1 | 스쿼시 | 5000 | 1 | 1 | 김을용 | 서울대 |
| 2 | 헬스 | 6000 | 2 | 2 | 박덕팔 | 연세대 |
| 3 | 골프 | 8000 | 3 | 3 | 이상구 | 고려대 |
| 4 | 골프중급 | 9000 | 3 | 3 | 이상구 | 고려대 |
| 5 | 개인피티 | 15000 | 4 | NULL | NULL | NULL |
SELECT *
FROM program
RIGHT JOIN teacher
ON program.강사id = teacher.id;
-- 매칭되지 않는 데이터 찾기
SELECT *
FROM program
LEFT JOIN teacher
ON program.강사id = teacher.id
WHERE teacher.id IS NULL; -- 강사 정보가 없는 프로그램
| JOIN 유형 | 결과 |
|---|---|
| INNER JOIN | 양쪽 테이블에 모두 있는 행만 |
| LEFT JOIN | 왼쪽 테이블 전체 + 매칭되는 오른쪽 행 |
| RIGHT JOIN | 오른쪽 테이블 전체 + 매칭되는 왼쪽 행 |
| FULL JOIN | 양쪽 테이블의 모든 행 (MySQL 미지원) |
| CROSS JOIN | 모든 조합 (조건 없음) |
INSERT INTO 테이블명 (컬럼명1, 컬럼명2, ...) VALUES (값1, 값2, ...);
-- 테이블 생성
CREATE TABLE product (
id INT AUTO_INCREMENT PRIMARY KEY,
상품명 VARCHAR(100),
가격 INT
);
-- 데이터 삽입
INSERT INTO product (id, 상품명, 가격) VALUES (1, '김치', 500);
-- AUTO_INCREMENT 컬럼 생략
INSERT INTO product (상품명, 가격) VALUES ('두부', 1000);
-- 모든 컬럼 입력 시 컬럼명 생략 가능
INSERT INTO product VALUES (3, '수박', 1500);
-- 여러 행 동시 삽입
INSERT INTO product VALUES
(4, '참외', 2000),
(5, '배추', 2500);
-- SELECT 결과를 다른 테이블에 삽입
INSERT INTO product (id, 상품명, 가격)
SELECT id, 상품명, 가격 FROM product2;
-- 테이블 전체 복사 (MySQL)
CREATE TABLE 새테이블 SELECT * FROM 기존테이블;
-- 임시 테이블 생성
CREATE TEMPORARY TABLE 임시테이블 SELECT * FROM 기존테이블;
UPDATE 테이블명
SET 컬럼1 = 값, 컬럼2 = 값
WHERE 조건식;
-- 특정 행 수정
UPDATE product
SET 가격 = 5000, 상품명 = '단무지'
WHERE id = 1;
-- 기존 값 활용한 수정
UPDATE product
SET 가격 = 가격 + 100 -- 기존 가격에 100원 추가
WHERE id = 1;
-- 조건부 수정 (IF 함수 활용)
UPDATE user_sales
SET email = IF(first_name = 'Solly', 'admin@test.com', 'test@test.com')
WHERE email = '';
UPDATE A INNER JOIN B
ON 조인조건
SET A.컬럼 = 값, B.컬럼 = 값
WHERE 조건식;
DELETE FROM 테이블명 WHERE 조건식;
-- 특정 조건의 행 삭제
DELETE FROM product WHERE id = 5;
-- NULL 값을 가진 행 삭제
DELETE FROM user_sales WHERE sales IS NULL;
DELETE A
FROM A INNER JOIN B
ON 조인조건
WHERE 조건식;
-- A 테이블에서만 삭제: DELETE A
-- B 테이블에서만 삭제: DELETE B
-- 양쪽 모두 삭제: DELETE A, B
중요한 주의사항:
WHERE 조건 빼먹으면 모든 행이 삭제됨SELECT 컬럼1, 컬럼2 FROM 테이블1
UNION
SELECT 컬럼1, 컬럼2 FROM 테이블2;
-- 두 테이블의 모든 데이터 합치기
SELECT * FROM stock
UNION
SELECT * FROM stock2;
-- 같은 테이블의 다른 조건 결과 합치기
SELECT * FROM stock WHERE id = 1
UNION
SELECT * FROM stock WHERE id = 2;
-- UNION: 중복 제거
SELECT * FROM stock
UNION
SELECT * FROM stock2;
-- UNION ALL: 중복 포함
SELECT * FROM stock
UNION ALL
SELECT * FROM stock2;
UNION 사용 조건:
JOIN vs UNION 비교:
CREATE VIEW 뷰이름 AS
SELECT 컬럼1, 컬럼2, ...
FROM 테이블명
WHERE 조건;
-- 실제 예시
CREATE VIEW sales_summary AS
SELECT s.id, s.구매날짜, p.상품명, p.가격, u.고객명
FROM sales s
INNER JOIN product p ON s.상품id = p.id
INNER JOIN user_table u ON s.고객번호 = u.id;
-- VIEW를 테이블처럼 사용
SELECT * FROM sales_summary;
SELECT * FROM sales_summary WHERE 가격 > 10000;
-- VIEW 기반 추가 집계
SELECT 구매날짜, SUM(가격) as 일별매출
FROM sales_summary
GROUP BY 구매날짜;
VIEW의 장점:
1. 재사용성: 복잡한 JOIN 쿼리를 간단히 재사용
2. 보안: 필요한 컬럼만 노출하여 데이터 보안 강화
3. 용량 절약: 실제 데이터 복사 없이 가상으로 제공
4. 실험용: 테이블 구조 변경 전 테스트 용도
-- 1. 기본 매출 조회 (3개 테이블 JOIN)
SELECT s.id, s.구매날짜, p.상품명, p.가격, u.고객명
FROM sales s
INNER JOIN product p ON s.상품id = p.id
INNER JOIN user_table u ON s.고객번호 = u.id;
-- 2. 날짜별 매출 집계
SELECT 구매날짜, SUM(가격) as 일별매출
FROM sales s
INNER JOIN product p ON s.상품id = p.id
GROUP BY 구매날짜
ORDER BY 구매날짜;
-- 3. 매출에 없는 상품 찾기 (LEFT JOIN 활용)
SELECT p.*
FROM product p
LEFT JOIN sales s ON p.id = s.상품id
WHERE s.상품id IS NULL;
-- 4. 고객별 구매 내역과 총액
SELECT u.고객명, COUNT(*) as 구매횟수, SUM(p.가격) as 총구매액
FROM sales s
INNER JOIN product p ON s.상품id = p.id
INNER JOIN user_table u ON s.고객번호 = u.id
GROUP BY u.id, u.고객명
ORDER BY 총구매액 DESC;
이러한 JOIN과 데이터 조작 기법들을 마스터하면 복잡한 비즈니스 요구사항도 효율적으로 처리할 수 있습니다.