프로그래머스 LEVEL-4 문제입니다.
직전 게시글 문제랑 같은 레벨이지만 다른 난이도인 거 같아요. 아무래도 직전 게시글이 LEVEL-4가 아니라 5인 거 같음...
(다른 사람 해답 풀이과정 게시글 참고했는데, 거기서 'LEVEL-5'라고 적혀있는 거 보면..)
이번엔 제가 처음으로 AI(CLAUDE)의 도움을 안 받고 스스로 문제를 풀었습니다..!! (와우..ㅎㅎ..)



*테이블
1. FOOD_PRODUCT(PRODUCT_ID, PRODUCT_NAME, PRODUCT_CD, CATEGORY, PRICE)
- FOOD_ORDER(ORDER_ID, PRODUCT_ID(FK.비식별관계(일반 참조)), AMOUNT, PRODUCE_DATE, IN_DATE, OUT_DATE, FACTORY_ID, WAREHOUSE_ID)
*문제 정리
1.생산일자(PRODUCE_DATE, FOOD_ORDER 테이블)가 2022-05인 식품들
2.식품 ID(PRODUCT_ID, FOOD_PRODUCT 테이블), 식품 이름(PRODUCT_NAME, FOOD_PRODUCT 테이블), 총매출(유도 속성? 새로운 열을 만들어서 계산: PRICE * AMOUNT 같은데? 계산 테스트해보니 맞음.) 조회.
3.총매출 기준으로 내림차순 정렬, 총매출이 같으면 식품 ID 기준으로 오름차순 정렬.
->문제 정리에서 2번을 보면, 저는 문제를 읽으면서 이렇게 생각했습니다.
1.예시를 보니 AMOUNT를 합쳐야하는데, GROUP BY 사용해야 함.(ORDER_ID 기준으로 PK이므로, 중복이 허용될 수 밖에 없음.)
- 그리고 식품 이름이라고 한 거 보니, 2개의 테이블이 있는데 그 중에서 PK로 쓰는 첫번째 테이블을 지칭하는 거 보니 이 부분 유심히 봐봐.
<정답 코드>
SELECT
PRODUCT_ID
FROM FOOD_ORDER
WHERE PRODUCE_DATE BETWEEN '2022-05-01' AND '2022-05-31'
저는 날짜를 조회해야하므로, 'BETWEEN' 개념을 사용하였습니다.
TMI이지만, 이 BETWEEN 사용 시에도 여러번 틀렸습니다.
맨 처음에는,
WHERE PRODUCE_DATE BETWEEN CONVERT(DATE, '2022-05-01') AND CONVERT(DATE, '2022-05-31') -- MSSQL 언어 문법
이렇게 MSSQL 문법을 사용했고,
그 다음은,
WHERE PRODUCE_DATE BETWEEN (DATE, '2022-05-01') AND (DATE, '2022-05-31') -- 허용 안됨.
이는 허용이 안 되었고,
최종적으로 저 정답 코드를 사용했습니다.
이를 언급하는 이유는,
'각 SQL 언어에 맞는 문법이 있음'을 강조하기 위함입니다.
그리고, 저는 이 작업이 끝난 후에,
직전 게시글에 푼 문제처럼 단순 ID만이 아닌 NAME까지 알고 싶었습니다.
->이 방법은, JOIN 후 해당 열을 선택하면 되는데, 일단 문제에서 요구하는 것만 풀 것이므로 보류하도록 하겠습니다.
근데 '문제 정리'의 2번을 보아하니 JOIN을 해야되는 거처럼 보입니다.
그 이유는 다음과 같습니다.
*이유
1.유도 속성에서 AMOUNT와 PRICE로 값을 구해야한다.
2.위 2-1번에서 말한 것처럼 PRODUCT_ID 뿐만 아니라 PRODUCT_NAME까지 알고 싶다.
그럼 우리는 '무슨 조인?'을 하며 '어떤 공통된 열(속성)'을 기준으로 조인을 할 것인지 정해야 합니다.
*공통 속성: PRODUCT_ID -> PK이자 FK이니깐
*조인: INNER JOIN.
조인에서 저는 처음에는 'LEFT OUTER JOIN'을 선택했습니다.
그 이유는 다음과 같습니다.
*'LEFT OUTER JOIN'을 선택한 이유
테이블 크기는 '왼쪽 테이블(FOOD_PRODUCT) >= 오른쪽 테이블(FOOD_ORDER)'.
식품들 정보는 다 살려두면서 겹치지 않는 부분만 FOOD_ORDER 테이블의 속성들을 모두 NULL 처리하면 깔끔하다.
PRODUCT_ID는 공통된 속성(열)이자, 왼쪽 테이블에 온전히 있으므로, 이 부분은 원본 그대로 ALL 유지됨.
그렇게 해서 결과를 돌려봤습니다.

그런데 뭔가 이상한 느낌이 들었습니다.
뭔가 INNER JOIN(교집합)으로도 같은 결과가 나올 거 같다는 생각이 든 것입니다.
그래서 INNER JOIN으로 바꿔서 결과를 돌려봤습니다.

어 결과가 다릅니다.
곰곰이 생각해봤습니다.
당연히 다른 이유가 있었습니다.
※결과가 다른 이유
FP.PRODUCT_ID는 공통된 속성에 특히 왼쪽 테이블 열입니다.
그니깐 ON 조건은 같은 거지만, 어쨋든 LEFT OUTER JOIN은 결과가 다 나오는 게 당연한 겁니다.
반면, INNER JOIN은 말 그대로 공통된 부분들만 추려내므로, 정답은 INNER JOIN이 맞습니다.
(결과를 보면, 왼쪽 조인 시에 중간에 번호가 건너뛴 거는 원래 데이터 형태가 그런 거 같습니다. 일정하게 그런 규칙들이 보입니다.)
그 다음은
1. 각 테이블에 별칭을 지어주고(간편성 목적)
2. 2-1번의 WHERE절 조건을 붙이고
3. GROUP BY를 실행합니다.
->GROUP BY 열은 FO.PRODUCT_ID로 하고, SELECT에 유도 속성 열을 추가합니다.
->GROUP_BY 열을 FP.PRODUCT_ID가 아닌 FO.PRODUCT_ID로 선택한 이유는, 당연히 FP.PRODUCT_ID는 PK(기본키)이므로 중복 자체가 허용될 수 없으므로 FO.PRODUCT_ID를 사용합니다.
->엄밀히 말하면, 틀린 설명입니다. 밑에 설명을 참고하시면 됩니다. 제가 말하고 싶은 부분이 밑 부분 설명입니다.
<틀린 이유>

->좀 더 명확한 설명은, FO.PRODUCT_ID는 FK(외래키)로 중복이 있을 수 있으므로, 같은 상품의 여러 주문을 하나로 묶기 위해 GROUP BY 사용
->GROUP BY의 개념을 정확히 이해하고, 결과표를 보면서 이해하면 쉽게 이해가 될 것입니다.
SELECT
FP.PRODUCT_ID,
FP.PRODUCT_NAME,
(FP.PRICE * FO.AMOUNT) AS TOTAL_SALES
FROM FOOD_PRODUCT FP INNER JOIN FOOD_ORDER FO
ON FP.PRODUCT_ID = FO.PRODUCT_ID
WHERE FO.PRODUCE_DATE BETWEEN '2022-05-01' AND '2022-05-31'
GROUP BY FO.PRODUCT_ID
하지만, 이렇게 하면 오답이 나옵니다.
GROUP BY를 하기 전에 SUM(집계)하는 것을 빼먹었기 때문입니다.
<GROUP BY 안하고 결과 낸 것>

<GROUP BY 하고 결과 낸 것>

이 사진들을 유심히 보면,
GROUP BY는 그룹화만 하는 성질만을 가지고 있으므로,
SUM(집계)가 따로 없으면, 여러 행(중복 행)을 한 행으로 합치지만, 다른 행은 무시하고 버려버립니다.
그래서 SUM을 추가합니다.
<'SUM TEST'>


이렇게 SUM이 잘 작동되는 것을 확인했으므로,
<정답 코드>
SELECT
FP.PRODUCT_ID,
FP.PRODUCT_NAME,
-- SUM(FO.AMOUNT)
FP.PRICE * SUM(FO.AMOUNT) AS TOTAL_SALES
FROM FOOD_PRODUCT FP INNER JOIN FOOD_ORDER FO
ON FP.PRODUCT_ID = FO.PRODUCT_ID
WHERE FO.PRODUCE_DATE BETWEEN '2022-05-01' AND '2022-05-31'
GROUP BY FO.PRODUCT_ID
여기는 세부 조건만 작성하면 됩니다.
->ORDER BY절
<최종 정답 코드>
SELECT
FP.PRODUCT_ID,
FP.PRODUCT_NAME,
-- SUM(FO.AMOUNT)
FP.PRICE * SUM(FO.AMOUNT) AS TOTAL_SALES
FROM FOOD_PRODUCT FP INNER JOIN FOOD_ORDER FO
ON FP.PRODUCT_ID = FO.PRODUCT_ID
WHERE FO.PRODUCE_DATE BETWEEN '2022-05-01' AND '2022-05-31'
GROUP BY FO.PRODUCT_ID
ORDER BY TOTAL_SALES DESC, FP.PRODUCT_ID ASC;
이렇게 해서 이번 LEVEL-4 문제를 풀어보았습니다.
직전 게시글 문제보다는 훨씬 간편하네요.
다음에 또 뵙도록 하겠습니다.~~ :) bb