최선의 아이디어를 찾기 위해 고안된 방식이니만큼 새로운 제품을 개발하거나 인사이트를 찾고자 할 때 도입해보면 좋겠습니다.
데이터 연결하기 심화
이전 JOIN의 가장 기본이 되는 INNER JOIN을 배웠었다.
이제는 JOIN 을 다양한 방법으로 사용할 수 있게되는 심화 버전을 진행하게 되었다.
1. LEFT JOIN / LETF OUTER JOIN
SELECT * FROMordersAS o LEFT JOIN customers AS c ON o.컬럼1 = c.컬럼1
1. from 뒤에 나오는 왼쪽 테이블 즉, 기준이 되는 orders 테이블을 기준으로 조인을 하게 된다. 2. orders 테이블에 있는 값은 모두 불러오기 때문에, customers에 없는 값은 null처리로 매칭된다 3. 반대로 기준이 되는 테이블에 없는 값이면, 가져오지 않는다.
2. RIGHT JOIN / RIGHT OUTER JOIN
SELECT * FROM orders AS o RIGHT JOINcustomersAS c ON o.컬럼1 = c.컬럼1
1. RIGHT JOIN 뒤에 나오는 오른쪽 테이블 즉, 기준이 되는 customers 테이블을 기준으로 조인을 하게 된다. 2. customers에 있으나, orders 테이블에 없는 경우 null값으로 보여진다 3. customers에 없는 값이면 가져오지 않는다.
-- Q2. 주문을 아직 한 번도 하지 않은 고객들의 정보만 뽑아주세요. SELECT c.* -- 주문내역이 null값이라 고객정보만 보고싶을 경우 FROM customers AS c LEFT JOIN orders AS o ON c.id = o.customer_id WHERE o.id IS NULL
-- Q4. 주문이 한 번도 없었던 음식점 정보를 뽑아주세요. -- 음식점 분류별('restaurant_category')로 몇 개의 음식점이 있는지 숫자를 세어주세요. SELECT r.restaurant_category , COUNT(r.id) AS cnts FROM restaurants AS r LEFT JOIN orders AS o ON r.id = o.restaurant_id WHERE o.id IS NULL GROUP BY r.restaurant_category
[RIGHT JOIN]
-- Q. 배달앱에 가입한 고객 중 앱으로 -- 한 번도 주문하지 않은 고객 정보와 주문 정보를 RIGHT JOIN으로 뽑아주세요. SELECT * FROM orders AS o RIGHT JOIN customers AS c ON o.customer_id = c.id WHERE o.id IS NULL
실무팁)
그럼 모두 다 구하고 싶을 땐??
3. FULL OUTER JOIN : MySQL에선 Outer Join (LEFT JOIN, RIGHT JOIN)
JOIN으로 연결된 두 테이블의 모든 데이터를 표시하고 싶을 때 사용한다.
다만MySQL은 Full Outer Join을 공식적으로 지원하지는 않기 때문에, LEFT JOIN 한 테이블과 RIGHT JOIN한 테이블을 UNION으로 결합하여 Full Outer Join을 구할 수 있다.
SELECT * FROM orders AS o LEFT JOIN customers AS c ON o.컬럼1 = c.컬럼1
UNION
SELECT * FROM orders AS o RIGHT JOIN customers AS c ON o.컬럼1 = c.컬럼1
1. 2. 각 각의 테이블에 없는 값까지 모두 출력될 수 있다.
참고) 그럼 반대로 교집합을 제외한 나머지를 보고 싶은 경우 (= orders에 없는 데이터, customers에 없는 데이터만 보고 싶을 때)
SELECT * FROM orders AS o LEFT JOIN customers AS c ON o.컬럼1 = c.컬럼1 WHERE c.컬럼1 IS NULL UNION
SELECT * FROM orders AS o RIGHT JOIN customers AS c ON o.컬럼1 = c.컬럼1 WHERE o.컬럼1 IS NULL
4. SELF JOIN
MySQL에서 공식적으로 지원하는 문법은 아니지만 실무에서 자주 사용되는 조인 활용법이다.
왜 필요한가?
한 개의 테이블 안에서 매칭되는 경우가 있기 때문에 마치 두개의 테이블을 사용을 해서 조인을 할 수 있다는 개념을 위해 사용되기 때문.
SELECT * FROM customers INNER JOIN customers AS recommender ON customers.ID = recommender.추천인ID ※ 쿼리 하나에 똑같은 테이블이 들어가면 안되어서 별칭을 주어야 한다.
※ INNER JOIN 사용시 매칭되는 값만 추출된다.
[LEFT JOIN] SELECT * FROM customers LEFT JOIN customers AS recommender ON customers.ID = recommender.추천인ID
※LEFT JOIN 사용시 테이블의 모든 컬럼이 추출 된다.
추가 팁)
SQL에서 JOIN은 보통 두 테이블의 동일한 값을 기준으로 연결할 때 사용되지만,
날짜나 수치처럼 단순한 일치가 아닌 조건 비교로도 활용할 수 있다.
예를 들어, 측정일 + 1일 = 다음날과 같은 조건을 JOIN 절에 넣어 당일과 다음날 데이터를 나란히 비교하는 식이다.
이처럼 조인 키가 단순히 같을 필요는 없고,
DATE_ADD, DATE_SUB 같은 함수와 조건문을 활용하면 시계열 데이터 간 비교 분석에도 매우 유용하게 쓸 수 있다.
예시 문제) 일별 카페 매출 데이터 분석
서울의 한 카페에서는 2022년 한 해 동안의 일별 매출 데이터를 기록해두었습니다. daily_sales 테이블에는 각 날짜의 총 매출액이 기록되어 있습니다.
이 데이터를 이용하여 전날보다 매출이 증가한 날의 목록을 구해주세요. 결과에는 아래의 4개 컬럼이 포함되어야 합니다:
today: 현재 날짜 (YYYY-MM-DD) yesterday: 바로 전날 날짜 (YYYY-MM-DD) sales: 현재 날짜의 매출 prev_sales: 전날의 매출
예상 결과는 today 기준 오름차순으로 정렬되어야 하며, 매출이 오른 날만 출력합니다.
답변 쿼리) SELECT s1.sales_date AS today , s2.sales_date AS yesterday , s1.sales , s2.sales AS prev_sales FROM daily_sales AS s1 INNER JOIN daily_sales AS s2 ON s1.sales_date = DATE_SUB(s2.sales_date, INTERVAL 1 DAY)
WHERE s1.sales < s2.sales ORDER BY today
5. [실무 팁] INNER JOIN의 예전 방식도 있다? (=카티션 곱)
일반적으로 사용하는 INNER JOIN
SELECT * FROM orders INNER JOIN customers ON orders.customer_id = customers.id
예전 방식의 조인 (지금은 거의 안 씀)
SELECT * FROM orders, customers WHERE orders.customer_id = customers.id
과거에는 다음과 같이 FROM 절에 테이블을 쉼표로 나열하고,
WHERE 절에서 조인 조건을 명시하는 방식이 종종 사용되었다.
이 방식은 두 테이블의 가능한 모든 조합(카티션 곱)을 생성한 뒤,
조건을 만족하는 결과만 필터링하는 구조이다.
예를 들어, 한 테이블에 2개, 다른 테이블에 3개의 데이터가 있다면 총 6개의 조합이 생성된다.
지금은 가독성과 성능 면에서 INNER JOIN을 사용하는 방식이 훨씬 명확하고 표준이기 때문에 권장된다.
다만, 과거에 작성된 쿼리나 유지보수가 필요한 코드에서 이러한 형태를 마주할 수 있으므로, 익숙해두면 분명 도움이 될 것이다.
날짜 함수 정리
함수
설명
예시
결과
CURDATE()
오늘 날짜 반환
SELECT CURDATE();
2025-05-03
NOW()
현재 날짜와 시간 반환
SELECT NOW();
2025-05-03 14:25:00
DATE_ADD(date, INTERVAL n unit)
날짜에 기간 더하기
DATE_ADD('2025-05-01', INTERVAL 7 DAY)
2025-05-08
DATE_SUB(date, INTERVAL n unit)
날짜에서 기간 빼기
DATE_SUB('2025-05-01', INTERVAL 1 MONTH)
2025-04-01
DATEDIFF(date1, date2)
날짜 차이 계산 (date1 - date2)
DATEDIFF('2025-05-03', '2025-05-01')
2
YEAR(date)
연도 추출
YEAR('2025-05-01')
2025
MONTH(date)
월 추출
MONTH('2025-05-01')
5
DAY(date)
일(day) 추출
DAY('2025-05-01')
1
DAYOFWEEK(date)
요일 (1=일요일, 7=토요일)
DAYOFWEEK('2025-05-03')
7
DATE_FORMAT(date, format)
날짜 포맷 바꾸기
DATE_FORMAT('2025-05-03', '%Y년 %m월 %d일')
2025년 05월 03일
어제보다 높은 값 필터링 WHERE date_col = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
최근 일주일 데이터 WHERE date_col >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
(본 내용은 데이터리안 ‘SQL 데이터 분석 캠프 입문반’을 수강하며 작성한 내용입니다.)