@@ -108,6 +108,8 @@ ORDER BY SALES DESC, PRODUCT_CODE ASC
108108```
109109
110110
111+ ---
112+
111113## 조건에 맞는 도서와 저자 리스트 출력하기(Lv.2)
112114
113115> [ 조건에 맞는 도서와 저자 리스트 출력하기] ( https://school.programmers.co.kr/learn/courses/30/lessons/144854 )
@@ -216,7 +218,10 @@ ORDER
216218
217219* 1번째 보다 더 효율적이라고 생각한다.
218220
219- ## 오랜 기간 보호한 동물(1)
221+
222+ ---
223+
224+ ## 오랜 기간 보호한 동물(1) (Lv.3)
220225
221226> [ 오랜 기간 보호한 동물(1)] ( https://school.programmers.co.kr/learn/courses/30/lessons/59044 )
222227
@@ -226,15 +231,15 @@ ORDER
226231
227232 이때 결과는 보호 시작일 순으로 조회해야 합니다.
228233
229- ### Answer Code(24.01.03 )
234+ ### Answer Code (25.09.04 )
230235
231236``` sql
232- SELECT A .NAME , A .DATETIME
233- FROM ANIMAL_INS A
234- LEFT JOIN ANIMAL_OUTS B ON A .ANIMAL_ID = B .ANIMAL_ID
235- WHERE B .DATETIME IS NULL
236- ORDER BY A .DATETIME
237- LIMIT 3
237+ SELECT INS .NAME , INS .DATETIME
238+ FROM ANIMAL_INS AS INS
239+ LEFT JOIN ANIMAL_OUTS AS OUTS ON INS .ANIMAL_ID = OUTS .ANIMAL_ID
240+ WHERE OUTS .DATETIME IS NULL
241+ ORDER BY INS .DATETIME
242+ LIMIT 3
238243```
239244
240245### 문제 풀이
@@ -247,48 +252,79 @@ ANIMAL_OUTS - 동물의 아이디, 생물 종, 입양일, 이름, 성별 및 중
247252
248253* ` ANIMAL_ID ` 를 기준으로 ` ANIMAL_INS ` 테이블과 ` ANIMAL_OUTS ` 테이블을 JOIN
249254
250- * 아직 입양을 못간 동물 조회(WHERE) -> WHERE B.DATETIME IS NULL
255+ * ` JOIN ` : ANIMAL_INS 테이블(INS)을 기준으로, ANIMAL_OUTS 테이블(OUTS)과 ANIMAL_ID로 LEFT JOIN 한다.
256+
257+ * ** 입양 못 간 동물 조회 (WHERE)** : LEFT JOIN을 하면 입양 기록이 없는 동물의 OUTS 테이블 정보는 ` NULL ` 이 된다. 이 원리를 이용해 ` WHERE OUTS.DATETIME IS NULL ` 조건으로 입양 가지 못한 동물만 골라낸다.
258+
259+ * 정렬 (ORDER BY): 가장 오래 보호소에 있었던 동물'은 ** '보호 시작일이 가장 빠른 동물'** 과 같다. 따라서 보호 시작일 순서대로 정렬한다. (ORDER BY INS.DATETIME)
260+
261+ * 참고로, ORDER BY의 ** 기본 정렬은 오름차순(ASC)** 이다.
262+
263+ * 결과 수 제한 (LIMIT): 정렬된 결과 중 상위 3마리만 선택한다. (LIMIT 3)
264+
265+ * 최종 조회 (SELECT): 해당 동물의 이름(INS.NAME)과 보호 시작일(INS.DATETIME)을 조회한다.
266+
267+ > LIMIT 3가 마지막에 오는 이유
268+
269+ * LIMIT가 마지막에 오는 이유는 ** SQL 쿼리의 실행 순서** 때문이다.
270+
271+ * 보통은 쿼리의 실행 순서는 아래와 같이 진행된다.
272+
273+ * (1) FROM / JOIN: NIMAL_INS와 ANIMAL_OUTS 테이블을 합친다.
251274
252- * 3마리 이름과 보호 시작일 조회(SELECT) -> A.NAME, A .DATETIME (A: ANIMAL_INS) / LIMIT 3
275+ * (2) WHERE: 친 데이터에서 입양 간 동물들을 제외한다. (OUTS .DATETIME IS NULL).
253276
254- * 결과는 보호 시작일 순으로 조회, 가장 오래 보호소에 있었던 동물 -> ORDER BY A .DATETIME (기본이 내림차순)
277+ * (3) ORDER BY: 은 동물들을 보호 시작일(INS .DATETIME) 순서대로 정렬한다.
255278
256- ## 없어진 기록 찾기(1)
279+ * (4) SELECT: 정렬된 결과에서 이름과 보호 시작일을 선택한다.
280+
281+ * (5) LIMIT: 최종적으로 정렬된 결과의 맨 위에서 3개만 잘라낸다.
282+
283+ * 따라서 ** 원하는 순서대로 모든 데이터를 정렬한 뒤** (` ORDER BY ` ), 그중에서 원하는 개수만큼만 가져오기 위해 ` LIMIT ` 는 ** 항상 ` ORDER BY ` 뒤에 위치** 해야 한다.
284+
285+ ---
286+
287+ ## 없어진 기록 찾기 (LV.3)
257288
258289> [ 없어진 기록 찾기] ( https://school.programmers.co.kr/learn/courses/30/lessons/59042 )
259290
260- * 문제: 천재지변으로 인해 일부 데이터가 유실되었습니다.
291+ * 문제:
292+
293+ * 천재지변으로 인해 일부 데이터가 유실되었습니다.
261294
262- 입양을 간 기록은 있는데,
295+ * 입양을 간 기록은 있는데,
263296
264- 보호소에 들어온 기록이 없는 동물의 ID와 이름을 ID 순으로 조회하는 SQL문을 작성해주세요.
297+ * 보호소에 들어온 기록이 없는 동물의 ID와 이름을 ID 순으로 조회하는 SQL문을 작성해주세요.
265298
266- ### Answer Code(24.01.03)
299+ ### Answer Code 1 (24.01.03)
267300
268301``` sql
269- SELECT B .ANIMAL_ID , B .NAME
270- FROM ANIMAL_INS A
271- RIGHT JOIN ANIMAL_OUTS AS B ON A .ANIMAL_ID = B .ANIMAL_ID
272- WHERE A .ANIMAL_ID IS NULL
273- ORDER BY B .ANIMAL_ID
302+ SELECT O .ANIMAL_ID , O .NAME
303+ FROM ANIMAL_INS AS I
304+ RIGHT JOIN ANIMAL_OUTS AS O ON I .ANIMAL_ID = O .ANIMAL_ID
305+ WHERE I .ANIMAL_ID IS NULL
306+ ORDER BY O .ANIMAL_ID ;
274307```
275308
276- ### 문제 풀이
309+ ### Answer Code 1 - 문제 풀이
277310
278- ANIMAL_INS - 동물의 아이디, 생물 종, 보호 시작일, 보호 시작 시 상태, 이름, 성별 및 중성화 여부
311+ ANIMAL_INS
312+ - 동물 보호소에 들어온 동물의 정보.
313+ - 동물의 아이디, 생물 종, 보호 시작일, 보호 시작 시 상태, 이름, 성별 및 중성화 여부
279314
280- ANIMAL_OUTS - 각각 동물의 아이디, 생물 종, 입양일, 이름, 성별 및 중성화 여부
315+ ANIMAL_OUTS
316+ - 동물 보호소에서 입양 보낸 동물의 정보
317+ - 각각 동물의 아이디, 생물 종, 입양일, 이름, 성별 및 중성화 여부
281318
282319> 문제를 풀기 위해서 해야할 작업들
283320
284- * ` ANIMAL_OUTS ` 테이블 기준으로 ` ANIMAL_INS ` 테이블과 ANIMAL_ID으로 ** RIGHT JOIN**
321+ * ` ANIMAL_OUTS ` 테이블 기준으로 ` ANIMAL_INS ` 테이블과 ` ANIMAL_ID ` 으로 ** RIGHT JOIN**
285322
286323* 입양일은 존재하나, 보호 시작일이 존재하지 않은 동물 -> ANIMAL_INS 테이블에서 ANIMAL_ID가 없는 경우
287324
288325* ID와 이름을 조회(SELECT)
289326
290327* ID 순으로 조회(ORDER)
291-
292328---
293329
294330![ ] ( /assets/img/sql/sql-right-join.png )
@@ -299,10 +335,40 @@ ANIMAL_OUTS - 각각 동물의 아이디, 생물 종, 입양일, 이름, 성별
299335
300336* 예를 들어, TABLE_A LEFT JOIN TABLE_B -> TABLE_A의 결과는 모두 노출하고 A와 교집합이 있는 B만 추가로 추출한다는 뜻이다.
301337
302- * 이 문제에서는 ` ANIMAL_OUTS ` 테이블을 기준으로 ID와 이름을 조회할 때 ** RIGHT JOIN** 을 사용하면, 오른쪽에는 ` ANIMAL_OUTS ` 테이블을 작성하고,
303- 왼쪽인 FROM 절 뒤에는 ` ANIMAL_OUTS ` 테이블이 아닌 ` ANIMAL_INS ` 테이블을 작성한다.
338+ * 이 문제에서는 ` ANIMAL_OUTS ` 테이블을 기준으로 ID와 이름을 조회할 때 ** RIGHT JOIN** 을 사용하면,
339+ * 오른쪽에는 ` ANIMAL_OUTS ` 테이블을 작성하고, 왼쪽인 FROM 절 뒤에는 ` ANIMAL_OUTS ` 테이블이 아닌 ` ANIMAL_INS ` 테이블을 작성한다.
304340
305- ## 있었는데요 없었습니다.
341+
342+ ### Answer Code 2(25.09.04)
343+
344+ ``` sql
345+ -- ANIMAL_INS: 동물 보호소에 들어온 동물의 정보
346+ -- ANIMAL_OUTS: 동물 보호소에서 입양 보낸 동물의 정보
347+ -- 보호소에 들어온 기록이 없는 동물의 ID, 이름을 ID순으로 조회
348+ -- Allie -> 입양 보낸 기록은 있는데, 들어온 기록은 없음
349+ -- Spice -> 역시 마찬가지
350+
351+ SELECT O .ANIMAL_ID , O .NAME
352+ FROM ANIMAL_OUTS AS O
353+ LEFT JOIN ANIMAL_INS AS I ON O .ANIMAL_ID = I .ANIMAL_ID
354+ WHERE I .ANIMAL_ID IS NULL
355+ ORDER BY O .ANIMAL_ID ;
356+ ```
357+
358+ ### Answer Code 2 - 문제 풀이
359+
360+ * ` ANIMAL_OUTS ` 테이블을 기준으로 ` ANIMAL_INS ` 테이블과 ` ANIMAL_ID ` 로 LEFT JOIN을 수행한다.
361+
362+ * 입양 기록(` ANIMAL_OUTS ` )은 있지만 보호소에 들어온 기록(` ANIMAL_INS ` )이 없는 동물을 찾는다. (` WHERE I.ANIMAL_ID IS NULL ` 조건)
363+
364+ * 찾아낸 동물의 ID와 이름을 조회(` SELECT ` )한다.
365+
366+ * 결과를 ID 순으로 정렬(` ORDER BY ` )한다.
367+
368+
369+ ---
370+
371+ ## 있었는데요 없었습니다. (LV.3)
306372
307373> [ 있었는데요 없었습니다] ( https://school.programmers.co.kr/learn/courses/30/lessons/59043 )
308374
@@ -312,7 +378,7 @@ ANIMAL_OUTS - 각각 동물의 아이디, 생물 종, 입양일, 이름, 성별
312378
313379 이때 결과는 보호 시작일이 빠른 순으로 조회해야합니다.
314380
315- ### Answer Code(24.01.04)
381+ ### Answer Code 1 (24.01.04)
316382
317383``` sql
318384SELECT ANIMAL_ID, NAME
@@ -322,7 +388,7 @@ WHERE B.DATETIME < A.DATETIME
322388ORDER BY B .DATETIME
323389```
324390
325- ### 문제 풀이
391+ ### Answer Code 1 - 문제 풀이
326392
327393ANIMAL_INS - 동물의 아이디, 생물 종, 보호 시작일, 보호 시작 시 상태, 이름, 성별 및 중성화 여부
328394
@@ -340,6 +406,37 @@ ANIMAL_OUTS - 각각 동물의 아이디, 생물 종, 입양일, 이름, 성별
340406
341407* 보호 시작일을 기준으로 내림차순 정렬하여 결과를 출력한다.
342408
409+ ### Answer Code 2(25.09.04)
410+
411+ ``` sql
412+ -- INS: 동물 보호소에 들어온 동물의 정보
413+ -- OUTS: 테이블은 동물 보호소에서 입양 보낸 동물의 정보
414+
415+ SELECT OUTS .ANIMAL_ID , OUTS .NAME
416+ FROM ANIMAL_OUTS AS OUTS
417+ LEFT JOIN ANIMAL_INS AS INS ON OUTS .ANIMAL_ID = INS .ANIMAL_ID
418+ WHERE OUTS .DATETIME < INS .DATETIME
419+ ORDER BY INS .DATETIME ;
420+ ```
421+
422+ ### Answer Code 2 - 문제 풀이
423+
424+ * 보호 시작일(INS)보다 입양일(OUTS)이 더 빠른 동물의 아이디와 이름을 조회하는 문제.
425+
426+ * 더 빠르다는 것은 더 작다는것이므로 => ` OUTS.DATETIME (입양일) < INS.DATETIME(보호 시작일) `
427+
428+ * 결과는 보호 시작일(INS)이 빠른 순 -> 오름차순
429+
430+ * 날짜와 시간 데이터에서 ** '빠르다'는 것은 '더 이전'을 의미하고, 이는 곧 '더 작은 숫자'** 에 해당한다.
431+
432+ * 빠른 날짜 (과거): 2023년 1월 1일
433+
434+ * 늦은 날짜 (미래): 2025년 9월 4일
435+
436+ * 데이터로 변환하면 과거의 날짜가 더 작은 값을 가집니다. 즉, ` 2023-01-01 ` < ` 2025-09-04 ` 이다.
437+
438+ ---
439+
343440## 주문량이 많은 아이스크림들 조회하기
344441
345442> [ 주문량이 많은 아이스크림들 조회하기] ( https://school.programmers.co.kr/learn/courses/30/lessons/133027 )
@@ -410,4 +507,4 @@ ORDER BY SUM(A.TOTAL_ORDER) + SUM(B.TOTAL_ORDER) DESC LIMIT 3
410507## Reference
411508
412509- https://kkw-da.tistory.com/
413- - https://habiis.tistory.com/118 (JOIN 종류)
510+ - https://habiis.tistory.com/118 (JOIN 종류)
0 commit comments