๐ฆ [SQL] 59413 ์ ์ ์๊ฐ ๊ตฌํ๊ธฐ (2)
https://school.programmers.co.kr/learn/courses/30/lessons/59413
1. ๋ฌธ์
ํ ์ด๋ธ
- ANIMAL_OUTS(ANIMAL_ID, ANIMAL_TYPE, DATETIME, NAME, SEX_UPON_OUTCOME)
์กฐ๊ฑด
- 0์๋ถํฐ 23์๊น์ง ๊ฐ ์๊ฐ๋๋ณ ์ ์ ๊ฑด์(COUNT) ๊ตฌํ๊ธฐ
- ์ ์์ด ์๋ ์๊ฐ๋๋ 0์ผ๋ก ํ์
- ๊ฒฐ๊ณผ๋ ์๊ฐ๋(HOUR) ์์๋๋ก ์ ๋ ฌ
- DATETIME ์ปฌ๋ผ์์ ์๊ฐ๋ง ์ถ์ถํด์ ๊ณ์ฐ
2. ์ค๋ต์ฟผ๋ฆฌ
2.1 ์ฒซ๋ฒ์งธ ์๋
์ ์ ๊ธฐ๋ก์ด 0๊ฑด์ธ ์๊ฐ๋ ๋๋ฝ
-- ์ฝ๋๋ฅผ ์
๋ ฅํ์ธ์
SELECT HOUR(DATETIME) AS HOUR, COUNT(*) AS COUNT
FROM ANIMAL_OUTS
GROUP BY HOUR(DATETIME)
ORDER BY HOUR(DATETIME)
์ฒ์์ ์์ฑํ SQL๋ฌธ์ด ํ๋ฆฐ ์ด์ ๋ 0~23์ ์ค ์ ์์ด ์๋ ์๊ฐ๋๋ ๊ฒฐ๊ณผ์ ํฌํจ๋์ด์ผ ํ๋๋ฐ, ์๋์ ๊ฐ์ด ์ถ๋ ฅ๊ฒฐ๊ณผ ์ ์ ๋ฐ์ดํฐ๊ฐ ์๋ ์๊ฐ๋ง ๋์ค๊ธฐ ๋๋ฌธ์ด๋ค. GROUP BY๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์กด์ฌํ๋ ๋ฐ์ดํฐ๋ง ์ง๊ณํด์, ์ ์ ๊ธฐ๋ก์ด ์๋ ์๊ฐ๋๋ ๊ฒฐ๊ณผ์ ์ ๋ํ๋๋ค๊ณ ํ๋ค.
๋ฌธ์ ์์์ฒ๋ผ 0๊ฑด๋ ํ์ํ๋ ค๋ฉด ์๊ฐ ๋ฒ์๋ฅผ ๋ง๋ค์ด์ LEFT JOINํด์ผ ํ๋ค.
LEFT JOINํ๋ฉด ๋์์ด ๋๋ ํ
์ด๋ธ์ ๋ฐ์ดํฐ๋ ๋ชจ๋ ์ถ๋ ฅ๋๊ธฐ ๋๋ฌธ์ด๋ค.
2.2 ๋ ๋ฒ์งธ ์๋
๋ณ์ @HOUR๋ฅผ ํ๋์ฉ ์ฆ๊ฐ์ํค๋ฉด์ ์๋ธ์ฟผ๋ฆฌ๋ก COUNT๋ฅผ ๋ฝ๋ ๋ฐฉ์
SET @HOUR = -1;
SELECT (@HOUR := @HOUR + 1) AS HOUR,
(SELECT COUNT(HOUR(DATETIME))
FROM ANIMAL_OUTS
WHERE HOUR(DATETIME)=@HOUR) AS COUNT
FROM ANIMAL_OUTS
WHERE @HOUR < 23;
FROM ANIMAL_OUTS
๋ฅผ ๋ถ์ฌ์ ์ฌ์ค์ ํ ์ด๋ธ์ ํ ์๋งํผ ๋ฃจํ๊ฐ ๋๋ ๊ตฌ์กฐ๋ผ ๋ถํ์ํ๊ฒ ๋ง์ด ๋ฐ๋ณต๋๋ค.WHERE @HOUR < 23
์กฐ๊ฑด๋ ํ ์ด๋ธ ํ๋ง๋ค ๊ฒ์ฌํ๊ธฐ ๋๋ฌธ์ ์ค๋ณต๋ ๊ฒฐ๊ณผ๊ฐ ๋์ฌ ์ ์๋ค.- ์๋ธ์ฟผ๋ฆฌ์์
COUNT(HOUR(DATETIME))
๋์COUNT(*)
๊ฐ ๋ ์์ ํ๋ค.
๋ฐ๋ผ์ ์๋์ ๊ฐ์ด ๋ณ์ + ์๋ธ์ฟผ๋ฆฌ์ LIMIT ๋ฐฉ์์ผ๋ก ๋ฐ๊พธ๋ฉด ์ฑ๋ฅ์ด ์ข ๋ซ๋ค๊ณ ํ๋ค.
SET @HOUR := -1;
SELECT @HOUR := @HOUR + 1 AS HOUR,
(SELECT COUNT(*)
FROM ANIMAL_OUTS
WHERE HOUR(DATETIME) = @HOUR) AS COUNT
FROM information_schema.COLUMNS
LIMIT 24;
3. ์ ๋ต์ฟผ๋ฆฌ
์ ์ ์๊ฐ ๊ตฌํ๊ธฐ ๋ฌธ์ ๋ ํฌ๊ฒ ์ธ ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ํด๊ฒฐํ ์ ์๋ค๊ณ ํ๋ค.
- UNION ALL ๋ฐฉ์
- ๋ณ์(@HOUR) ๋ฐฉ์ - ๋ฃจํ ํ๋ด
- WITH RECURSIVE ๋ฐฉ์ (MySQL 8 ์ด์) - ์ถ์ฒ
3.1 UNION ALL ๋ฐฉ์ (MySQL 5.x์์๋ ๊ฐ๋ฅ)
WITH hours AS (...)
๋ก 0~23์๋ฅผ ๋ง๋ค์ด์ ๋ชจ๋ ์๊ฐ๋ ํฌํจํ๊ธฐLEFT JOIN
์ผ๋ก ์ ์ ๊ธฐ๋ก๊ณผ ์ฐ๊ฒฐ, ๊ธฐ๋ก์ด ์์ผ๋ฉดNULL
COALESCE
๋ก NULL์ 0์ผ๋ก ๋ณํํ๊ธฐGROUP BY
ํORDER BY
๋ก ์๊ฐ์ ์ ๋ ฌ
SELECT h AS HOUR,
COALESCE(COUNT(a.DATETIME), 0) AS COUNT
FROM (
SELECT 0 AS h UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11
UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19
UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23
) AS hours
LEFT JOIN ANIMAL_OUTS a
ON h = HOUR(a.DATETIME)
GROUP BY h
ORDER BY h;
COALESCE(COUNT(a.DATETIME), 0)
โ โ์์ผ๋ฉด 0์ผ๋ก ํ์โSELECT 0 AS h
โ โ์๊ฐ ์ซ์(0)๋ฅผ ๋ง๋ค๊ณ ์ด ์ด๋ฆ์ h๋ผ๊ณ ๋ถ์โ
UNION ALL ์ด ๊ธธ์ด์ ๊ฐ๋ ์ฑ์ด ์ข ๋จ์ด์ง๊ธด ํ์ง๋ง ์ด๋ ๊ฒ ํ๋ฉด MySQL๋ฒ์ ์ ์๊ด ์์ด ๋์ํ๋ค.
NULL ์ฒ๋ฆฌ
1๏ธโฃ COALESCE(COUNT(a.DATETIME), 0) AS COUNT
COUNT(a.DATETIME)
- ํน์ ์๊ฐ๋์ ์ ์๋ ๋๋ฌผ ์๋ฅผ ์ธ๋ ๊ฒ์ด๋ค.
- LEFT JOIN์ ํ์ ๋ ํด๋น ์๊ฐ๋์ ์ ์์ด 1๊ฑด๋ ์์ผ๋ฉด NULL์ด ๋จ์ด์ง ์ ์๊ธฐ ๋๋ฌธ์..
COALESCE(๊ฐ, ๋์ฒด๊ฐ)
- ์ฒซ ๋ฒ์งธ ๊ฐ์ด NULL์ด๋ฉด ๋ ๋ฒ์งธ ๊ฐ์ ๋์ ๋ฐํํ๋ค.
- ์ฆ, COUNT(a.DATETIME)์ด NULL์ผ ๊ฒฝ์ฐ 0์ผ๋ก ๋ฐ๊ฟ์ฃผ๋ ํจ์์ด๋ค.
UNION ALL ์ฐ์ฐ
2๏ธโฃ SELECT 0 AS h
์ด ๋ถ๋ถ์ ์๊ฐ ์ซ์๋ฅผ ๊ฐ์ ๋ก ๋ง๋ค์ด๋ด๋ ์ฝ๋์ด๋ค.
SELECT 0 AS h
โ ์คํํ๋ฉด ๊ฒฐ๊ณผ๊ฐ ์ด๋ ๊ฒ ๋์จ๋ค.
h |
---|
0 |
์ฌ๊ธฐ์๋ค๊ฐ
SELECT 0 AS h UNION ALL SELECT 1 UNION ALL SELECT 2
โ ์คํํ๋ฉด
h |
---|
0 |
1 |
2 |
์ด๋ ๊ฒ 0, 1, 2โฆ ์ซ์ ๋ชฉ๋ก์ ์ธ์์ ์ผ๋ก ๋ง๋ค์ด์ ANIMAL_OUTS
ํ
์ด๋ธ๊ณผ JOIN
ํ ์ ์๋ ๋ฐํ์ ๋ง๋๋ ๊ฒ์ด๋ค.
์ฆ, "0์, 1์, 2์ โฆ 23์"๋ผ๋ ๋ชจ๋ ์๊ฐ๋๋ฅผ ๊ฐ์ ๋ก ์์ฑํด์ฃผ๋ ์ญํ ์ด๋ค.
3.2 ๋ณ์(@HOUR) ๋ฐฉ์ (๋ฃจํ ํ๋ด)
SET @HOUR := -1;
โ ๋ณ์ ์ค๋นSELECT @HOUR := @HOUR + 1 AS HOUR ... LIMIT 24;
โ@HOUR
๊ฐ 0 โ 1 โ 2 โฆ โ 23๊น์ง ์๋ ์ฆ๊ฐ(SELECT COUNT(*) FROM ANIMAL_OUTS WHERE HOUR(DATETIME) = @HOUR)
โ ํด๋น ์๊ฐ๋์ ์ ์ ๊ฑด์๋ฅผ ์ธ์ ๊ฒฐ๊ณผ์ ๋ถ์
SET @HOUR := -1;
SELECT @HOUR := @HOUR + 1 AS HOUR,
(SELECT COUNT(*)
FROM ANIMAL_OUTS
WHERE HOUR(DATETIME) = @HOUR) AS CNT
FROM (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16
UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20
UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23 UNION ALL SELECT 24) AS tmp;
- ์ฝ๋๊ฐ ์งง์์ ํ์คํ
UNION ALL
๋ณด๋ค ๊น๋ํ๋ค. information_schema.COLUMNS
๊ฐ์ ์์คํ ํ ์ด๋ธ์ ์ด์ฉํด์ 24๋ฒ๋ง ๋ฃจํ๋ฅผ ๋๋ฆฌ๋ ์๋ฆฌ์ด๋ค.- ๋ฐ์ดํฐ๋๊ณผ ๊ด๊ณ์์ด ํญ์ 24ํ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฅํ๋ค.
- ํ์ง๋ง ๋ณ์ ์ฌ์ฉ์ด ๋ฏ์ค์ด์ ๋๊ฐ์ ์ด๋ณด์๊ฐ ์ดํดํ๊ธฐ ์ด๋ ต๋ค.
- ๋๋ SQL์์ ์ด๋ ๊ฒ ๋ณ์๋ฅผ ์ ์ธํ๋ ๊ฑธ ์ค๋ต์ ๋ฆฌ ํ๋ฉด์ ์ฒ์ ๋ณด์๋ค.
1๏ธโฃ SET @HOUR := -1;
- ์ฌ๊ธฐ์
@HOUR
๋ ์ฌ์ฉ์ ๋ณ์(User-defined variable)์ด๋ค. - MySQL์์
@
๋ก ์์ํ๋ ๊ฑด ์ธ์ ๋จ์ ๋ณ์๋ผ์ ์ฟผ๋ฆฌ ์คํํ๋ ๋์๋ง ์ด์ ์๋ค๊ณ ํ๋ค. SET @HOUR := -1;
์@HOUR
๋ผ๋ ๋ณ์๋ฅผ-1
๋ก ์ด๊ธฐํํ๋ค๋ ๋ป์ด๋ค.- "์ด์ ๋ถํฐ
@HOUR
๋ผ๋ ์ด๋ฆ์ ๋ณ์๋ฅผ ๋ง๋ค๊ณ ๊ฐ์ -1๋ก ๋ฃ์ด๋๊ฒ" ๋ผ๊ณ ์ดํดํ๋ฉด ์ฝ๋ค.
2๏ธโฃ :=
์ฐ์ฐ์
MySQL์์๋ =๋ ๋์ ์ฐ์ฐ์๋ก ์ฐ์ด์ง๋ง, :=์ ์ฐ๋ ๊ฒ ๋ ๋ช ํํ๊ฒ โ๊ฐ์ ๋ณ์์ ๋์ ํ๋คโ๋ ์๋ฏธ๋ผ๊ณ ํ๋ค.
SET @num := 10;
โ ๋ณ์ @num์ 10์ ๋ฃ์ด๋ผ.
3๏ธโฃ @HOUR := @HOUR + 1 AS HOUR
- ์ด๊ฑด ์คํํ ๋๋ง๋ค
@HOUR
๊ฐ์ 1์ฉ ์ฆ๊ฐ์ํค๋ ํธ๋ฆญ์ด๋ค. - ์ฒ์์
@HOUR = -1
๋ก ์ธํ ํ์ผ๋๊น, ์ฒซ ๋ฒ์งธ ์คํ์์@HOUR := -1 + 1 = 0
โ ๊ฒฐ๊ณผ HOUR = 0 - ๋ ๋ฒ์งธ ์คํ์์
@HOUR := 0 + 1 = 1
โ ๊ฒฐ๊ณผ HOUR = 1 - โฆ ์ด๋ ๊ฒ ๊ณ์ ์ฌ๋ผ๊ฐ์ 23๊น์ง ์ถ๋ ฅ๋๋ค.
- ๊ทธ๋์
LIMIT 24
๋ฅผ ๋ถ์ฌ์ ๋ฑ 0~23๊น์ง๋ง ๋์ค๊ฒ ํ๋ ๊ฒ์ด๋ค.
3.3 WITH RECURSIVE ๋ฐฉ์ (MySQL 8 ์ด์)
WITH RECURSIVE hours AS (
SELECT 0 AS HOUR
UNION ALL
SELECT HOUR + 1 FROM hours WHERE HOUR < 23
)
SELECT h.HOUR,
COALESCE(COUNT(a.DATETIME), 0) AS COUNT
FROM hours h
LEFT JOIN ANIMAL_OUTS a
ON h.HOUR = HOUR(a.DATETIME)
GROUP BY h.HOUR
ORDER BY h.HOUR;
MySQL 8 ์ด์์์๋ง ์ง์ํ๋ ๋ฐฉ์์ธ๋ฐ, ๋ณ์๋ฅผ ์ ์ธํ์ง ์๊ณ ๋ ์ซ์๋ฅผ ์ฌ๊ท์ ์ผ๋ก ์๋ ์์ฑํ ์ ์์ด์ ๊ฐ์ฅ ๊น๋ํ๋ค. WITH RECURSIVE
๋ ์ฌ๊ท์ CTE(Common Table Expression)๋ฅผ ๋ง๋๋ ๊ตฌ๋ฌธ์ด๋ค. ์ฆ, ์์ ํ
์ด๋ธ(hours
)์ ๋ง๋ค๊ณ ๊ทธ ์์์ ์๊ธฐ ์์ ์ ๋ค์ ์ฐธ์กฐํ๋ฉด์ ๋ฐ๋ณต์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๋ค. MySQL ๋ฒ์ ์ด ๋ฎ์ง ์์ผ๋ฉด ๊ฐ์ฅ ์ถ์ฒ๋๋ ๊น๋ํ ๋ฐฉ์์ด๋ผ๊ณ ํ๋ค.
์๋ฐ for
๋ฌธ์ ๋ ์ฌ๋ฆฌ๋ฉด ์ดํดํ๊ธฐ ์ฝ๋ค. โ0๋ถํฐ ์์ํด์ 1์ฉ ์ฆ๊ฐ์ํค๋ฉฐ 23๊น์ง ์ซ์ ๋ชฉ๋ก์ ๋ง๋ค์ด๋ผโ ๋ผ๋ ๋ก์ง์ SQL๋ก ์ฎ๊ฒจ๋์ ๊ฒ๊ณผ ๊ฐ๋ค.
1๏ธโฃ WITH RECURSIVE hours AS (...)
WITH RECURSIVE
๋ ์ฌ๊ท์ CTE(Common Table Expression)๋ฅผ ๋ง๋ ๋ค๋ ๋ป์ด๋ค.- ์ฆ, ์์ ํ
์ด๋ธ(
hours
)์ ๋ง๋ค๊ณ , ๊ทธ ์์์ ์๊ธฐ ์์ ์ ๋ค์ ์ฐธ์กฐํด์ ๋ฐ๋ณต์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์์ฑํ ์ ์์ด๋ค.
2๏ธโฃ SELECT 0 AS HOUR
- ์ฌ๊ท์ ์์(Anchor part)์ด๋ค.
- ์ฌ๊ธฐ์
0
์ด๋ผ๋ ๊ฐ์ ํ๋ ๋ง๋ค๊ณ , ๊ทธ ์ด ์ด๋ฆ์HOUR
๋ผ๊ณ ๋ถ์ธ๋ค. - ๊ฒฐ๊ณผ:
HOUR |
---|
0 |
3๏ธโฃ UNION ALL
- ์ฒซ ๋ฒ์งธ ๊ฒฐ๊ณผ(0)์ ๋ค์ ๋์ค๋ ์ฌ๊ท ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ํฉ์น๋ค.
UNION
์ ์ค๋ณต ์ ๊ฑฐ๋ฅผ ํ์ง๋ง,UNION ALL
์ ์ค๋ณต๋ ๋ค ํฌํจ์ํจ๋ค.- ์ฌ๊ธฐ์๋ ์ค๋ณต ๊ฑฑ์ ์์ผ๋๊น
UNION ALL
์ ์ฐ๋ ๊ฒ ๋น ๋ฅด๊ณ ์์ ํ๋ค.
4๏ธโฃ SELECT HOUR + 1 FROM hours WHERE HOUR < 23
- ์ฌ๊ท ๋ถ๋ถ(Recursive part)์ด๋ค.
- ๋ฐฉ๊ธ ๋ง๋
hours
์์ ํ ์ด๋ธ์์HOUR
๊ฐ์ ํ๋์ฉ ๊บผ๋ด์+1
์ ํด์ค - ๊ทธ๋ฆฌ๊ณ
WHERE HOUR < 23
์กฐ๊ฑด์ผ๋ก 23๊น์ง๋ง ์์ฑ๋๊ฒ ์ ํํ๋ค.
๋์ ํ๋ฆ์ ๋ณด์:
- ์ฒ์์๋
0
์ด ๋์ด (Anchor part) hours
ํ ์ด๋ธ์0
์ด ์์ผ๋ โ0+1=1
์์ฑhours
ํ ์ด๋ธ์1
์ด ์์ผ๋ โ1+1=2
์์ฑ- โฆ ๋ฐ๋ณต โฆ
23
๊น์ง ์ค๋ฉด23 < 23
์กฐ๊ฑด์ด false โ ๋ฉ์ถค
โ ์ฌ๊ธฐ์ <24๊ฐ ์๋๋ผ <23๊น์ง ํด์ผ 23์๊น์ง ์ถ๋ ฅ๋๋ค๋ ๊ฒ์ด ์ฝ๊ฐ ํท๊ฐ๋ฆฐ๋ค.- ์ด ๊ณผ์ ์ ๋ค ๊ฑฐ์น๋ฉด
hours
๋ผ๋ CTE์๋ 0๋ถํฐ 23๊น์ง ์ซ์๊ฐ ์๊ธด๋ค.