일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 프로그래머스 # sql #mysql # 코딩테스트 # recursive cte
- inflearn
- 파이썬 # python # copy
- 프로그래머스 # sql # mysql # 코딩테스트 # 연습
- Git
- NVIDIA # GTC 2020 # Webinar # GTC 행사
- github
- 딥러닝 # GPU # 프레임워크 # 엣지 AI # GTC 2020 Online
- udacity#유다시티#자료구조#알고리즘#기초
- mysql # SQL # 프로그래머스 # 코딩테스트
- 딥러닝#초보#MLE#Cross-entropy#메모
- 프로그래머스 # 코딩테스트 # SQL # mysql # 차집합
- spark#apache spark#입문#스파크#빅데이터
- Today
- Total
from Karte,
[프로그래머스 코딩 테스트 연습 SQL] #4. 교집합 본문
* SQL 아마추어가 작성한 내용이라 오류가 있을 수 있습니다. 이외에도 오타, 저작권 문제 등 문제가 있을 시 댓글로 알려주세요.
바로 수정하겠습니다.
* 유/무료 서비스 이름이 등장합니다만, 광고 또는 홍보 목적이 아니며 금전적 지원을 받은 사실이 없음을 밝힙니다.
* 깊은 개념적 이해보다 문제의 예시 답안 코드를 이해하기 위한 수준의 내용을 다루는 점 유의 바랍니다.
다시 돌아온 프로그래머스 SQL 코딩테스트 풀이! 이 문제를 마지막으로 코딩테스트에 올라온 28개의 문제를 모두 풀게 되었다. 👏👏👏 이 문제는 대부분의 다른 문제와 달리 식료품점의 소비자 장바구니 기록을 담은 테이블을 활용하고 있어 푸는 재미가 있었던 기억이 난다.
바로 문제를 보자. 문제의 이름은 <우유와 요거트가 담긴 장바구니> 이다.
네 번째 정리 교집합 또는 그 무엇
문제: 코딩테스트 연습 - 모든 문제 - MySQL 필터링 적용 - 우유와 요거트가 담긴 장바구니
본문 링크: programmers.co.kr/learn/courses/30/lessons/62284
CART_PRODUCTS 테이블은 장바구니에 담긴 상품 정보를 담은 테이블로, 구조는 아래와 같다.
NAME | TYPE |
ID | INT |
CART_ID | INT |
NAME | VARCHAR |
PRICE | INT |
* ID : 테이블의 아이디
* CART_ID : 장바구니의 아이디
* NAME : 상품 종류
* PRICE : 가격
데이터 분석 팀에서는 우유 ( Milk ) 와 요거트 ( Yogurt ) 를 동시에 구입한 - 원문에는 '구입한' 이라고 되어 있는데 맥락 상 '담은' 이 맞는 표현 아닌가 싶다 - 장바구니가 있는지 알아보려고 한다. 따라서 우유와 요거트를 동시에 구입한 장바구니의 아이디를 오름차순으로 조회하는 SQL 문을 작성해야 한다.
예를 들어 우측과 같이 CART_PRODUCTS 테이블이 존재한다면, 우유와 요거트 구매 기록이 모두 존재하는 장바구니는 286 과 448 이므로 쿼리 문을 돌렸을 때 286 과 448 이 순서대로 나와야 한다.
여기서 주목해야 할 점은 CART_PRODUCTS 테이블이 하나만 주어졌다는 것이다. 따라서 하나의 테이블을 상품 종류 ( NAME ) 에 따라 Milk 만을 포함하는 가상의 테이블과 Yogurt 만을 포함하는 가상의 테이블로 나눈 후, 두 개의 가상의 테이블에 모두 존재하는 CART_ID 를 조회하면 문제를 해결할 수 있을 것이라고 생각했다.
위에서 생각한 해결 방식에 따라 가장 먼저 떠오른 쿼리는 바로 아래와 같이 INTERSECT 를 이용하는 것이었는데...stackoverflow 를 찾아보니 MySQL 에는 INTERSECT 란 키워드가 존재하지 않는다고 한다. 즉, 집합 연산자로 손쉽게 끝을 볼 수는 없다는 이야기! 그래서 가장 처음 생각한 코드는 아래와 같이 WHERE 과 서브쿼리의 조합을 이용한 것이었다.
SELECT DISTINCT CART_ID # 같은 CART_ID 가 여러번 나오지 않도록 방지
FROM CART_PRODUCTS
WHERE CART_ID IN (
SELECT CART_ID FROM CART_PRODUCTS WHERE NAME = 'Milk' # 서브쿼리로 우유 구매 기록만 포함하는 가상의 테이블 생성
) AND CART_ID IN (
SELECT CART_ID FROM CART_PRODUCTS WHERE NAME = 'Yogurt' # 서브쿼리로 요거트 구매 기록만 포함하는 가상의 테이블 생성
)
ORDER BY CART_ID ASC;
그런데 이전 "[프로그래머스 코딩 테스트 연습 SQL] #3. 차집합" 포스팅에서 이렇게 특정 조건을 만족하는 가상의 테이블을 생성하고 집합 연산을 해야 하는 경우 WHERE 과 서브 쿼리를 사용하면 성능이 현저히 떨어질 수도 있다는 이야기를 한 적이 있다.
2020/10/01 - [study log/SQL] - [프로그래머스 코딩테스트 연습 SQL] #3. 차집합
그러니 이번에도 위와 같은 방법 말고 JOIN 을 쓰는 방법도 생각해 보기로 하자.
SELECT DISTINCT milk.CART_ID CART_ID
FROM (SELECT CART_ID FROM CART_PRODUCTS WHERE NAME = 'Milk') milk # 서브쿼리로 우유 구매 기록만 포함하는 가상의 테이블 milk 생성
INNER JOIN (SELECT CART_ID FROM CART_PRODUCTS WHERE NAME = 'Yogurt') yogu # 서브쿼리로 요거트 구매 기록만 포함하는 가상의 테이블 yogu 생성
ON milk.CART_ID = yogu.CART_ID # CART_ID 기준으로 INNER JOIN 실행 -> milk 와 yogu 에 공통적으로 존재하는 CART_ID 추출
ORDER BY CART_ID ASC;
만약 위의 코드가 지저분해 보여서 싫다면? WITH Clause 로 주된 작업을 수행하는 쿼리 밖에서 먼저 CTE ( Common Table Expression ) 을 생성해 주면 된다.
# 2개의 cte 생성
WITH milk AS (
SELECT CART_ID FROM CART_PRODUCTS
WHERE NAME = 'Milk'
), # 2개 이상의 cte 생성 시 하나 선언 후 쉼표 찍어서 구분하는 점 주의
yogu AS (
SELECT CART_ID FROM CART_PRODUCTS
WHERE NAME = 'Yogurt'
)
# 아래부터 주 쿼리
SELECT DISTINCT milk.CART_ID CART_ID
FROM milk INNER JOIN yogu
ON yogu.CART_ID = milk.CART_ID
ORDER BY CART_ID ASC;
코드의 수는 더 길어졌지만 가독성 부분에서 더 좋아진 것을 볼 수 있다.
이렇게 해서 이번 문제도 다 풀었다. 다음에는 이번 문제를 활용해서 MySQL 의 또 다른 함수에 대해 알아보는 포스팅을 계획 중인데, 머신러닝 관련 작성 중인 글들도 있어 언제 올릴지는 잘 모르겠다.
코딩 테스트 문제를 풀어보니 좋은 것이, 내 코드의 성능은 어떨지 한번이라도 더 고민하게 된다. 또 현업에서는 분명 몇 백 줄 단위로 코드를 작성할 텐데, 지금부터 미리 MySQL 코드 스타일 가이드가 있는지 한번 찾아보고 가독성을 높이기 위해 노력을 해야겠다는 생각도 든다.
다음에는 더 어쩌면 나에게만 흥미로운 SQL 문제로 돌아오려고 한다. 그럼 오늘은 여기서 이만 끄-읕!
'study log > SQL' 카테고리의 다른 글
[프로그래머스 코딩테스트 연습 SQL] #3. 차집합 (0) | 2020.10.01 |
---|---|
[프로그래머스 코딩 테스트 연습 SQL] #2. RECURSIVE WITH 문 ( Recursive CTE) (0) | 2020.09.25 |
[프로그래머스 코딩테스트 연습 SQL ] #1. string type 열에서 조건 검색 (0) | 2020.09.25 |