데이터 아키텍처는 데이터를 효과적으로 수집, 저장, 처리, 관리하는 구조와 방법을 설계하는 과정이다.
VMS Solutions의 AWS 기반 데이터 아키텍처 구축
IBM의 데이터 패브릭 아키텍처
자동차 대여 회사의 CAR_RENTAL_COMPANY_CAR
테이블에서 네비게이션
옵션이 포함된 자동차 리스트를 조회한다.
자동차 ID 기준 내림차순 정렬해야 한다.
SELECT CAR_ID, CAR_TYPE, DAILY_FEE, OPTIONS
FROM CAR_RENTAL_COMPANY_CAR
WHERE OPTIONS LIKE '%네비게이션%'
ORDER BY CAR_ID DESC;
OPTIONS LIKE '%네비게이션%'
→ OPTIONS
컬럼에서 네비게이션
이 포함된 자동차 필터링 ORDER BY CAR_ID DESC
→ 자동차 ID 기준 내림차순 정렬 USED_GOODS_BOARD
테이블에서 2022-10-05
에 등록된 중고거래 게시물의 ID, 작성자 ID, 제목, 가격, 거래 상태를 조회한다.
STATUS
값에 따라 한글로 변환하여 출력해야 한다.
SELECT BOARD_ID, WRITER_ID, TITLE, PRICE,
CASE
WHEN STATUS = 'SALE' THEN '판매중'
WHEN STATUS = 'RESERVED' THEN '예약중'
WHEN STATUS = 'DONE' THEN '거래완료'
END STATUS
FROM USED_GOODS_BOARD
WHERE DATE_FORMAT(CREATED_DATE,'%Y-%m-%d') = '2022-10-05'
ORDER BY BOARD_ID DESC;
CASE
문을 사용해 STATUS
값 변환 (SALE
→ 판매중
, RESERVED
→ 예약중
, DONE
→ 거래완료
) DATE_FORMAT(CREATED_DATE, '%Y-%m-%d') = '2022-10-05'
→ 날짜 필터링 ORDER BY BOARD_ID DESC
→ 게시글 ID 기준 내림차순 정렬 PATIENT
, DOCTOR
, APPOINTMENT
테이블에서 2022년 4월 13일 취소되지 않은 흉부외과(CS) 진료 예약 목록을 조회한다.
진료예약번호, 환자이름, 환자번호, 진료과코드, 의사이름, 진료예약일시를 조회해야 한다.
SELECT a.APNT_NO, p.PT_NAME, p.PT_NO, a.MCDP_CD, d.DR_NAME, a.APNT_YMD
FROM APPOINTMENT a
LEFT JOIN DOCTOR d ON a.MDDR_ID = d.DR_ID
LEFT JOIN PATIENT p ON a.PT_NO = p.PT_NO
WHERE a.APNT_CNCL_YN = 'N'
AND DATE_FORMAT(a.APNT_YMD,'%Y-%m-%d') = '2022-04-13'
ORDER BY a.APNT_YMD;
LEFT JOIN
을 사용하여 환자 정보, 의사 정보, 예약 정보 결합 DATE_FORMAT(a.APNT_YMD, '%Y-%m-%d') = '2022-04-13'
→ 날짜 필터링 WHERE a.APNT_CNCL_YN = 'N'
→ 취소되지 않은 예약만 조회 ORDER BY A.APNT_YMD
→ 진료예약일시 기준 오름차순 정렬 CAR_RENTAL_COMPANY_RENTAL_HISTORY
테이블에서 2022년 9월 대여 기록을 조회한다.
대여 기간이 30일 이상이면 '장기 대여', 그렇지 않으면 '단기 대여'로 구분해야 한다.
결과는 대여 기록 ID 기준 내림차순 정렬해야 한다.
SELECT HISTORY_ID,
CAR_ID,
DATE_FORMAT(START_DATE,'%Y-%m-%d') START_DATE,
DATE_FORMAT(END_DATE, '%Y-%m-%d') END_DATE,
CASE WHEN DATEDIFF(END_DATE, START_DATE) >= 29 THEN '장기 대여'
ELSE '단기 대여' END AS RENT_TYPE
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE START_DATE LIKE '2022-09%'
ORDER BY HISTORY_ID DESC;
DATEDIFF(END_DATE, START_DATE) >= 29
→ 대여 기간이 30일 이상이면 '장기 대여'로 분류 DATE_FORMAT(START_DATE, '%Y-%m') LIKE '2022-09'
→ 2022년 9월의 대여 기록만 필터링 ORDER BY HISTORY_ID DESC
→ 대여 기록 ID 기준 내림차순 정렬 USER_INFO
와 ONLINE_SALE
테이블에서 년, 월, 성별 별 상품 구매 회원 수를 조회한다.
결과는 년, 월, 성별을 기준으로 오름차순 정렬해야 한다.
SELECT DATE_FORMAT(S.SALES_DATE, '%Y') YEAR,
DATE_FORMAT(S.SALES_DATE, '%m') MONTH,
U.GENDER,
COUNT(DISTINCT S.USER_ID) USERS
FROM USER_INFO U
JOIN ONLINE_SALE S ON U.USER_ID = S.USER_ID
WHERE U.GENDER IS NOT NULL
GROUP BY YEAR, MONTH, U.GENDER
ORDER BY YEAR, MONTH, U.GENDER;
DATE_FORMAT(S.SALES_DATE, '%Y')
, DATE_FORMAT(S.SALES_DATE, '%m')
→ 연도와 월 추출 JOIN
을 사용해 USER_INFO
와 ONLINE_SALE
테이블 결합 COUNT(DISTINCT S.USER_ID)
→ 회원별 중복 구매 제거하여 구매 회원 수 계산 WHERE U.GENDER IS NOT NULL
→ 성별 정보 없는 회원 제외 ORDER BY YEAR, MONTH, U.GENDER
→ 연, 월, 성별 기준 오름차순 정렬 오늘 푼 5문제를 통해 JOIN
, GROUP BY
, CASE
, DATEDIFF()
, COUNT(DISTINCT)
활용법을 익혔다.
앞으로도 실전 SQL 문제를 풀면서 실력을 키워 나가자.
오늘 학습한 SQL 문제들을 정리한다. 각 문제의 조건과 풀이, 그리고 간단한 설명을 포함한다.
users
테이블을 기준으로 payment
테이블을 LEFT JOIN
하여 결제 여부를 구분.CASE WHEN
을 사용하여 결제 유저와 비결제 유저를 나눔.gb
를 기준으로 유저 수(usercnt
)를 계산.SELECT
CASE WHEN p.game_account_id IS NULL THEN '결제안함'
ELSE '결제함'
END gb,
COUNT(DISTINCT u.game_account_id) usercnt
FROM basic.users u
LEFT JOIN basic.payment p
ON u.game_account_id = p.game_account_id
GROUP BY 1;
LEFT JOIN
을 사용하여 users
테이블을 기준으로 payment
테이블과 조인.CASE WHEN
을 사용해 결제 내역이 있는지 확인하여 분류.COUNT(DISTINCT game_account_id)
를 사용해 각 그룹별 유저 수를 계산.GROUP BY 1
을 통해 결제 여부별로 그룹화.users
테이블에서 serverno >= 2
인 유저만 선택.payment
테이블에서 결제 방식이 CARD
인 데이터만 필터링.game_account_id
를 기준으로 game_actor_id
(캐릭터 ID)를 중복 없이 카운트.pay_amount
를 총합하여 sumamount
계산.HAVING
을 사용하지 않고 actor_cnt >= 2
조건을 적용하여 필터링.SELECT *
FROM (
SELECT u.game_account_id,
COUNT(DISTINCT u.game_actor_id) actor_cnt,
SUM(p.pay_amount) sumamount
FROM (
SELECT *
FROM basic.users
WHERE serverno >= 2
) u
LEFT JOIN (
SELECT *
FROM basic.payment
WHERE pay_type = 'CARD'
) p
ON u.game_account_id = p.game_account_id
GROUP BY u.game_account_id
) t
WHERE t.actor_cnt >= 2
ORDER BY t.sumamount DESC;
LEFT JOIN
을 사용하여 users
테이블과 payment
테이블을 결제 방식이 CARD
인 경우만 조인.COUNT(DISTINCT game_actor_id)
를 사용해 유저가 가진 캐릭터 개수를 중복 없이 계산.SUM(pay_amount)
를 사용해 유저별 총 결제 금액(sumamount
)을 계산.actor_cnt >= 2
인 데이터만 서브쿼리에서 필터링.ORDER BY sumamount DESC
로 총 결제금액이 높은 순서로 정렬.users
테이블에서 game_account_id
, first_login_date
, serverno
추출.payment
테이블에서 각 game_account_id
별 가장 최근 결제일(date2
) 추출.DATEDIFF()
를 사용하여 diffdate
(마지막 결제일 - 첫 접속일) 계산.serverno
별 평균 diffdate
를 ROUND()
로 정수 변환하여 출력.diffdate >= 10
인 경우만 필터링.SELECT serverno,
ROUND(AVG(diffdate)) avgdiffdate
FROM (
SELECT u.game_account_id,
u.first_login_date,
u.serverno,
p.date2,
DATEDIFF(p.date2, u.first_login_date) diffdate
FROM (
SELECT game_account_id, first_login_date, serverno
FROM basic.users
) u
INNER JOIN (
SELECT game_account_id, MAX(approved_at) AS date2
FROM basic.payment
GROUP BY game_account_id
) p
ON u.game_account_id = p.game_account_id
WHERE u.first_login_date < p.date2
) t
WHERE diffdate >= 10
GROUP BY serverno
ORDER BY serverno DESC;
users
테이블에서 필요한 컬럼(game_account_id
, first_login_date
, serverno
)을 가져옴.payment
테이블에서 MAX(approved_at)
을 사용하여 유저별 가장 최근 결제일(date2
)을 찾음.INNER JOIN
을 수행하여 game_account_id
기준으로 데이터를 매칭.WHERE first_login_date < date2
조건을 추가하여 첫 접속일보다 최근 결제일이 큰 경우만 필터링.DATEDIFF(date2, first_login_date)
를 사용하여 diffdate
(이용 기간)를 계산.ROUND(AVG(diffdate))
를 사용하여 서버별 평균 이용 일수를 정수로 변환.WHERE diffdate >= 10
을 추가하여 이용 기간이 10일 이상인 경우만 선택.ORDER BY serverno DESC
를 사용하여 서버번호 기준 내림차순 정렬.오늘 학습한 SQL 문제들은 JOIN 활용, 집계 함수 사용, 조건별 필터링, 서브쿼리 활용 등의 개념을 포함했다.
LEFT JOIN
과 INNER JOIN
의 차이점
LEFT JOIN
: 모든 데이터를 유지하면서 조인된 데이터가 없으면 NULL
반환.INNER JOIN
: 양쪽 테이블에서 일치하는 데이터만 유지.CASE WHEN
을 사용한 데이터 그룹화
CASE
문을 활용하여 결제 유저와 비결제 유저를 나눔.집계 함수 활용 (COUNT(DISTINCT)
, SUM()
, AVG()
)
COUNT(DISTINCT game_actor_id)
: 중복 없이 캐릭터 수 세기.SUM(pay_amount)
: 유저별 총 결제 금액 계산.AVG(diffdate)
: 평균 이용 기간 계산 (반올림 처리).서브쿼리를 활용한 HAVING
대체 방법
HAVING
없이 WHERE
절에서 actor_cnt >= 2
을 필터링.DATEDIFF()
를 활용한 날짜 차이 계산
DATEDIFF(date2, first_login_date) AS diffdate
를 사용하여 이용 기간을 계산.결과 정렬 (ORDER BY
)
sumamount
기준 내림차순 (ORDER BY sumamount DESC
).serverno
기준 내림차순 (ORDER BY serverno DESC
).