[강동7기 전Z전능 AI PM] 코멘토 청년취업사관학교 DAY 34
DAY 34 | SQL 기초 — 정렬·연산·날짜·CASE로 쿼리 조합하기
SQL 기초 2일차. 어제 SELECT와 WHERE로 “무엇을, 어디서, 어떤 조건으로” 가져오는지를 잡았다면, 오늘은 그 위에 정렬 → 연산 → 날짜 가공 → CASE를 차례로 얹으면서 쿼리를 점점 조합해가는 하루였다. 데이터를 단순히 꺼내오는 걸 넘어서 분석 가능한 형태로 바꾸는 과정이라, PM 입장에서 실제로 쓸 일이 많은 파트였다.
ORDER BY — 결과의 순서를 정한다
WHERE로 걸러낸 결과를 어떤 순서로 볼지 지정하는 게 ORDER BY다. 기본값은 오름차순(ASC)이고 내림차순은 DESC, ASC는 생략할 수 있다.
SELECT * FROM Customer ORDER BY LastName ASC;
SELECT * FROM Invoice ORDER BY Total DESC;컬럼을 여러 개 쓰면 앞 컬럼부터 우선 적용된다. 아래는 국가는 오름차순으로 묶고, 같은 국가 안에서는 금액이 큰 순으로 정렬된다.
SELECT * FROM Invoice ORDER BY BillingCountry ASC, Total DESC;한 가지 짚고 넘어간 건 ORDER BY가 실제 테이블을 바꾸는 게 아니라 조회 결과의 출력 순서만 바꾼다는 점이다. 정렬은 어디까지나 보여주는 방식이다.
LIMIT / OFFSET — 몇 개만, 어디서부터
LIMIT은 가져올 최대 행 수를 제한하고, OFFSET은 앞에서 몇 개를 건너뛸지 정한다. 둘을 조합하면 페이지네이션이 된다.
-- 이름순 상위 3명
SELECT * FROM Customer ORDER BY LastName ASC LIMIT 3;
-- 앞 5개를 건너뛰고 그다음 5개 (2페이지)
SELECT * FROM Customer ORDER BY LastName ASC LIMIT 5 OFFSET 5;목록 화면에서 “다음 페이지”를 누를 때 뒤에서 어떤 쿼리가 도는지를 생각하면 OFFSET이 바로 그 역할이다.
수치 연산과 계산된 컬럼
SELECT 안에서 컬럼을 계산해서 새 값을 만들 수 있다. + - * / %를 쓸 수 있고, 우선순위는 * / %가 먼저, + -가 나중이다. 계산 결과에는 AS로 별칭을 붙인다.
SELECT Price, Quantity, Price * Quantity AS amount FROM sample;여기서 amount는 실제로 테이블에 저장된 컬럼이 아니라 조회 시점에 만들어지는 계산 컬럼(Computed/Derived Column)이다. 별칭은 영어로 지정하는 걸 권장했다.
실행 순서 — WHERE에서는 별칭을 못 쓴다
오늘 가장 중요한 포인트가 여기였다. WHERE에서도 연산은 할 수 있지만, SELECT에서 만든 별칭은 WHERE에서 바로 쓸 수 없다.
-- 가능: WHERE에서 직접 연산
SELECT Price * Quantity AS amount FROM sample WHERE Price * Quantity >= 2000;
-- 불가능: SELECT 별칭을 WHERE에서 사용
SELECT Price * Quantity AS amount FROM sample WHERE amount >= 2000; -- 에러이유는 SQL의 실행 순서 때문이다. 작성하는 순서와 실제 실행되는 순서가 다르다.
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
WHERE가 SELECT보다 먼저 실행되니, WHERE 시점에는 아직 amount라는 별칭이 존재하지 않는다. 반대로 ORDER BY는 SELECT 다음에 실행되기 때문에 별칭을 그대로 쓸 수 있다.
-- 가능: ORDER BY는 SELECT 이후 실행
SELECT Price * Quantity AS amount FROM sample ORDER BY amount DESC;정리하면 WHERE에서는 별칭 사용 불가, ORDER BY에서는 별칭 사용 가능이다. 이 차이는 실행 순서 한 줄만 기억하면 자연스럽게 따라온다.
자주 쓰는 함수
숫자와 문자열을 다듬는 함수들을 짚었다. ROUND는 반올림, CONCAT은 문자열 합치기, SUBSTRING은 일부 잘라내기다.
SELECT ROUND(3.14159, 2); -- 3.14
SELECT CONCAT(FirstName, ' ', LastName) AS FullName FROM Customer;
SELECT SUBSTRING(LastName, 1, 3) FROM Customer; -- 앞 3글자CONCAT은 컬럼 사이에 ' ' 같은 일반 문자열도 끼워 넣을 수 있어서 이름을 붙일 때 자주 쓴다.
날짜 연산 — 실무에서 제일 많이 만나는 파트
날짜는 실무에서 가장 자주 다루는 데이터다. 최근 30일 매출, 마지막 구매 후 경과일, 주문에서 배송까지 걸린 기간, 이탈 고객, 월·분기별 매출 같은 질문이 전부 날짜 계산에서 나온다.
현재 시각은 FROM 없이도 가져올 수 있다.
SELECT CURRENT_TIMESTAMP; -- NOW() 와 동일날짜를 더하거나 두 날짜의 차이를 구하는 함수들이다.
-- 날짜 더하기 (DAY / MONTH / YEAR / HOUR 단위)
SELECT CURRENT_DATE + INTERVAL 1 DAY;
SELECT DATE_ADD(NOW(), INTERVAL 1 DAY);
-- 두 날짜의 차이 (날짜1 - 날짜2, 일수)
SELECT DATEDIFF('2025-07-10', '2025-07-01'); -- 9
-- 단위를 지정한 차이 (SECOND/MINUTE/HOUR/DAY/MONTH/YEAR)
SELECT TIMESTAMPDIFF(DAY, '2025-07-01', '2025-07-10');집계에서 특히 많이 쓰는 게 DATE_FORMAT이다. 날짜를 원하는 형태의 문자열로 바꾼다.
SELECT DATE_FORMAT('2025-07-10', '%Y-%m'); -- 2025-07
SELECT DATE_FORMAT('2025-07-10', '%Y-%m-%d'); -- 2025-07-10
SELECT DATE_FORMAT('2025-07-10', '%Y년%m월'); -- 2025년07월주요 포맷 코드는 아래와 같다.
| 코드 | 의미 | 코드 | 의미 |
|---|---|---|---|
| %Y | 4자리 연도 | %H | 시 |
| %y | 2자리 연도 | %i | 분 |
| %m | 월 | %s | 초 |
| %d | 일 | %W | 요일 전체 |
| %a | 요일 약어 |
'%Y-%m'으로 월 단위 문자열을 만들어 그룹핑하면 월별·분기별 매출 집계로 바로 이어진다. 실무에서 “월별 추이 보여주세요”라는 요청이 결국 이 포맷에서 시작한다는 감이 왔다.
CASE — 오늘의 핵심
가장 비중 있게 다룬 건 CASE였다. 조건에 따라 다른 값을 반환하는 조건문으로, 다른 언어의 if/else나 switch와 비슷하다. 위에서부터 조건을 확인하고, 반드시 END로 닫아야 한다.
SELECT Address,
CASE
WHEN Address LIKE '서울특별시%' THEN '서울'
WHEN Address LIKE '대구광역시%' THEN '대구'
ELSE '기타'
END AS 거주지
FROM Customer;조건은 위에서부터 순서대로 확인되기 때문에 순서가 중요하다. ELSE에 특정 컬럼을 두면 조건에 안 걸린 값은 기존 값을 그대로 유지할 수도 있다.
CASE에는 두 가지 형태가 있다. 조건식을 직접 쓰는 방식과, 컬럼을 먼저 지정하는 방식이다.
-- 조건식 직접
CASE WHEN age >= 20 THEN '성인' ELSE '미성년' END
-- 컬럼 먼저 지정 (정확히 일치하는 값만, LIKE 불가)
CASE Country WHEN 'Korea' THEN '한국' ELSE '기타' END컬럼을 먼저 지정하는 방식은 값이 정확히 일치할 때만 매칭되고 LIKE는 쓸 수 없다는 점이 다르다. NULL을 다룰 때는 WHEN a IS NULL THEN '미지정'처럼 CASE 안에서 처리한다.
Binning — 숫자를 구간으로 나눠 라벨링
CASE의 대표적인 활용이 구간화(Binning)다. 연속된 숫자를 범위로 나눠 라벨을 붙이는 건데, VIP/일반 고객 분류, 점수별 등급, 가격대별 상품 수, 매출 구간별 비중처럼 시각화 직전 단계에서 계속 쓰인다.
SELECT Total,
CASE
WHEN Total <= 5 THEN 'Low'
WHEN Total <= 10 THEN 'Medium'
ELSE 'High'
END AS SalesLevel
FROM Invoice;날것의 매출 숫자를 이렇게 등급으로 바꿔두면 “High 구간 고객이 전체의 몇 %“처럼 바로 해석 가능한 지표가 된다. PM이 대시보드에서 보고 싶어 하는 형태가 대부분 이 구간화를 거친 값이다.
CASE + ORDER BY — 사용자 지정 정렬
CASE를 ORDER BY 안에 넣으면 원하는 순서를 직접 정할 수 있다. 알파벳순이 아니라 “프랑스 먼저, 그다음 캐나다, 나머지” 같은 임의의 우선순위를 만들 때 쓴다.
SELECT * FROM Customer
ORDER BY
CASE Country
WHEN 'France' THEN 1
WHEN 'Canada' THEN 2
ELSE 3
END,
LastName ASC;같은 우선순위 안에서는 뒤에 정렬 기준을 하나 더 붙여 이름순으로 다시 정렬했다.
오늘 헷갈리기 쉬웠던 것
정리하면서 다시 확인한 지점들이다.
- NULL은
= NULL이 아니라IS NULL로 비교한다. - SELECT 별칭은 WHERE에서 못 쓰고(
WHERE amount불가,WHERE Price*Quantity가능), ORDER BY에서는 쓸 수 있다. - CASE는 END로 닫고, 조건 순서가 결과를 바꾼다.
핵심은 SQL이 데이터를 가져오는 것에서 끝나지 않는다는 점이다. 조건으로 거르고, 정렬하고, 계산하고, 날짜를 가공하고, CASE로 의미 있는 그룹과 라벨을 만들어 분석 가능한 데이터로 바꾸는 게 전체 흐름이었다. 어제 SELECT/WHERE가 재료를 꺼내오는 단계였다면, 오늘은 그 재료를 실제로 볼 수 있는 형태로 다듬는 단계였다.
취업 부트캠프 청년취업사관학교 새싹 aipm dx교육 ai교육 실무프로젝트 실무경험 취업포트폴리오 포트폴리오 전z전능 코멘토 모비니티