🎯 오늘 수업 목표

 

1. 수강생이 세션 종료 후 할 수 있어야 하는 것:

2. 윈도우 함수가 “행을 유지하면서(행 수 그대로) 계산 결과를 옆에 붙이는 기능”임을 이해한다.

3. OVER() 안의 PARTITION BY(그룹 나누기) / ORDER BY(그룹 안에서 줄 세우기)를 쓸 수 있다.

  • 실무에서 가장 많이 쓰는 패턴 4개를 익힌다:
  • 그룹 합/개수/평균을 각 행에 붙이기: SUM/COUNT/AVG OVER
  • 그룹별 순번/순위 만들기: ROW_NUMBER, RANK, DENSE_RANK
  • 누적합(러닝 토탈): SUM OVER (… ORDER BY … ROWS …)
  • 이전/다음 행 비교: LAG, LEAD

4. IFNULL, COALESCE로 NULL을 안정적으로 처리한다.

5. 윈도우 함수 결과를 WHERE에서 바로 못 쓸 때 CTE로 감싸서 필터할 수 있다.

 

 

Part 0. NULL 처리: IFNULL / COALESCE (윈도우 함수랑 같이 자주 씀)

IFNULL

expr1...

NULL이 아니면 expr1 반환

NULL이면 expr2 반환

IFNULL(expr1, expr2)

 

COALESCE

var1→var2→var3 .... 를 거쳐가며 처음으로 NULL이 아닌 값을 반환 

COALESCE
COALESCE(val1, val2, val3, ...)

 

IFNULL vs COALESCE

IFNULL → 단일 처리 (NULL이 무엇이 되야할지 정해질 경우 좋음 )

COALESCE → 다중처리 (표준 + 여러 후보 가능)


Part 1. 윈도우 함수 “제일 많이 쓰는 1번 패턴”

 

그룹 합 / 개수 / 평균을 행을 유지한 채로 붙이기

  • COUNT/SUM/AVG OVER
  • 집계 결과를 만들되, 행을 줄이지 않는다
함수(...) OVER (
PARTITION BY ...
ORDER BY ...
)

PARTITION BY → 어떤 기준으로 “그룹을 나눌지”
ORDER BY → 파티션 안에서의 순서 (선택)

 

⭐ MySQL에서 윈도우 함수는 SELECT/ORDER BY에서만 사용 가능
`WHERE`, `GROUP BY`, `HAVING`에서는 사용 불가.

→집계 이후 단계에서 작동한다는 의미

 

GROUP BY vs COUNT OVER

GROUP BY

역할: 행을 묶어서 결과를 줄임

결과:

• 그룹당 1행

• 원본 행 정보는 사라짐

목적:

• 요약 테이블

• 리포트용 집계

→ 결과의 단위(grain)를 바꾸는 문법

 

COUNT OVER:

역할: 행을 유지한 채, 집계 값을 계산해서 붙임

결과:

• 원본 행 수 그대로

• 같은 파티션 안에서는 집계 값이 반복 표시됨

목적:

• 비율 계산

• 비교 기준 값 붙이기

• “이 행이 전체 중 어디쯤인지” 보기

→  결과의 단위를 유지한 채, 정보만 추가

 

요약

GROUP BY → “요약해서 새 테이블을 만든다”
COUNT OVER → “요약값을 컬럼처럼 덧붙인다”

 

# SUM / AVG도 동일한 논리 적용 가능

SUM OVER → 같은 그룹의 총합을 각 행에 붙임

AVG OVER → 같은 그룹의 평균을 각 행에 붙임

⭐ COUNT만의 문제가 아니라 모든 집계 함수에 동일하게 적용되는 개념


Part 2. 윈도우 함수 “제일 많이 쓰는 2번 패턴”

 

그룹별 순번/순위 매기기

  • ROW_NUMBER / RANK / DENSE_RANK

 

2-1) ROW_NUMBER()

무조건 1,2,3… 번호 (동점이어도 나눠짐)

ROW_NUMBER() OVER (
PARTITION BY ...
ORDER BY ...
)

개념

• 파티션 내에서 각 행에 고유한 번호를 부여

• 동점이 있어도 번호는 무조건 나뉜다

• 결과는 항상: 1, 2, 3, 4, …

→순위 보단 순번에 가까움

 

⭐ 포인트

• ORDER BY가 없으면 결과가 비결정적 (실행할 때마다 달라질 수 있음)

• “가장 최신 1건”, “각 그룹의 대표 행 1개” 뽑을 때 자주 사용

 

2-2) RANK vs DENSE_RANK (동점 처리)

RANK() 동점이면 같은 순위, 다음 순위는 건너뜀(갭)

RANK() OVER (
PARTITION BY ...
ORDER BY ...
)

개념적으로 “등수 개념에 가까움” (1등이 2명이면 다음은 3등)

 

DENSE_RANK() : 동점이면 같은 순위, 다음 순위는 안 건너뜀(촘촘)

DENSE_RANK() OVER (
PARTITION BY ...
ORDER BY ...
)

개념적으로 “그룹 내 순서 레벨” (1 → 2 → 3 처럼 촘촘하게 증가)

 

2-3) RANK() vs DENSE_RANK()

동점이 있을 때의 처리 방식 차이

둘의 공통점:

• 값이 같으면 같은 순위

• ORDER BY 기준으로 순위 계산

• 행은 줄어들지 않음

→차이는 다음 순위를 어떻게 매기느냐


Part3. 윈도우 함수 “제일 많이 쓰는 3번 패턴”

 

누적합/비율/최근 N개 평균

  • SUM/AVG OVER + ORDER BY + Frame
  • 행의 위치에 따라 계산에 포함되는 범위가 달라진다

 

3-1) 누적합에서 꼭 알아야 하는 1가지: 프레임(Frame)

ORDER BY가 있는 윈도우 집계에는 기본 Frame이 자동 적용된다

RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

개념

• 파티션의 첫 행부터 현재 행까지
• 값 기준(RANGE)으로 묶어서 누적

→ 즉, ORDER BY만 써도 암묵적으로 누적 계산이 된다

 

🚨문제 발생

RANGE는 값 기준 프레임, 정렬 기준 컬럼 값이 같으면 여러 행이 한 번에 포함될 수 있다

정확한 "행 단위 누적"을 원할 때, 날짜/시간이 아닌 단순 순번 기준일 때...

→ 결과가 의도와 어긋날 수 있다


때문에...
누적합은 Frame을 명시한다

ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

개념

• 물리적인 행 기준
• "앞에서부터 지금 이 행까지"를 명확히 보장

→ 누적합 = ROWS 프레임을 떠올리자!

 

3-2 각 행의 비율 계산 (리포트 · KPI · 대시보드에서 자주 사용)

개별 값 / 그룹 합계

개념

• 한 행의 값은 그대로 둔다
• 같은 그룹의 전체 합계를 윈도우 함수로 붙인다
• 둘을 나눈다

🚨 GROUP BY는 행이 줄어들기 떄문에 불가능

value
/ SUM(value) OVER (PARTITION BY group_key)

이 행의 값 = value
이 행이 속한 그룹의 전체합= SUM(value) OVER (PARTITION BY group_key)

 

이 구조만 기억해도

• 매출 비중

• 사용자별 사용량 비율

• 카테고리 점유율

이 가능해짐

 

Part4. 윈도우 함수 “제일 많이 쓰는 4번 패턴”

“이전/다음 행 비교” (LAG / LEAD) + NULL 처리(IFNULL/COALESCE)

→ “행과 행 사이의 관계를 비교한다”

 

집계(SUM/COUNT)나 순위와 달리,
LAG / LEAD는 행 자체를 이동해서 참조하는 함수

 

4-1) LAG / LEAD의 개념 졉근

LAG(값)

• 현재 행 기준 → 이전 행의 값을 가져옴
• “어제 값”, “이전 상태”, “직전 이벤트”를 볼 때 사용

 

LEAD(값)

• 현재 행 기준 → 다음 행의 값을 가져옴
• “다음 값”, “다음 상태”, “이후 이벤트”를 볼 때 사용

 

LAG(값) : 내 바로 “이전 행”의 값을 가져온다
LEAD(값) : 내 바로 “다음 행”의 값을 가져온다

LAG(expr, N, default) OVER (PARTITION BY ... ORDER BY ...)
LEAD(expr, N, default) OVER (PARTITION BY ... ORDER BY ...)

exper

→ 가져오고 싶은 컬럼 값

N

→  기본값 = 1 (한 칸 전/후) → 몇 행 전 / 몇 행 후를 볼지

default (기본값 = NULL)

→ 참조할 행이 없을 때 반환할 값

ORDER BY

→ 는 사실상 필수(“전/후” 기준이 필요)

 

⭐ORDER BY는 사실상 필수

LAG / LEAD에서 ORDER BY는 선택이 아니라 의미를 결정하는 기준
ORDER BY가 없을 경우:
“이전/다음”의 기준이 없다
결과는 비결정적 (매 실행마다 달라질 수 있음)

 

⭐ NULL이 생기는 이유 (중요)

LAG / LEAD는 구조적으로 NULL을 만들 수밖에 없다.

• 파티션의 첫 행 → LAG = NULL

• 파티션의 마지막 행 → LEAD = NULL

이 NULL은 “데이터가 없는 것”이 아닌

“참조할 이전/다음 행이 구조적으로 존재하지 않음” 을 의미

→반환값(default)를 설정하거나 IFNULL, COALESCE을 사용하자

 

4-2) “WINDOW로 이름 붙이기” (OVER 중복 줄이기)

윈도우 정의가 반복될 때는 WINDOW 절을 쓰면 좋다.

• OVER(...)에 붙일 설정을 이름으로 분리
• 쿼리가 짧아지고 의미가 명확해짐

WINDOW w AS (
PARTITION BY ...
ORDER BY ...
)
LAG(value) OVER w
LEAD(value) OVER w

→ LAG / LEAD 같이 OVER가 반복되는 패턴에서 특히 효과적


 

 

 

 

 

 

 
 

'A.Today I Learne > SQL' 카테고리의 다른 글

[TIL] SQL 라이브세션 5회차  (0) 2025.12.30
데이터 딕셔너리에 관하여  (0) 2025.12.29
[TIL] SQL 라이브세션 4회차  (1) 2025.12.29
포메팅 습관  (0) 2025.12.28

🎯 오늘 수업 목표

수강생이 세션 종료 후 할 수 있어야 하는 것:

1. CASE로 “조건에 따라 값 바꾸기/라벨 붙이기”를 할 수 있다.

2, 서브쿼리가 어떤 형태(값 1개 / 리스트 / 표)로 결과를 만드는지 이해한다.

3. IN(리스트 필터)과 EXISTS(존재 여부 필터)의 차이를 말로 설명하고 쓸 수 있다. (특히 NOT EXISTS) 

4. FROM에 (SELECT ...)를 넣는 Derived table(파생 테이블)을 만들고, 별칭(alias)이 필수라는 걸 안다.

5. CTE(WITH)가 “이름 붙인 임시 결과”이며 한 문장(statement) 안에서만 살아있다는 걸 이해한다

6. CTE + CASE를 합쳐서 “아주 간단한 리포트”를 만들 수 있다.

 

1. CASE

SQL에서 조건에 따라 다른 값을 반환하는 조건 함수

 

특징

- 위에서부터 순서대로 WHEN 검사
- 처음 TRUE인 WHEN에서 바로 종료
-  ELSE 없고 모든 WHEN이 FALSE면 → NULL

 

2. CASE + 집계 : 조건부 집계

2_1) 조건부 SUM: “조건을 만족한 대상의 총합”을 구할 때 사용

“조건을 만족한 대상의 총합”을 구할 때 사용
COUNT와 달리 ELSE 0이 매우 중요 (NULL이면 합산 누락)

 

2_2) 조건부 COUNT: “조건을 만족한 행의 개수”를 세고 싶을 때 사용

“조건을 만족한 행의 개수”를 세고 싶을 때 사용 
TRUE / FALSE를 직접 세는 게 아니라 NULL 여부를 이용해 필터링

 

2_3) 조건부  SUM  vs 조건부 COUNT 차이 정리

조건부 SUM 조건부 COUNT
얼마인가? (규모, 금액, 수치) 몇 건인가? (개수)
조건 불만족 시 0으로 누적 차단 NULL 여부를 이용한 필터링

 

2. Subquery (서브쿼리)

1. 서브쿼리란?

서브쿼리는 다른 SQL 문 안에 포함된 SELECT 쿼리 → 바깥 쿼리를 실행하기 위해 중간 결과를 제공하는 역할

 

특징

- 항상 SELECT로 시작

- 괄호 ()로 감싸서 사용

- 바깥 쿼리보다 먼저 실행되는 경우가 많음

- 결과는 바깥 쿼리의 → 조건 / 비교 대상 / 값으로 사용됨

 

Scalar Subquery (스칼라 서브쿼리): 결과가 반드시 “단 하나의 값(1행 1열)”인 서브쿼리

List Subquery(리스트 서브쿼리): 결과가 여러 행(1열 이상) 나오는 서브쿼리

 

⭐ 분류에 대한 메모

Scalar / List 구분은 이론적 분류는 큰 의미는 없다.
다만 “결과가 하나냐 / 여러 개냐”만 중요

2) EXISTS / NOT EXISTS (“없다/있다” 찾기 최고)

“있다 / 없다”를 판단하는 존재 여부 전용 서브쿼리

 

동작 방식

- 조건을 만족하는 행을 하나라도 찾으면 즉시 TRUE

- 찾지 못하면 FALSE

- 실제 값 반환 X

 

3) 상관 서브쿼리(Correlated Subquery) 가볍게 이해

서브쿼리가 바깥 쿼리의 값을 참조하는 형태

 

일반 서브쿼리와 차이

- 단독 실행 X

- 바깥 쿼리 없이는 의미 없음

 

언제 쓰나?

- “각 행 기준으로 조건을 따로 판단해야 할 때”

- 그룹 없이도→ 행별 비교 / 행별 존재 여부 확인 가능

 

Part 3. FROM 서브쿼리(파생 테이블)

“괄호 속 SELECT를 표로 만들기”

 

1, 왜 FROM 서브쿼리를 쓰는가?

4회차에서 힘들었던 포인트:

- 1 : N 관계에서 N쪽 테이블을 그대로 JOIN하면 행 수가 폭증

- 그 상태로 SUM / COUNT 하면 값이 부풀려짐

 

해결법

- N쪽 테이블을 먼저 GROUP BY로 요약

- 행 수를 줄인 ‘중간 결과 테이블’로 만든 뒤

→ 그 결과를 JOIN

 

2. 파생 테이블(Derived Table)이란?

FROM 절에 들어간 서브쿼리의 결과를 “임시 테이블처럼” 사용하는 것

 

특징

- 괄호 () 안의 SELECT 결과가

- 실행 중에 가상의 테이블이 됨

- SELECT / JOIN / WHERE에서

   → 일반 테이블과 동일하게 취급

 

서브쿼리 위치별 역할 차이

WHERE 서브쿼리 → 필터링

SELECT 서브쿼리 → 값 생성

FROM 서브쿼리 → 구조(표) 생성

 

⭐파생 테이블에서 가장 중요한 규칙

별칭(Alias) 필수
별칭 없으면 → 문법 에러 발생

 

Part 4. CTE (WITH)

“이름 붙인 파생테이블”

 

1. CTE란?

CTE(Common Table Expression)는 이름이 붙은 임시 결과 집합

 

핵심

-단일 SQL statement 범위에서만 존재

- 실행이 끝나면 자동 소멸

-하나의 쿼리 안에서 여러 번 참조 가능

 

 

2. CTE의 본질 (FROM 서브쿼리와의 관계)

CTE = 가독성 좋은 FROM 서브쿼리
기능적으로는 거의 동일 → 차이는 구조와 가독성

 

사용이유?

- 쿼리가 길어질수록 중첩 FROM 서브쿼리는 읽기 어려움

- CTE는 단계별로 이름을 붙여 로직을 위에서 아래로 읽을 수 있음

 

 

3) CTE 기본 문법 (가장 쉬운 형태)

구조

WITH 절에서...

- CTE 이름 정의

- 그 안에 SELECT 작성

 

아래 메인 쿼리에서...

- 테이블처럼 사용

 

자세한 내용은 복습 참조

'A.Today I Learne > SQL' 카테고리의 다른 글

[TIL] SQL 라이브세션 6회차  (0) 2025.12.31
데이터 딕셔너리에 관하여  (0) 2025.12.29
[TIL] SQL 라이브세션 4회차  (1) 2025.12.29
포메팅 습관  (0) 2025.12.28

SQL에서 가장 중요한 것은 문법이 아니라 “테이블 단위(레벨, grain)”를 잡는 것이다.
쿼리를 작성하기 전에 반드시 먼저 해야 할 질문은 이것이다.

“이 테이블에서 1행은 무엇을 의미하는가?”

이 질문에 답하지 않고 JOIN이나 집계를 시작하면,
결과는 거의 항상 행이 뻥튀기되거나 / 누락되거나 / 해석 불가능해진다.

때문에 쿼리 작성 전에 데이터 딕셔너리부터 정리하는 습관이 중요하다.

 

데이터 딕셔너리란?

데이터 딕셔너리는 단순한 “컬럼 설명 목록”이 아닌
각 테이블의 역할과 관계를 명확히 정의한 설계 문서에 가깝다.

 

떄문에 딕셔너리를 볼때 다음 3가지를 중점으로 생각해야 한다.

- 이 테이블의 행 단위(grain)
- 어떤 테이블과 어떻게 JOIN되는지
- 이 테이블을 어떤 목적의 쿼리에서 써야 하는지

 

 

SQL 관점에서 중요한 데이터 딕셔너리 작성 순서

1. 테이블 요약 (가장 중요)

ex.

  • students (학생 마스터)
  • 1행 = 학생 1명

 

2. 테이블 설명

  • 이벤트성 테이블인지?
  • 마스터 테이블인지?
  • 변하지 않는 정보인지?

를 명확히 한다.

 

3. PK (Primary Key)

 

  • 이 테이블의 행을 유일하게 식별하는 컬럼
  • “이 테이블의 주민등록번호”

 

 

4. 주요 JOIN 키 (관계 명시)

여기서 중요한 건:

  • 누가 1이고 누가 N인가
  • 이 JOIN이 행 수를 늘릴 수 있는지

를 바로 판단할 수 있어야 한다는 점.

 

5. 컬럼 정의 (필요한 만큼)

 

 

 

SQL에서 데이터 딕셔너리는 곧 설계도다

SQL은 “쿼리를 잘 짜는 기술”이 아니라 “데이터 구조를 이해하고 결과를 설계하는 기술”이다.

 

데이터 딕셔너리를 먼저 쓰면...

  • 최종 결과의 행 단위(grain) 를 명확히 선언할 수 있다
  • JOIN 전에 집계가 필요한지 판단할 수 있다
  • JOIN 결과를 보고 정상/비정상을 바로 구분할 수 있다

 

 

 

 

 

한 줄 요약

쿼리를 쓰기 전에 각 테이블에서 1행이 무엇을 의미하는지 먼저 정의하자.

'A.Today I Learne > SQL' 카테고리의 다른 글

[TIL] SQL 라이브세션 6회차  (0) 2025.12.31
[TIL] SQL 라이브세션 5회차  (0) 2025.12.30
[TIL] SQL 라이브세션 4회차  (1) 2025.12.29
포메팅 습관  (0) 2025.12.28

내일배움캠프에서 제공하는 데이터를 사용했습니다.

 

🎯 수업 목표 (오늘 “반드시” 가져갈 것)

세션 종료 후 수강생이 혼자 할 수 있어야 하는 것:

  1. UNION vs UNION ALL 차이를 설명하고, 목적에 맞게 선택한다. (중복 제거/유지)
  2. UNION이 성립하기 위한 규칙(컬럼 개수/순서, 데이터 타입, ORDER BY 위치)을 이해한다.
  3. JOIN을 하기 위해 공통 컬럼을 찾고, 그 컬럼이 PK인지 FK인지를 관계 관점에서 설명할 수 있다.
  4. INNER/LEFT/RIGHT JOIN의 결과 차이를 이해하고, “내가 보고 싶은 모수(기준 테이블)가 무엇인지”에 따라 JOIN 종류를 선택한다.
  5. LEFT JOIN + NULL 체크로 “~가 없는 대상”을 찾을 수 있다.
  6. 3개 이상 테이블 다중 JOIN에서 어떤 조인이 1:N이라서 결과가 늘어나는지를 설명하고, COUNT(DISTINCT)로 검증할 수 있다.

*테이블 파악 능력 중요!

- 추후 Today I Learne에 데이터명세서 방법론 기입!

 

4회차 선요약!

테이블 결합의 두 축!

  • UNION: 결과를 세로로 쌓기(수직 결합)
  • JOIN: 테이블을 옆으로 붙이기(수평 결합)

Part 1. UNION / UNION ALL

UNION은 여러 SELECT 결과를 하나로 합쳐서 보고 싶을 때 사용한다.

대표적인 상황은 다음과 같다.

  • 11월 신청자 목록이 있고, 12월 신청자 목록이 따로 있을 때
  • 웹 유입 로그와 앱 유입 로그가 따로 있고 “전체 유입”을 보고 싶을 때
  • “학생 목록”과 “강사 목록”을 합쳐서 “전체 사람 목록”을 만들고 싶을 때

UNION 핵심!

열(컬럼) 수가 늘어나지 않고 행(레코드)이 늘어남

💡 UNION은 “테이블 결합”이라기보단 "결과 결합"

즉, SELECT 결과를 세로로 붙여서 하나로 만든다!

 

1_2) UNION vs UNION ALL

✅ 기본은 “중복 제거” (UNION = DISTINCT가 기본)
MySQL set operation은 기본적으로 중복 행을 제거 (`DISTINCT`가 기본)

✅ UNION ALL은 “중복 유지”
`ALL`을 붙이면 중복 제거를 하지 않고 그대로 다 포함

 

1_3) UNION 규칙 3가지

  1. 각 SELECT는 같은 개수의 컬럼을 반환해야 한다.
    - 컬럼 수가 다르면 UNION 불가
  2. 같은 위치의 컬럼끼리 데이터 타입이 호환되어야 한다
    (숫자 ↔ 숫자, 문자열 ↔ 문자열 등)
  3. 결과 컬럼명은 첫 번째 SELECT 기준
    컬럼명이 꼭 똑같아야 하나?
    ❌. 컬럼명은 달라도 되지만, UNION 결과의 컬럼명은 첫 번째 SELECT의 컬럼명(또는 별칭)을 따른다.
UNION 기본 문법

SELECT col1, col2
FROM table_a
UNION [ALL]
SELECT col1, col2
FROM table_b;

 

1_4) UNION에서 ORDER BY는 어디에?

UNION 결과 전체를 정렬하고 싶다면 맨 마지막에 ORDER BY를 사용한다.

Part 2. JOIN

 

💡 JOIN은 SQL의 꽃
UNION이 “세로로 붙이기”였다면, JOIN은 옆으로 붙이기다.

 

JOIN의 성패는 결국 이 3단계로 결정된다.

  1. 공통컬럼 찾기
  2. PK/FK 관계 이해
  3. 적절한 JOIN 방식 선택

0) 먼저 어떤 목적으로 join을 할려고 하는건가?

JOIN을 하기로 마음먹었다면, 관련된 모든 테이블의 역할과 의미를 먼저 이해해야 한다.
무작정 JOIN부터 치면 거의 100% 결과가 꼬인다. 그렇기에 JOIN 전에 스스로에게 질문해야 한다.

 

 “최종적으로 무엇을 보고 싶은가?”

 

예를 들어, 최종 테이블은 학생 단위 당 결재 총액을 보고 싶다. 라고하면...

필요한 정보는

student

enrollments

payments

테이블이 필요할것이다. 그 뒤 확인을 해보자!

 

 

1) JOIN을 하기 전에 헤야할것: 공통 컬럼 찾기?

JOIN은 테이블을 연결하는 고리(키) 가 반드시 필요하다.
이 고리는 보통:

  • ID 컬럼
  • PK / FK 관계

JOIN은 결국 이렇게 말하는 것과 같다.
“이 행은, 저 테이블의 이 행과 같은 대상이다”

 

ex)
students.student_id ↔ enrollments.student_id
enrollments.enrollment_id ↔ payments.enrollment_id

 

2) PK / FK를 0부터 설명

PK(Primary Key, 기본키)

  • 한 테이블에서 각 행을 유일하게 식별하는 컬럼
  • 중복되면 안 됨
  • NULL이면 안 됨

👉 “이 테이블의 주민등록번호”

 

FK(Foreign Key, 외래키)

  • 다른 테이블의 PK를 참조하는 컬럼
  • “이 행이 누구/무엇과 연결되는지” 알려주는 값

👉 “우리 테이블에서, 저 테이블의 누구를 가리키는가”

 

 

 

3) 관계(카디널리티) 감각

1:1 / 1:N 이 왜 중요한가?

JOIN이 어려운 진짜 이유는
👉 문법이 아니라, 결과 행이 늘어나는 방식 때문이다.

 

 

🔹 1:1 관계 👉 비교적 안전한 JOIN

  • 한 행 ↔ 한 행
  • JOIN해도 행 수가 늘어나지 않음

 

🔹 1:N 관계 (가장 중요) 👉 JOIN하면 행이 N배로 늘어난다

  • 1쪽 테이블의 한 행
  • N쪽 테이블의 여러 행과 매칭

 

그래서 어떻게 해야 하나? (How?)

JOIN 전에 반드시 이 순서를 거친다.

  1. 최종 결과의 행 단위(grain)를 먼저 선언
  2. N쪽 테이블은
    → JOIN 전에 집계하거나 한 행으로 줄인다
  3. JOIN 후 결과를 반드시 확인한다

👉 “JOIN은 붙이는 기술이 아니라, 결과를 설계하는 과정”

 

 

4) JOIN의 종류 & 기본 문법(패턴)

조인 종류 설명 반환 데이터 범위 MySQL 지원 여부
INNER JOIN
(가장 많이 사용)
두 테이블에서 일치하는 값을 가진 행만 반환 (교집합) 두 테이블의 교집합 지원
LEFT JOIN
(가장 많이 사용)
왼쪽 테이블의 모든 행 + 오른쪽 테이블에서 일치하는 행 반환. 일치하지 않으면 오른쪽 컬럼은 NULL 왼쪽 전체 + 교집합 지원
RIGHT JOIN 오른쪽 테이블의 모든 행 + 왼쪽 테이블에서 일치하는 행 반환. 일치하지 않으면 왼쪽 컬럼은 NULL 오른쪽 전체 + 교집합 지원
FULL OUTER JOIN 양쪽 테이블의 모든 행 반환 (합집합). 일치하지 않는 컬럼은 NULL로 채움 합집합 (교집합 + 왼쪽/오른쪽 단독 데이터) MySQL 기본적으로 미지원
→ LEFT JOIN 과 RIGHT JOIN 의 합집합으로 계산해야 함.

 

JOIN 기본문법

SELECT
a.col1,
b.col2
FROM table_a AS a
JOIN table_b AS b
ON a.key= b.key;

 

5) INNER JOIN (교집합)

언제 쓰나?

“양쪽 테이블에 모두 존재하는 데이터만 보고 싶을 때”

  • 매칭 안 되는 행은 전부 제거됨

예)

  • 실제로 수강신청이 있는 학생만 보고 싶다
  • 주문이 있는 고객만 보고 싶다
SELECT
e.enrollment_id,
e.enroll_date,
s.student_id,
s.student_name,
s.region,
s.segment
FROM basic.enrollments AS e
INNER JOIN basic.students AS s
ON e.student_id= s.student_id
ORDER BY e.enroll_date;

 

 

 

6) LEFT JOIN (왼쪽 기준 + 매칭 없으면 NULL)

언제 쓰나?

기준(모수)을 유지하고 싶을 때

  • 왼쪽 테이블은 무조건 남김
  • 오른쪽에 매칭이 없으면 NULL

예)

 

  • 전체 학생 중 수강신청 안 한 사람 찾기
  • 전체 고객 대비 주문 여부 확인

 

사용 예시
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;

(null 활용)
SELECT
s.student_id,
s.student_name
FROM basic.students AS s
LEFT JOIN basic.enrollments AS e
ON s.student_id= e.student_id
WHERE e.enrollment_id IS NULL;

7) RIGHT JOIN (오른쪽 기준)

MySQL 공식 문서에서도:

  • RIGHT JOIN은 LEFT JOIN과 기능적으로 동일
  • 이식성(portability)을 위해 LEFT JOIN 사용 권장

그래서 수업에서는:

  • RIGHT JOIN은 개념만 이해
  • 실제 작성은 LEFT JOIN으로 통일 추천

 

8) ON vs WHERE (LEFT JOIN에서 자주 터지는 함정)

MySQL 문서:

ON: 조인을 “어떻게 붙일지” 조건
WHERE: 결과에서 “어떤 행을 남길지” 필터

⚠️ 흔한 실수: 

LEFT JOIN을 했는데 WHERE 때문에 INNER JOIN처럼 되어버림

 

오늘 JOIN 파트 회상 요약

  • JOIN은 “옆으로 붙이기”
  • 먼저 목적과 최종 단위(grain) 를 정해야 한다
  • 공통 컬럼 + PK/FK 이해가 핵심
  • 1:N 관계에서 행이 늘어나는 걸 항상 의식
  • LEFT JOIN에서는 ON / WHERE 위치에 특히 주의

쿼리문 요약

-- 1) UNION / UNION ALL (세로 결합: 행이 늘어남)
SELECT col1, col2
FROM t1
UNION-- 기본: 중복 제거
SELECT col1, col2
FROM t2;


SELECT col1, col2
FROM t1
UNION ALL-- 중복 유지
SELECT col1, col2
FROM t2;


-- 2) UNION 결과 정렬은 "맨 마지막"에
SELECT col1, col2 FROM t1
UNION ALL
SELECT col1, col2 FROM t2
ORDER BY col1;


-- 3) INNER JOIN (가로 결합: 공통키로 매칭되는 것만)
SELECT
a.key, a.col A, b.col B
FROM A a
INNER JOIN B b
ON a.key= b.key;


-- 4) LEFT JOIN (왼쪽 모수 유지 + 매칭 없으면 오른쪽은 NULL)
SELECT
a.key, a.col A, b.col B
FROM A a
LEFT JOIN B b
ON a.key= b.key;


-- 5) "B에 없는 A" 찾기 (LEFT JOIN + NULL 체크 = anti-join)
SELECT a.*
FROM A a
LEFT JOIN B b
ON a.key= b.key
WHERE b.key IS ULL;


-- 6) LEFT JOIN에서 오른쪽 조건은 ON에 두기 (WHERE에 두면 모수 유실 위험)
-- 모수 유지(결제 없는 신청도 남김)
SELECT e.enrollment_id, p.payment_status
FROM enrollments e
LEFT JOIN payments p
ON e.enrollment_id= p.enrollment_id
AND p.payment_status='paid';

 

 

Part 3. 다중 JOIN + 결과 검증 루틴

복습 때 정리

 

Part 4. UNION vs JOIN + 함께 쓰는 패턴

복습 때 정리

'A.Today I Learne > SQL' 카테고리의 다른 글

[TIL] SQL 라이브세션 6회차  (0) 2025.12.31
[TIL] SQL 라이브세션 5회차  (0) 2025.12.30
데이터 딕셔너리에 관하여  (0) 2025.12.29
포메팅 습관  (0) 2025.12.28

SQL 쿼리 포맷팅 (쿼리도 정리하면 예뻐진다)
SQL 작성할 때 포맷팅 습관 들이도록 노력하자.

정렬된 퀴리 혹은 코드는 오류 찾기도 훨씬 쉬워진다.

 

[SQL]
키워드는 대문자 : SELECT, FROM, WHERE, GROUP BY, DISTINCT, AS, CASE WHEN 등
함수도 대문자 : COUNT, ROUND, SUM, AVG 등
컬럼명, 테이블명은 소문자 : channel, order_id, orders
절마다 줄바꿈 : SELECT, FROM, WHERE, GROUP BY 등 각각 새 줄에서 시작
들여쓰기 : 스페이스 2칸 혹은 Tab으로 컬럼명, 조건 등 들여쓰기

'A.Today I Learne > SQL' 카테고리의 다른 글

[TIL] SQL 라이브세션 6회차  (0) 2025.12.31
[TIL] SQL 라이브세션 5회차  (0) 2025.12.30
데이터 딕셔너리에 관하여  (0) 2025.12.29
[TIL] SQL 라이브세션 4회차  (1) 2025.12.29

+ Recent posts