[프로그래머스 SQL 코딩테스트 연습] Lv3. 업그레이드 할 수 없는 아이템 구하기 (MySQL)
🤔 문제
어느 한 게임에서 사용되는 아이템들은 업그레이드가 가능합니다.
'ITEM_A' → 'ITEM_B'와 같이 업그레이드가 가능할 때 'ITEM_A'를 'ITEM_B'의 PARENT 아이템, PARENT 아이템이 없는 아이템을 ROOT 아이템이라고 합니다.
예를 들어 'ITEM_A' → 'ITEM_B' → 'ITEM_C' 와 같이 업그레이드가 가능한 아이템이 있다면
'ITEM_C'의 PARENT 아이템은 'ITEM_B'
'ITEM_B'의 PARENT 아이템은 'ITEM_A'
ROOT 아이템은 'ITEM_A'가 됩니다.
다음은 해당 게임에서 사용되는 아이템 정보를 담은 `ITEM_INFO` 테이블과 아이템 관계를 나타낸 `ITEM_TREE` 테이블입니다.
`ITEM_INFO` 테이블은 다음과 같으며, `ITEM_ID`, `ITEM_NAME`, `RARITY`, `PRICE`는 각각 아이템 ID, 아이템 명, 아이템의 희귀도, 아이템의 가격을 나타냅니다.
`ITEM_TREE` 테이블은 다음과 같으며, `ITEM_ID`, `PARENT_ITEM_ID`는 각각 아이템 ID, PARENT 아이템의 ID를 나타냅니다.
단, 각 아이템들은 오직 하나의 PARENT 아이템 ID 를 가지며, ROOT 아이템의 PARENT 아이템 ID 는 NULL 입니다.
ROOT 아이템이 없는 경우는 존재하지 않습니다.
더 이상 업그레이드할 수 없는 아이템의 아이템 ID(ITEM_ID), 아이템 명(ITEM_NAME), 아이템의 희귀도(RARITY)를 출력하는 SQL 문을 작성해 주세요. 이때 결과는 아이템 ID를 기준으로 내림차순 정렬해 주세요.
예시
예를 들어 `ITEM_INFO` 테이블이 다음과 같고
`ITEM_TREE` 테이블이 다음과 같다면
'ITEM_A' 는 'ITEM_B', 'ITEM_C' 로 업그레이드가 가능하며 'ITEM_B' 는 'ITEM_D', 'ITEM_E' 로 업그레이드가 가능합니다. 'ITEM_C', 'ITEM_D', 'ITEM_E' 는 더 이상 업그레이드가 가능하지 않으므로 결과는 다음과 같이 나와야 합니다.
💻 나의 풀이
첫 번째 시도(오답)
WITH PARENT AS(
SELECT ITEM_ID AS PARENTS
FROM ITEM_TREE
WHERE ITEM_ID IN PARENT_ITEM_ID -- (1) 해당 아이템이 부모 아이템이거나
AND ITEM_ID IN (SELECT ITEM_ID -- (2) 전체에서 루트 아이템밖에 없는 경우
FROM ITEM_TREE
WHERE COUNT(ITEM_ID) = 1
AND PARENT_ITEM_ID IS NULL)
)
SELECT ITEM_ID, ITEM_NAME, RARITY
FROM ITEM_INFO i
JOIN PARENT p ON i.ITEM_ID = p.PARENTS
WHERE p.PARENTS IS NULL
ORDER BY 1 DESC;
우선 문제에서 말하는 [더 이상 업그레이드할 수 없는 아이템]은 말단 노드들을 뜻한다.
이를 구하게 위해서,
(1) 해당 `ITEM_ID`가 `PARENT_ITEM_ID`이거나 (2) 전체에서 루트 아이템밖에 없는 경우를 제외한 ITEM들이 말단 노드라고 생각했다. ((1)은 그렇다치고 (2)는 왜 그렇게 생각했지..)
그래서 먼제 CTE절로 (1)과 (2)를 만족하는 `ITEM_ID`를 뽑고 `ITEM_INFO`와 조인해서 `ITEM_ID`가 NULL인 것들만 조회하려고 했다.
당연하게도 오답이다.
두번째 시도(오답)
SELECT ITEM_ID, ITEM_NAME, RARITY
FROM ITEM_INFO
WHERE ITEM_ID NOT IN (SELECT PARENT_ITEM_ID
FROM ITEM_TREE)
ORDER BY 1 DESC;
위의 생각을 반전시켜보니, 그냥 `PARENT_ITEM_ID`에 없는 `ITEM_ID`를 조회하면 되는 것이었다.
그런데 오답으로 나왔다..
🖍 오답노트
틀린 이유
두번째 시도가 틀린 이유가 무엇일까?
`NOT IN`을 사용하게 되면 서브쿼리에 있는 모든 `PARENT_ITEM_ID`에 대해서 `!=`의 결과가 참인 데이터가 조회된다.
`PARENT_ITEM_ID`컬럼에는 NULL값(최상단 노드)이 있기 때문에 `NULL`과 `!=`연산을 하게 될 경우, UNKNOWN(거짓)값이 리턴되어 전부 조회되지 않는 것이다. 따라서 `PARENT_ITEM_ID`컬럼이 `NULL`인 행은 제외하고 조회해야 한다.
정답 풀이
SELECT ITEM_ID, ITEM_NAME, RARITY
FROM ITEM_INFO
WHERE ITEM_ID NOT IN (SELECT PARENT_ITEM_ID
FROM ITEM_TREE
WHERE PARENT_ITEM_ID IS NOT NULL)
ORDER BY 1 DESC;
✅ 핵심 정리
1. IN
2. NOT IN
3. EXISTS
4. NOT EXISTS
🔗 References
[틀린 이유 참고]
[NOT IN vs. NOT EXISTS 참고]
https://wildeveloperetrain.tistory.com/223