당신은 급성장하고 있는 글로벌 이커머스 기업 **'Orbit(오빗)'**에 갓 입사한 데이터 분석가입니다. 입사 직후, 회사는 연말 최대 할인 행사인 '블랙 프라이데이' 준비로 매우 분주합니다. 경영진을 비롯하여 마케팅팀, CS(고객 서비스)팀, 영업팀 등 다양한 부서에서 당장 캠페인 기획과 운영에 필요한 기초 데이터들을 추출해 달라는 요청이 쏟아지고 있습니다.
아직 복잡한 데이터 가공보다는, 정확한 타겟을 필터링(WHERE)하고 정돈되게 나열(ORDER BY)하여 각 부서에 빠르게 전달하는 것이 당신의 첫 번째 미션입니다. 자, 이제 각 부서의 실무 요청을 바탕으로 SQL을 작성해 봅시다!
경영지원팀은 회사의 전반적인 규모와 파트너사 현황을 한눈에 파악하기 위해 전체 명단 조회를 요청했습니다.
- [전체 고객 명단] "우리 쇼핑몰에 가입한 전체 고객 규모를 눈으로 직접 확인하고 싶습니다.
customers테이블의 모든 데이터를 추출해 주세요." - [전체 상품 목록] "현재 판매를 위해 시스템에 등록된 전체 상품이 무엇무엇인지 알아야 합니다.
products테이블의 모든 데이터를 추출해 주세요." - [공급업체 연락처 추출] "연말 행사를 맞아 파트너사들에게 안내 메일을 돌려야 합니다.
suppliers테이블에서 업체의 이름(name)과 담당자 이메일(contact_email) 컬럼만 뽑아주세요." - [카테고리 종류 파악] "우리 쇼핑몰에 상품이 등록된 카테고리는 총 몇 종류나 되나요?
products테이블을 조회하여, 중복을 제거한 고유한 카테고리 ID(category_id) 목록을 뽑아주세요."
마케팅팀은 한정된 예산으로 최대의 효율을 내기 위해 특정 조건에 맞는 고객과 상품을 타겟팅하려고 합니다.
- [특정 등급 고객] "VIP 고객들을 위한 프라이빗 파티를 기획 중입니다.
customers테이블에서 등급(grade)이 'VIP'인 고객의 이름과 이메일을 찾아주세요." - [날짜 기반 신규 고객] "올해 신규 가입자 웰컴 이벤트를 하려고 합니다.
customers테이블에서 가입일(join_date)이 '2023년 1월 1일' 이후(해당일 포함)인 고객의 모든 정보를 추출하세요." - [특정 가격대 상품 기획전] "1만 원에서 5만 원 사이의 중저가 선물 기획전을 준비 중입니다.
product_prices테이블에서 가격(price)이 10,000원 이상, 50,000원 이하인 상품 ID와 가격을 추출해 주세요. (BETWEEN사용)" - [할인 상품 제외] "가격이 100,000원 초과인 프리미엄 상품만 따로 마케팅하려고 합니다.
product_prices테이블에서 가격이 100,000원을 초과하는 상품 ID와 가격을 찾아주세요." - [특정 키워드 검색 1] "최근 '김'씨 성을 가진 고객들을 대상으로 한 재미있는 이벤트를 구상 중입니다.
customers테이블에서 이름(name)이 '김'으로 시작하는 고객들을 모두 찾아주세요. (LIKE사용)" - [특정 키워드 검색 2] "메일 서버 오류로 '@gmail.com' 주소를 사용하는 고객들에게만 메일이 발송되지 않았습니다. 이메일 주소가 '@gmail.com'으로 끝나는 고객 정보만 추출해 주세요."
- [다중 조건 필터링] "영업부와 마케팅부 소속 직원들에게만 사내 공지를 돌리려 합니다.
staff테이블에서 부서(department)가 'Sales' 이거나 'Marketing'인 직원의 이름과 부서를 조회하세요. (IN사용)" - [복합 조건 필터링] "전자기기 기획전을 위해, 카테고리 ID(
category_id)가 2번이면서 납품업체 ID(supplier_id)가 3번인 상품의 목록이 필요합니다.products테이블에서 두 조건을 모두 만족하는 상품을 찾아주세요."
운영팀은 데이터 누락이나 오류를 찾아내어 시스템을 정비하려고 합니다.
- [데이터 누락 확인 1] "회원 가입 시 등급 부여가 누락된 고객이 있는지 확인해야 합니다.
customers테이블에서 등급(grade) 값이 비어있는(NULL) 고객의 전체 정보를 찾아주세요." - [데이터 누락 확인 2] "협력사 비상 연락망을 만들고 있는데, 담당자 전화번호가 없는 곳이 있습니다.
suppliers테이블에서 전화번호(phone)가 없는(NULL) 업체의 이름을 찾아주세요." - [데이터 누락 제외] "전화번호가 제대로 등록된 협력사들에게만 SMS를 발송하려고 합니다.
suppliers테이블에서 전화번호가 존재하는(NOT NULL) 업체의 정보를 추출하세요." - [부정 조건 검색] "배송 지연 문제가 있는 1번 공급업체를 제외하고 나머지 업체의 상품들만 급하게 메인 페이지에 노출하려 합니다.
products테이블에서 공급업체 ID(supplier_id)가 1번이 아닌 상품들을 조회하세요."
영업팀은 추출된 데이터를 보기 좋게 정렬하여 보고서를 작성하려고 합니다.
- [기본 오름차순 정렬] "전체 상품 목록을 가나다순으로 확인하고 싶습니다.
products테이블의 모든 데이터를 상품명(name) 기준 오름차순으로 정렬하여 조회하세요." - [기본 내림차순 정렬] "가장 최근에 가입한 고객부터 순서대로 컨택을 진행하려 합니다.
customers테이블의 정보를 가입일(join_date) 기준 내림차순으로 정렬해 주세요." - [다중 정렬] "고객 목록을 가입일 순으로 최신 가입자가 먼저 오게 정렬하되, 만약 같은 날에 가입한 고객들이 있다면 그 안에서는 이름의 가나다순으로 한 번 더 정렬해 주세요."
- [조건부 정렬 결합] "리뷰(
reviews) 테이블에서 평점(rating)이 4점 이상인 긍정적인 리뷰들만 모아서, 가장 최근에 작성된 리뷰(created_at)부터 내림차순으로 볼 수 있게 리포트를 뽑아주세요."
Tip
쿼리가 왜 이렇게 동작하는지에 대한 원리를 이해하는 것이 중요합니다. 아래 해설을 통해 데이터베이스 엔진이 내부적으로 데이터를 어떻게 걸러내는지 원리를 파악해 보세요.
1. 전체 고객 명단
SELECT *
FROM customers;- 동작 원리:
SELECT는 '무엇을 볼 것인가',FROM은 '어디서 가져올 것인가'를 지정합니다.*(애스터리스크)는 해당 테이블이 가진 모든 컬럼을 가져오라는 의미입니다. DB 엔진은customers테이블의 첫 번째 행부터 마지막 행까지 순차적으로(또는 인덱스를 통해) 스캔하며 모든 열의 데이터를 반환합니다.
2. 전체 상품 목록
SELECT *
FROM products;3. 공급업체 연락처 추출
SELECT name, contact_email
FROM suppliers;- 동작 원리:
*대신 특정 컬럼명을 명시하면, 테이블의 여러 정보 중 지정한 열(Column)의 데이터만 잘라내어(Projection) 메모리에 올린 후 사용자에게 보여줍니다. 이는 네트워크 대역폭과 메모리를 절약하는 실무의 기본 원칙입니다.
4. 카테고리 종류 파악 (중복 제거)
SELECT DISTINCT category_id
FROM products;- 동작 원리:
products테이블에는 수만 개의 상품이 있고, 각 상품은 카테고리 ID를 가집니다.DISTINCT키워드를 사용하면 DB 엔진은category_id열의 데이터를 쭉 읽어들이면서 내부적으로 해시(Hash) 테이블을 만들거나 정렬을 수행하여 중복된 값을 하나로 병합합니다. 그 결과 고유한 ID 종류만 남게 됩니다.
5. 특정 등급 고객
SELECT name, email
FROM customers
WHERE grade = 'VIP';- 동작 원리:
WHERE절은 조건에 맞는 **행(Row)만 필터링(Selection)**하는 역할을 합니다. 오라클에서 문자열은 반드시 홑따옴표(')로 감싸야 하며, 대소문자를 엄격하게 구분합니다('VIP'와 'vip'는 다름).
6. 날짜 기반 신규 고객
SELECT *
FROM customers
WHERE join_date >= TO_DATE('2023-01-01', 'YYYY-MM-DD');- 동작 원리: 날짜 데이터를 비교할 때는 단순 문자열('2023-01-01')로 비교하기보다는,
TO_DATE()함수를 사용하여 명시적으로 오라클의 DATE 타입으로 변환한 후 비교하는 것이 좋습니다. DB 내부에서는 날짜를 고유한 숫자 형태로 저장하므로, 부등호(>=)를 통해 쉽게 '이후' 날짜를 걸러낼 수 있습니다.
7. 특정 가격대 상품 기획전 (BETWEEN)
SELECT product_id, price
FROM product_prices
WHERE price BETWEEN 10000 AND 50000;- 동작 원리:
BETWEEN A AND B는 값이 A 이상이고 B 이하(A <= value <= B)인 데이터를 찾습니다. 경계값(10000과 50000)이 **포함(Inclusive)**된다는 점이 중요합니다. 내부적으로는price >= 10000 AND price <= 50000으로 변환되어 실행됩니다.
8. 할인 상품 제외
SELECT product_id, price
FROM product_prices
WHERE price > 100000;9. 특정 키워드 검색 1 (시작하는 문자열)
SELECT *
FROM customers
WHERE name LIKE '김%';- 동작 원리:
LIKE연산자는 패턴 매칭을 수행합니다.%(퍼센트 기호)는 '길이에 상관없이 어떤 문자라도 올 수 있음'을 의미하는 와일드카드입니다. 따라서'김%'는 '김'으로 시작하는 모든 문자를 찾아냅니다. (단, 와일드카드가 앞에 오는'%김'패턴은 인덱스를 타지 못해 풀 테이블 스캔을 유발하므로 실무에서 주의해야 합니다.)
10. 특정 키워드 검색 2 (끝나는 문자열)
SELECT *
FROM customers
WHERE email LIKE '%@gmail.com';11. 다중 조건 필터링 (IN)
SELECT name, department
FROM staff
WHERE department IN ('Sales', 'Marketing');- 동작 원리:
IN (A, B, C)는 특정 컬럼의 값이 괄호 안의 값 중 하나라도 일치하면 조건을 만족하는 것으로 판단합니다. 내부적으로는department = 'Sales' OR department = 'Marketing'과 동일하게 동작하지만, 조건이 많을수록 가독성이 훨씬 뛰어나며 옵티마이저가 처리하기에도 효율적입니다.
12. 복합 조건 필터링 (AND)
SELECT *
FROM products
WHERE category_id = 2
AND supplier_id = 3;- 동작 원리:
AND연산자는 양쪽의 조건이 **모두 참(TRUE)**일 때만 해당 행을 결과로 반환합니다.
13. 데이터 누락 확인 1 (IS NULL)
SELECT *
FROM customers
WHERE grade IS NULL;- 동작 원리: 데이터베이스에서
NULL은 '값이 0'이거나 '빈 칸(공백)'이라는 뜻이 아니라, **'값이 아예 존재하지 않음(Unknown)'**을 의미하는 특수한 상태입니다. 따라서= NULL이라고 비교하면 영원히 결과를 찾을 수 없습니다. 반드시IS NULL연산자를 사용하여 상태를 점검해야 합니다.
14. 데이터 누락 확인 2
SELECT name
FROM suppliers
WHERE phone IS NULL;15. 데이터 누락 제외 (IS NOT NULL)
SELECT *
FROM suppliers
WHERE phone IS NOT NULL;- 동작 원리:
IS NOT NULL은 반대로 비어있지 않은, 즉 어떤 값이라도 정상적으로 들어가 있는 행만 추출합니다.
16. 부정 조건 검색 (!=, <>)
SELECT *
FROM products
WHERE supplier_id != 1;
-- 또는 WHERE supplier_id <> 1;- 동작 원리: 오라클에서는 같지 않음을 표현할 때
!=또는<>기호를 사용합니다. 둘 다 동일하게 동작하며, 지정한 값을 제외한 나머지 모든 데이터를 반환합니다.
17. 기본 오름차순 정렬
SELECT *
FROM products
ORDER BY name ASC;- 동작 원리:
ORDER BY는 필터링이 모두 끝난 최종 결과를 화면에 출력하기 직전에 재배열하는 역할을 합니다.ASC(Ascending)는 오름차순(가나다, ABC, 123 순서)을 의미하며, 생략 시 기본값으로 적용됩니다. 정렬은 데이터베이스에 큰 부하를 주는 작업(Sorting)이므로 꼭 필요한 경우에만 사용합니다.
18. 기본 내림차순 정렬
SELECT *
FROM customers
ORDER BY join_date DESC;- 동작 원리:
DESC(Descending)는 내림차순을 의미합니다. 날짜의 경우 가장 최근 날짜(큰 값)부터 과거 날짜(작은 값) 순서로 나열됩니다.
19. 다중 정렬
SELECT *
FROM customers
ORDER BY join_date DESC, name ASC;- 동작 원리: 콤마(
,)를 사용하여 정렬 기준을 여러 개 줄 수 있습니다. 먼저join_date를 기준으로 내림차순 정렬을 수행한 후, 가입일이 완전히 동일한 데이터 그룹 내에서만 두 번째 기준인name오름차순 정렬이 적용됩니다.
20. 조건부 정렬 결합
SELECT *
FROM reviews
WHERE rating >= 4
ORDER BY created_at DESC;- 동작 원리 (실행 순서): SQL은 작성된 순서와 실행되는 순서가 다릅니다. 이 쿼리는 1.
FROM reviews(테이블 접근) 2.WHERE rating >= 4(데이터 필터링) 3.SELECT *(데이터 추출) 4.ORDER BY created_at DESC(추출된 데이터를 정렬)의 순서로 내부적으로 실행됩니다. 즉, 필터링이 완전히 끝난 적은 양의 데이터를 바탕으로 마지막에 정렬을 수행하게 됩니다.
