- SQL 세션 4회차 (코드: https://github.com/rladbstj56/2025_main_bootcamp/blob/main/SQL_session/251229_4th)
- UNION vs UNION ALL
- UNION
- 열이 늘어나지 않고 행(레코드)만 늘어남
- 사용 사례
- 11월 신청자 목록과 12월 신청자 목록을 한번에 보고 싶을 때
- 웹 유입 로그와 앱 유입 로그가 따로 있을 때 전체 유입을 보고 싶을 때
- UNION ALL
- UNION은 중복 제거 (DISTINCT가 기본), UNION ALL은 중복 유지 (그대로 포함)
- UNION
- UNION 성립 규칙 (컬럼 개수/순서, 데이터 타입, ORDER BY 위치)
- UNION 결합 되는 두 테이블에서 select 하는 컬럼의 개수는 같아야하고,
- 각 컬럼의 데이터 타입도 같아야 함
- ORDER BY는 쿼리의 맨 마지막에 옴
- 개별 SELECT에만 ORDER BY/LIMIT을 걸고 싶다면 괄호로 묶어야 함
- JOIN을 하기 위해 공통 컬럼을 찾고, 그 컬럼이 PK인지 FK인지 관계 관점에서 설명
- PK
- 중복 불가, NULL 불가
- FK
- 다른 테이블의 PK 컬럼과 연결되는 컬럼
- PK
- 카디널리티(1:N)관계
- 내가 보고자 하는 테이블이 기준이라면 그 테이블의 행 수만큼 나와야함
- 그외 나머지 테이블의 경우 집계함수를 활용해서 집계 정보만 메인 테이블 각 행에 매칭될 수 있도록.
- 1:N 조인 후 '건수'는 DISTINCT로 뻥튀기 방어
SELECT
c.course_id,
COUNT(DISTINCT e.enrollment_id) AS enrollments
FROM courses c
LEFT JOIN enrollments e ON c.course_id= e.course_id
LEFT JOIN payments p ON e.enrollment_id= p.enrollment_id
GROUP BY c.course_id;
- Full Outer Join은 MySQL에서 지원 안함 but left join + right join 하면 흉내낼 수 있음
-- FULL OUTER JOIN 흉내 (MySQL)
SELECT
s.student_id,
s.student_name,
e.enrollment_id
FROM basic.students AS s
LEFT JOIN basic.enrollments AS e
ON s.student_id= e.student_id
UNION
SELECT
s.student_id,
s.student_name,
e.enrollment_id
FROM basic.students AS s
RIGHT JOIN basic.enrollments AS e
ON s.student_id= e.student_id;
- 3개 이상 테이블 다중 JOIN에서 어떤 조인이 1:N이라서 결과가 늘어나는지 설명하고, COUNT(DISTINCT)로 검증하기
# 미니 실습 3
select
c.course_id,
c.course_name,
count(distinct e.enrollment_id) as enroll_cnt,
sum(p.amount) as paid_revenue,
count(p.payment_id) as paid_payment_rows
from basic.courses c
left join basic.enrollments e
on c.course_id = e.course_id
left join basic.payments p
on e.enrollment_id = p.enrollment_id
and p.payment_status = 'paid'
where c.is_active = 1
group by c.course_id, c.course_name
order by paid_revenue desc, course_name asc
- JOIN 학습에 도움 되는 시각화 도구: https://sql-joins.leopard.in.ua/?utm_source=chatgpt.com
- UNION 기본 문법
SELECT col1, col2
FROM table_a
UNION [ALL]
SELECT col1, col2
FROM table_b;
- LEFT JOIN 할 때 주의할 점 ( LEFT JOIN + NULL 체크로 "~가 없는 대상" 찾기 )
- 전제: STUDENT 테이블과 ENROLLMENT 테이블을 STUDENT_ID 가 같은 조건으로 LEFT JOIN 할 것.
- SELECT e.student_id를 할 경우, 기준은 student 테이블인데 enrollment 테이블에서 해당하는 student_id가 없을 수도 있음 → 분명 student 테이블에서 있는 student_id인데 select 되지 않고 null 값이 나올 수 있음
- 따라서, student 테이블을 중심으로 조인을 할 경우, 특히 student_id는 PK이기 때문에 s.student_id 를 SELECT 해줘야 함
- 다만, 해당 컬럼이 오른쪽 테이블의 PK이더라도 그것과는 상관없이 데이터가 유실되지 않고 보장될 수 있는 왼쪽 테이블에서 그 열을 불러오는 것이 더 안전함 (enrollments left join courses를 한다고 해도 e.course_id를 select 하는 것이 안전하다는 뜻)
SELECT s.student_id, s.student_name
FROM basic.students s
LEFT JOIN basic.enrollments e ON s.student_id = e.student_id
WHERE e.student_id IS NULL; -- 오른쪽 테이블 ID가 비어있는 사람만 골라냄
- 이와 같이 PK가 아닌 테이블에서 불러온 e.student_id IS NULL을 활용하면, 수강신청을 하지 않은 학생을 찾아낼 때 유용
- Left Join에서 조건을 포함할 때 주의할 점
# 잘못된 쿼리
SELECT
e.enrollment_id,
p.payment_status
FROM basic.enrollments AS e
LEFT JOIN basic.payments AS p
ON e.enrollment_id= p.enrollment_id
WHERE p.payment_status='paid';
# 실제 써야하는 쿼리
SELECT
e.enrollment_id,
p.payment_status
FROM basic.enrollments e
LEFT JOIN basic.payments p
ON e.enrollment_id= p.enrollment_id
AND p.payment_status='paid';
- 내가 하고 싶었던 쿼리: enrollment 테이블 기준으로 해서 payment status가 paid가 아닌 (refunded나 failed) 경우엔 해당 행이 null값으로 표현되도록 실행하는 쿼리
- but, where절을 사용하면 그게 아닌 payment 테이블에서 status가 paid인 행만 가져오게 되므로 inner join의 효과가 발생함
- 따라서, on 절에 and로 조건을 하나 더 붙여주면 enrollment 테이블의 left join이 확실하게 보장되면서 paid가 아닌 행들은 null값으로 표현됨.
- CROSS JOIN
- Students 와 Subjects 사이에 연결할 공통 컬럼이 없고, 무조건적인 모든 경우의 수(조합)를 만들어야 할 때 사용
- CROSS JOIN을 사용하면 기준 조건 없이 두 테이블의 행을 하나씩 전부 곱해버림 (데카르트 곱, Cartesian Product)
SELECT st.student_id, st.student_name, sb.subject_name
FROM Students st
CROSS JOIN Subjects sb
'SQL 공부' 카테고리의 다른 글
| Ifnull / Coalesce 함수, 윈도우 함수 (0) | 2026.01.10 |
|---|---|
| 서브쿼리(상관/FROM), IN, EXISTS 함수, CTE(WITH) (0) | 2026.01.10 |
| SQL 기초, case when (집계함수 포함), DELETE vs TRUNCATE vs DROP (0) | 2026.01.10 |
| DBeaver 설치하기 (MySQL 연결) (0) | 2026.01.10 |
| 날짜 포맷, 날짜 함수 (0) | 2026.01.10 |