๐Ÿง  ์ž…์–‘ ์‹œ๊ฐ ๊ตฌํ•˜๊ธฐ(2)

๋ฌธ์ œ

ANIMAL_OUTS ํ…Œ์ด๋ธ”์€ ๋™๋ฌผ ๋ณดํ˜ธ์†Œ์—์„œ ์ž…์–‘ ๋ณด๋‚ธ ๋™๋ฌผ์˜ ์ •๋ณด๋ฅผ ๋‹ด์€ ํ…Œ์ด๋ธ”์ž…๋‹ˆ๋‹ค. ANIMAL_OUTS ํ…Œ์ด๋ธ” ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์œผ๋ฉฐ, ANIMAL_ID, ANIMAL_TYPE, DATETIME, NAME, SEX_UPON_OUTCOME๋Š” ๊ฐ๊ฐ ๋™๋ฌผ์˜ ์•„์ด๋””, ์ƒ๋ฌผ ์ข…, ์ž…์–‘์ผ, ์ด๋ฆ„, ์„ฑ๋ณ„ ๋ฐ ์ค‘์„ฑํ™” ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

NAME TYPE NULLABLE
ANIMAL_ID VARCHAR(N) FALSE
ANIMAL_TYPE VARCHAR(N) FALSE
DATETIME DATETIME FALSE
NAME VARCHAR(N) TRUE
SEXUPONOUTCOME VARCHAR(N) FALSE

๋ณดํ˜ธ์†Œ์—์„œ๋Š” ๋ช‡ ์‹œ์— ์ž…์–‘์ด ๊ฐ€์žฅ ํ™œ๋ฐœํ•˜๊ฒŒ ์ผ์–ด๋‚˜๋Š”์ง€ ์•Œ์•„๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค. 0์‹œ๋ถ€ํ„ฐ 23์‹œ๊นŒ์ง€, ๊ฐ ์‹œ๊ฐ„๋Œ€๋ณ„๋กœ ์ž…์–‘์ด ๋ช‡ ๊ฑด์ด๋‚˜ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ์กฐํšŒํ•˜๋Š” SQL๋ฌธ์„ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. ์ด๋•Œ ๊ฒฐ๊ณผ๋Š” ์‹œ๊ฐ„๋Œ€ ์ˆœ์œผ๋กœ ์ •๋ ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ’€์ด

WITH RECURSIVE hour AS (
	SELECT 0 AS h
	UNION ALL
	SELECT h+1 FROM hour WHERE h<23
)

SELECT hour.h AS hour, COUNT(hour(a.DATETIME)) AS count FROM hour
LEFT JOIN ANIMAL_OUTS AS a ON hour.h = hour(a.DATETIME)
GROUP BY hour
ORDER BY hour;

๋„์ €ํžˆ {0์‹œ, 1์‹œ, โ€ฆ 23์‹œ}๋ฅผ ์ƒˆ๋กœ์šด ์ปฌ๋Ÿผ์œผ๋กœ ๋นผ๋‚ด๋Š” ๋ฐฉ๋ฒ•์ด ๋– ์˜ค๋ฅด์ง€ ์•Š์•˜๋‹ค. ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค SQL ๋ฌธ์ œ๋ฅผ ํ’€๋‹ค๊ฐ€ ๊ตฌ๊ธ€๋ง์„ ํ•œ ๊ฑด ์ฒ˜์Œ์ด์—ˆ๋‹ค. WITH RECURSIVE ๊ตฌ๋ฌธ ์ž์ฒด๊ฐ€ ์ดˆ๋ฉด์ด์—ˆ๊ธฐ์— ๋ธ”๋กœ๊ทธ์˜ ํ’€์ด๋ฅผ ๋ณด๊ณ ๋„ ํ•œ์ฐธ ์ƒ๊ฐํ•ด์•ผ ํ–ˆ๋‹ค. ํ’€์ด๋ฅผ ํ†ตํ•ด ์–ต์ง€๋กœ ์ดํ•ด๋ฅผ ํ•ด๋‚ด๊ธด ํ–ˆ์ง€๋งŒ ๋” ์ •ํ™•ํžˆ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด MySQL ๊ณต์‹๋ฌธ์„œ๋ฅผ ํ™•์ธํ–ˆ๋‹ค.

WITH(Common Table Expressions)

๊ณตํ†ต ํ…Œ์ด๋ธ” ํ‘œํ˜„์‹์ด๋ผ๊ณ ๋„ ๋ถˆ๋ฆฌ๋Š” WITH ๊ตฌ๋ฌธ์€ ๋‹ค๋ฅธ ๋ช…๋ น๋ฌธ์—์„œ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋Š” ์ž„์‹œ ๊ฒฐ๊ณผ ์ง‘ํ•ฉ์ด๋‹ค. ๋‹ค๋ฅธ ๋ช…๋ น๋ฌธ์—์„œ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋“ฏ์ด ๋‹ค๋ฅธ WITH ๋ฌธ์—์„œ๋„ ๊ฒฐ๊ณผ ์ง‘ํ•ฉ์„ ์ฐธ์กฐํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

WITH RECURSIVE

์žฌ๊ท€ CTE๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” WITH RECURSIVE ๊ตฌ๋ฌธ์€ ๋ฐ˜๋ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” ํ–‰ ์ง‘ํ•ฉ์ด๋‹ค. ๋น„์žฌ๊ท€ CTE์™€ ์ฐจ์ด์ ์œผ๋กœ๋Š” ๋‹ค์Œ ๋‘๋ฒˆ์งธ SELECT์—์„œ cte_name์„ ์ฐธ์กฐํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๊ฒ ๋‹ค.

WITH RECURSIVE cte_name AS
(
	  SELECT ...      <-- specifies initial set
	  UNION ALL
	  SELECT ...      <-- specifies how to derive new rows
)

์ฒซ๋ฒˆ์งธ SELECT์—์„œ๋Š” ๋ฐ˜๋ณตํ•  ํ–‰์„ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ๋‘๋ฒˆ์งธ SELECT์—์„œ๋Š” ํ–‰์„ ์–ด๋–ป๊ฒŒ ํ™•์žฅํ•  ์ง€ ์ž‘์„ฑํ•œ๋‹ค. ๋”์ด์ƒ ํ–‰์„ ์ƒ์„ฑํ•˜์ง€ ์•Š์„๋•Œ๊นŒ์ง€ ๋ฐ˜๋ณตํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ WHERE์ ˆ์„ ์ž‘์„ฑํ•˜์—ฌ ๋ฃจํ”„ ์ข…๋ฃŒ๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ’€์ด์—์„œ๋Š” 0์œผ๋กœ ํ–‰์„ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  0+1, 1+1, 2+1 โ€ฆ ์ฒ˜๋Ÿผ h๋ฅผ 1์”ฉ ์ฆ๊ฐ€ํ•˜๋ฉฐ ํ–‰์„ ํ™•์žฅํ•œ๋‹ค. h๊ฐ€ 23์ด ๋˜๋ฉด ๋ฃจํ”„๊ฐ€ ์ข…๋ฃŒ๋˜๊ณ  UNION ALL์„ ํ†ตํ•ด {0, 1, โ€ฆ 23}์ด ํ•ฉ์ง‘ํ•ฉ์˜ ๊ฒฐ๊ณผ๋กœ ๋–จ์–ด์ง€๊ฒŒ ๋œ๋‹ค.

WITH ์ž„์‹œ ํ…Œ์ด๋ธ”์€ ์–ด๋””์— ์ €์žฅ๋ ๊นŒ?

Real MySQL 8.0 ์ฑ…์„ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ์ž„์‹œํ…Œ์ด๋ธ”์˜ ๊ฒฝ์šฐ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•ด๋‘์—ˆ๋‹ค๊ฐ€ ์šฉ๋Ÿ‰์ด ์ปค์ง€๋ฉด ๋””์Šคํฌ๋กœ ์˜ฎ๊ธฐ๋Š” ๊ณผ์ •์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค. ๊ทธ๋Ÿผ WITH ๊ตฌ๋ฌธ์˜ ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•  ๋•Œ๋„ ๋น„์Šทํ•˜๊ฒŒ ๋™์ž‘ํ• ๊นŒ? ์ฐพ์•„๋ณธ ๊ฒฐ๊ณผ ๋™์ผํ–ˆ๋‹ค.

CTE actual cost may also be affected by result set size. A CTE that produces many rows may require an internal temporary table large enough to be converted from in-memory to on-disk format and may suffer a performance penalty.

WITH vs Subquery

์„ฑ๋Šฅ์ ์ธ ๋ฉด์—์„œ๋„ ํฐ ์ฐจ์ด๊ฐ€ ์—†๋Š” WITH ๊ตฌ๋ฌธ๊ณผ Subquery์€ ๊ฐ๊ฐ ์–ด๋–จ ๋•Œ ์“ฐ๋Š” ๊ฒƒ์ด ์ข‹์„๊นŒ?

CTE๋Š” ์žฌ๊ท€์ ์ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ๊ฐ€์žฅ ํฐ ํŠน์ง•์ด๋‹ค. ๋˜ํ•œ WITH ๊ตฌ๋ฌธ์€ ๋‹จ์ผ ๋ช…๋ น์–ด๋กœ ์žฌ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— Subquery๋ณด๋‹ค ์‚ฌ์šฉ์„ฑ์ด ์ข‹๋‹ค. Subquery์˜ ๊ฒฝ์šฐ WHERE์ ˆ์—์„œ ๋ฐ”๋กœ ํ•„ํ„ฐ๋ง์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์ , ํ–‰์ด ์•„๋‹Œ ์—ด์ฒ˜๋Ÿผ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ํŠน์ง•์ด๋‹ค.

๊ฐ ๊ตฌ๋ฌธ์˜ ํŠน์ง•์„ ์ˆ™์ง€ํ•˜๊ณ  ๋ฌธ์ œ ํ•ด๊ฒฐ์˜ ํฌ์ธํŠธ์™€ ์—ฐ๊ฒฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๊ตฌ๋ฌธ์„ ์„ ํƒํ•˜๋„๋ก ํ•˜์ž.

References


Written by@ugaemi
Record things I want to remember

๐Ÿฑ GitHub๐Ÿ“š Reading Space