๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ“š Computer Science/Database

SQL ๋™์ž‘ ์›๋ฆฌ์™€ ์ตœ์ ํ™”

by hyebin (Helia) 2025. 12. 12.
๋ฐ˜์‘ํ˜•

1. JOIN / GROUP BY / HAVING

1-1. JOIN

ํ˜„์‹ค ์„ธ๊ณ„์˜ ๋ฐ์ดํ„ฐ๋Š” ๋ณดํ†ต ํ…Œ์ด๋ธ” ํ•˜๋‚˜์— ๋‹ค ๋„ฃ์ง€ ์•Š๊ณ  ์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ”๋กœ ๋‚˜๋ˆ„์–ด ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด,

  • user ํ…Œ์ด๋ธ”: ์‚ฌ์šฉ์ž ์ •๋ณด (id, name, email…)
  • orders ํ…Œ์ด๋ธ”: ์ฃผ๋ฌธ ์ •๋ณด (id, user_id, total_price…)

์ด๋ ‡๊ฒŒ ๋‚˜๋ˆ  ๋†“์œผ๋ฉด, ์–ด๋–ค ์‚ฌ์šฉ์ž๊ฐ€ ์–ด๋–ค ์ฃผ๋ฌธ์„ ํ–ˆ๋Š”์ง€๋ฅผ ๋ณด๋ ค๋ฉด ๋‘ ํ…Œ์ด๋ธ”์„ ๋ฌถ์–ด์„œ ์กฐํšŒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด JOIN์ž…๋‹ˆ๋‹ค.

๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์€ INNER JOIN, LEFT JOIN์ž…๋‹ˆ๋‹ค.

 

1. INNER JOIN

๋‘ ํ…Œ์ด๋ธ”์—์„œ ์กฐ๊ฑด์— ์ผ์น˜ํ•˜๋Š” ํ–‰๋งŒ ๊ฐ€์ ธ์˜ค๋Š” ์กฐ์ธ์ž…๋‹ˆ๋‹ค.

SELECT u.id, u.name, o.id AS order_id, o.total_price
FROM users u
JOIN orders o ON u.id = o.user_id;
  • users์™€ orders์—์„œ u.id = o.user_id๊ฐ€ ๊ฐ™์€ ํ–‰๋“ค๋งŒ ๊ฒฐ๊ณผ์— ๋‚˜์˜ต๋‹ˆ๋‹ค.
  • ์ฃผ๋ฌธ์„ ํ•œ ์ ์ด ์—†๋Š” ์‚ฌ์šฉ์ž(orders์— ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” ์‚ฌ์šฉ์ž)๋Š” ๊ฒฐ๊ณผ์— ๋‚˜์˜ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

2. LEFT (OUTER) JOIN

์™ผ์ชฝ ํ…Œ์ด๋ธ”์„ ๊ธฐ์ค€์œผ๋กœ ์™ผ์ชฝ์€ ๋ชจ๋‘ ๊ฐ€์ ธ์˜ค๊ณ , ์˜ค๋ฅธ์ชฝ์€ ๋งค์นญ๋˜๋Š” ๊ฒƒ๋งŒ ๊ฐ€์ ธ์˜ค๋Š” ์กฐ์ธ์ž…๋‹ˆ๋‹ค.

SELECT u.id, u.name, o.id AS order_id, o.total_price
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
  • ์‚ฌ์šฉ์ž๋Š” ๋ชจ๋‘ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
  • ์ฃผ๋ฌธ์ด ์—†๋Š” ์‚ฌ์šฉ์ž๋Š” order_id, total_price๊ฐ€ NULL๋กœ ๋‚˜์˜ต๋‹ˆ๋‹ค.
  • ์ฃผ๋ฌธ์„ ํ–ˆ๋“  ์•ˆ ํ–ˆ๋“  ์ „์ฒด ์‚ฌ์šฉ์ž ๋ชฉ๋ก๊ณผ ์ฃผ๋ฌธ์ •๋ณด๋ฅผ ๋ณด๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

3. FULL (OUTER) JOIN

๋‘ ํ…Œ์ด๋ธ”์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , ๋งค์นญ๋˜์ง€ ์•Š๋Š” ๋ถ€๋ถ„์€ NULL๋กœ ์ฑ„์›Œ์„œ ํ•ฉ์น˜๋Š” ์กฐ์ธ์ž…๋‹ˆ๋‹ค.

SELECT *
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;
  • ์‚ฌ์šฉ์ž์™€ ์ฃผ๋ฌธ ์ •๋ณด๋ฅผ ๋ชจ๋‘ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
  • MySQL์€ FULL OUTER JOIN์„ ๊ณต์‹ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. (PostgreSQL, Oracle ๋“ฑ์—์„œ๋Š” ์ง€์›ํ•ฉ๋‹ˆ๋‹ค)
    • MySQL์—์„œ ๋น„์Šทํ•œ ํšจ๊ณผ๋ฅผ ๋‚ด๋ ค๋ฉด LEFT JOIN + UNION + RIGHT JOIN ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

1-2. GROUP BY

GROUP BY๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํŠน์ • ๊ธฐ์ค€์œผ๋กœ ๋ฌถ์–ด์„œ ์ง‘๊ณ„ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉ์ž๋ณ„ ์ฃผ๋ฌธ ์ด์•ก, ์ผ์ž๋ณ„ ๊ฐ€์ž…์ž ์ˆ˜, ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์ƒํ’ˆ ๊ฐœ์ˆ˜ ๊ฐ™์€ ๊ฒƒ์„ ๊ตฌํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id;
  • orders ํ…Œ์ด๋ธ”์—์„œ user_id๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ฌถ๊ณ ,
  • ๊ฐ ์‚ฌ์šฉ์ž๋ณ„๋กœ ์ฃผ๋ฌธ ๊ฑด์ˆ˜๋ฅผ ์„ธ์–ด์ค๋‹ˆ๋‹ค.
SELECT user_id, SUM(total_price) AS total_spent
FROM orders
GROUP BY user_id;
  • ์‚ฌ์šฉ์ž๋ณ„๋กœ ์–ผ๋งˆ๋‚˜ ๊ฒฐ์ œํ–ˆ๋Š”์ง€ ์ด์•ก์„ ๊ตฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ค‘์š”ํ•œ ์ ์€ SELECT ์ ˆ์— ์žˆ๋Š” ์นผ๋Ÿผ ์ค‘, ์ง‘๊ณ„ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹Œ ์นผ๋Ÿผ์€ ๋ชจ๋‘ GROUP BY์— ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ์ง‘๊ณ„ ํ•จ์ˆ˜: COUNT(), SUM(), AVG(), MAX(), MIN() ๋“ฑ
// ์ž˜๋ชป๋œ ์˜ˆ์‹œ - total_price๋Š” GROUP BY์—๋„ ์—†๊ณ , ์ง‘๊ณ„ ํ•จ์ˆ˜๋„ ์•„๋‹˜
SELECT user_id, total_price, SUM(total_price)
FROM orders
GROUP BY user_id;

// ์ •์ƒ ์˜ˆ์‹œ1 - ๋ชจ๋“  ๋น„์ง‘๊ณ„ ์ปฌ๋Ÿผ์„ GROUP BY์— ํฌํ•จ
SELECT user_id, total_price, SUM(total_price)
FROM orders
GROUP BY user_id, total_price;

// ์ •์ƒ ์˜ˆ์‹œ2 - total_price๋ฅผ ์ง‘๊ณ„ ํ•จ์ˆ˜๋กœ ๋ฌถ๊ธฐ
SELECT user_id, SUM(total_price) AS total_spent
FROM orders
GROUP BY user_id;

1-3. HAVING

HAVING์€ GROUP BY ํ›„์— ๋งŒ๋“ค์–ด์ง„ ๊ทธ๋ฃน์— ์กฐ๊ฑด์„ ๊ฑฐ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. WHERE๊ณผ ๋น„๊ตํ•˜์ž๋ฉด, WHERE๋Š” GROUP BY ์ „์— ๊ฐ ํ–‰(row)์— ์กฐ๊ฑด์„ ๊ฑฐ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉ์ž๋ณ„ ์ฃผ๋ฌธ ์ด์•ก์ด 10๋งŒ ์› ์ด์ƒ์ธ ์‚ฌ์šฉ์ž๋งŒ ๋ณด๊ณ  ์‹ถ๋‹ค๊ณ  ํ•œ๋‹ค๋ฉด

SELECT user_id, SUM(total_price) AS total_spent
FROM orders
GROUP BY user_id
HAVING SUM(total_price) >= 100000;
  • SUM(total_price)๋Š” ์ง‘๊ณ„ ๊ฒฐ๊ณผ์ด๋ฏ€๋กœ WHERE์ด ์•„๋‹Œ HAVING์—์„œ ํ•„ํ„ฐ๋งํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ •๋ฆฌํ•˜๋ฉด:

  • WHERE: ์ผ๋ฐ˜ ํ–‰ ํ•„ํ„ฐ๋ง
  • HAVING: ์ง‘๊ณ„ ๊ฒฐ๊ณผ ํ•„ํ„ฐ๋ง

SQL ์‹คํ–‰ ์ˆœ์„œ FROM → ON → JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY


2. ์ธ๋ฑ์Šค์™€ B-Tree ๋™์ž‘ ์›๋ฆฌ

2-1. ์ธ๋ฑ์Šค๋ž€?

์ธ๋ฑ์Šค๋Š” ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ผ์ผ์ด ํ›‘์ง€ ์•Š๊ณ ๋„, B-Tree ๊ฐ™์€ ์ •๋ ฌ๋œ ๊ตฌ์กฐ๋ฅผ ์ด์šฉํ•ด ์›ํ•˜๋Š” ๊ฐ’์„ ๋น ๋ฅด๊ฒŒ ์ฐพ๊ธฐ ์œ„ํ•œ ‘๊ฒ€์ƒ‰์šฉ ๋ชฉ์ฐจ’์ž…๋‹ˆ๋‹ค.

DB์—์„œ ์ธ๋ฑ์Šค๊ฐ€ ์—†์œผ๋ฉด ํ…Œ์ด๋ธ”์˜ ๋ชจ๋“  ํ–‰์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ๊ฒ€์‚ฌ(Full Scan) ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ธ๋ฑ์Šค๊ฐ€ ์žˆ์œผ๋ฉด, ์ธ๋ฑ์Šค๋ฅผ ๋ณด๊ณ  ์›ํ•˜๋Š” ๊ฐ’์ด ์žˆ๋Š” ์œ„์น˜๋ฅผ ๋น ๋ฅด๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ธ๋ฑ์Šค๋Š” ์ฃผ๋กœ WHERE ์กฐ๊ฑด, JOIN ์กฐ๊ฑด, ORDER BY ์นผ๋Ÿผ์— ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

// ๋‹จ์ผ ์ธ๋ฑ์Šค
CREATE INDEX idx_users_age ON users(age);

// ๋ณตํ•ฉ ์ธ๋ฑ์Šค
CREATE INDEX idx_orders_user_created
ON orders(user_id, created_at);

2-2. B-Tree ์ธ๋ฑ์Šค์˜ ๊ธฐ๋ณธ ๋™์ž‘

๊ด€๊ณ„ํ˜• DB์—์„œ ๊ฐ€์žฅ ํ”ํ•œ ์ธ๋ฑ์Šค ๊ตฌ์กฐ๋Š” B-Tree์ž…๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ๊ฐœ๋…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. ์ธ๋ฑ์Šค๋Š” ํŠน์ • ์นผ๋Ÿผ ๊ฐ’๋“ค์„ ์ •๋ ฌ๋œ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  2. ํŠธ๋ฆฌ์˜ ๋…ธ๋“œ์—๋Š” “ํ‚ค ๊ฐ’”๊ณผ “๋‹ค์Œ ๋…ธํŠธ ๋˜๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ ์œ„์น˜”๊ฐ€ ๋“ค์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์„ ๋•Œ๋Š” ํŠธ๋ฆฌ๋ฅผ ํƒ€๊ณ  ๋‚ด๋ ค๊ฐ€๋ฉฐ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. → ์ด์ง„ ํƒ์ƒ‰๊ณผ ๋น„์Šทํ•˜๊ฒŒ O(log N) ์‹œ๊ฐ„์— ์›ํ•˜๋Š” ๊ฐ’์„ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. ์ธ๋ฑ์Šค๊ฐ€ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š”(์•ˆ ๋จนํžˆ๋Š” ) ๊ฒฝ์šฐ

์ธ๋ฑ์Šค๋ฅผ ๋งŒ๋“ค์–ด๋„, ํ•ญ์ƒ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.

DB๊ฐ€ ์ธ๋ฑ์Šค๋ฅผ ์จ๋„ ์ด๋“์ด ์—†๋‹ค๊ณ  ํŒ๋‹จํ•˜๋Š” ๊ฒฝ์šฐ๋„ ์žˆ๊ณ , ๊ฐœ๋ฐœ์ž๊ฐ€ ์ž‘์„ฑํ•œ ์ฟผ๋ฆฌ ๊ตฌ์กฐ ๋•Œ๋ฌธ์— ์ธ๋ฑ์Šค๋ฅผ ๋ชป ์“ฐ๋Š” ๊ฒฝ์šฐ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

3-1. ํ•จ์ˆ˜/์—ฐ์‚ฐ์„ ์นผ๋Ÿผ์— ์ง์ ‘ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ

SELECT * FROM users
WHERE DATE(created_at) = '2025-01-01';
  • created_at์— ์ธ๋ฑ์Šค๊ฐ€ ์žˆ์–ด๋„ DATE(created_at)์ฒ˜๋Ÿผ ์นผ๋Ÿผ์— ํ•จ์ˆ˜๋ฅผ ์”Œ์šฐ๋ฉด ์ธ๋ฑ์Šค๋ฅผ ์ž˜ ํ™œ์šฉํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.
SELECT * FROM users
WHERE created_at >= '2025-01-01'
  AND created_at <  '2025-01-02';
  • ๊ฐ€๋Šฅํ•˜๋ฉด ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

3-2. LIKE ‘% ๊ฐ’’ ํ˜•ํƒœ (์•ž์— %๊ฐ€ ๋ถ™๋Š” ๊ฒฝ์šฐ)

SELECT * FROM users
WHERE name LIKE '%bin';
  • ๋ฌธ์ž์—ด ์•ž์— %๊ฐ€ ๋ถ™์–ด ์žˆ์œผ๋ฉด, ์ธ๋ฑ์Šค ์ž…์žฅ์—์„œ๋Š” ์•ž๋ถ€๋ถ„์ด ๋ญ”์ง€ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์ •๋ ฌ๋œ ์ธ๋ฑ์Šค๋ฅผ ์ œ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.
  • ๊ฒฐ๊ณผ์ ์œผ๋กœ Full Scan์ด ์ผ์–ด๋‚  ๊ฐ€๋Šฅ์„ฑ์ด ํฝ๋‹ˆ๋‹ค.
WHERE name LIKE 'hyebin%'
  • ๋ฐ˜๋ฉด ์•ž์ด ๊ณ ์ •๋˜์–ด ์žˆ๊ณ  ๋’ค์— %๊ฐ€ ๋ถ™๋Š” ๊ฒฝ์šฐ์—๋Š” ์ธ๋ฑ์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.

3-3. ๋ณตํ•ฉ ์ธ๋ฑ์Šค์—์„œ ์„ ๋‘ ์นผ๋Ÿผ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ

INDEX idx_user (user_id, created_at)

// ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ
WHERE user_id = 10
WHERE user_id = 10 AND created_at >= '2025-01-01'

//์‚ฌ์šฉ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ
WHERE created_at >= '2025-01-01'
  • user_id → created_at ์ˆœ์„œ๋กœ ์ •๋ ฌ๋˜์–ด ์žˆ๋Š” ๋ณตํ•ฉ ์ธ๋ฑ์Šค์—์„œ๋Š” ์™ผ์ชฝ(์„ ๋‘) ์นผ๋Ÿผ๋ถ€ํ„ฐ ์ˆœ์„œ๋Œ€๋กœ ์‚ฌ์šฉํ•  ๋•Œ ํšจ๊ณผ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

3-4. ์กฐ๊ฑด์— ํ•ด๋‹นํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์„ ๋•Œ (์„ ํƒ๋„๊ฐ€ ๋‚ฎ์„ ๋•Œ)

SELECT * FROM users
WHERE gender = 'F';
  • ์ „์ฒด์˜ 90%๊ฐ€ ‘F’๋ผ๋ฉด, ์ธ๋ฑ์Šค๋ฅผ ์‚ฌ์šฉํ•ด ์—ฌ๊ธฐ์ €๊ธฐ ์ ํ”„ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๊ทธ๋ƒฅ Full Scan์„ ํ•˜๋Š” ๊ฒŒ ๋” ๋น ๋ฅผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
  • DB๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ๋Œ€๋žต ๋ช‡ ๊ฑด์ด ๋‚˜์˜ฌ์ง€ ํ†ต๊ณ„๋ฅผ ๋ณด๊ณ  ์ธ๋ฑ์Šค๋ฅผ ์“ธ์ง€, Full Scan์„ ํ• ์ง€ ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค.

3-5. ํƒ€์ž…์ด ๋งž์ง€ ์•Š๋Š” ๋น„๊ต, ์•”์‹œ์  ํ˜•๋ณ€ํ™˜

SELECT * FROM users
WHERE id = '100';  -- id๋Š” INT์ธ๋ฐ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ์‚ฌ์šฉ
  • DB์— ๋”ฐ๋ผ ๋‹ค๋ฅด์ง€๋งŒ, ํƒ€์ž…์ด ๋งž์ง€ ์•Š์•„ ๋‚ด๋ถ€์—์„œ ํ˜•๋ณ€ํ™˜์ด ๋ฐœ์ƒํ•˜๋ฉด ์ธ๋ฑ์Šค๋ฅผ ์ œ๋Œ€๋กœ ํ™œ์šฉํ•˜์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐ€๋Šฅํ•˜๋ฉด ํƒ€์ž…์„ ์ •ํ™•ํžˆ ๋งž์ถฐ์„œ ๋น„๊ตํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

4. ์‹คํ–‰ ๊ณ„ํš(EXPLAIN) ๊ธฐ๋ณธ ์ฝ๋Š” ๋ฒ•

์‹ค๋ฌด์—์„œ๋Š” ์ฟผ๋ฆฌ๊ฐ€ ๋А๋ฆฐ๋ฐ ์™œ ๋А๋ฆฐ์ง€๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์‹คํ–‰ ๊ณ„ํš์„ ์ž์ฃผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

EXPLAIN
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id;

DB๋งˆ๋‹ค ์ถœ๋ ฅํ˜•์‹์€ ์กฐ๊ธˆ์”ฉ ๋‹ค๋ฅด์ง€๋งŒ, ์ค‘์š”ํ•˜๊ฒŒ ๋ณด๋“  ์นผ๋Ÿผ์€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ ๊ธ€์—์„œ๋Š” MySQL ๊ธฐ์ค€์œผ๋กœ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

4-1. ์ž์ฃผ ๋ณด๋Š” ์นผ๋Ÿผ

  • type
    • ์ ‘๊ทผ ๋ฐฉ์‹
    • ALL ์ด๋ฉด Full Table Scan (๊ฐ€์žฅ ์•ˆ ์ข‹์Œ)
    • range, ref, eq_ref, const ๋“ฑ์œผ๋กœ ๊ฐˆ์ˆ˜๋ก ๋” ์ข‹์€ ๋ฐฉ์‹
      • range: ์ธ๋ฑ์Šค๋ฅผ ์ด์šฉํ•ด ํŠน์ • ๋ฒ”์œ„๋งŒ ์กฐํšŒ (BETWEEN, IN, >, < ….)
      • ref: ์ธ๋ฑ์Šค๋กœ ํŠน์ • ๊ฐ’์„ ๊ฐ€์ง„ row ์กฐํšŒ (=)
      • eq_ref: ์กฐ์ธ์—์„œ ํƒ ํ•œ ๊ฐœ์˜ row๋ฅผ ์ฐพ์„ ๋•Œ
      • const: ๋”ฑ 1๊ฐœ์˜ row๋งŒ ์ฝ์œผ๋ฉด ๋˜๋Š” ์ƒํ™ฉ์ด๋ผ ์ƒ์ˆ˜์ฒ˜๋Ÿผ ์ทจ๊ธ‰ํ•  ์ˆ˜ ์žˆ์„ ๋•Œ
  • key
    • ์‹ค์ œ๋กœ ์‚ฌ์šฉ๋œ ์ธ๋ฑ์Šค ์ด๋ฆ„
    • NULL์ด๋ฉด ์ธ๋ฑ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ
  • rows
    • ์ด ์ฟผ๋ฆฌ๋ฅผ ์œ„ํ•ด ๋Œ€๋žต ๋ช‡ ๊ฐœ์˜ ํ–‰์„ ์ฝ์–ด์•ผ ํ•˜๋Š”์ง€ ์ถ”์ •ํ•˜๋Š” ๊ฐ’
    • ์ˆซ์ž๊ฐ€ ์ž‘์„์ˆ˜๋ก ์„ฑ๋Šฅ์— ์œ ๊ธฐ
  • Extra
    • Using index: ์ธ๋ฑ์Šค๋งŒ ๋ณด๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ํ•ด๊ฒฐํ–ˆ๋‹ค๋Š” ์˜๋ฏธ (์ปค๋ฒ„๋ง ์ธ๋ฑ์Šค)
    • Using where: WHERE ์กฐ๊ฑด์œผ๋กœ ํ•„ํ„ฐ๋งํ•จ
    • Using temporary, Using filesort: GROUP BY / ORDER BY ๋•Œ๋ฌธ์— ์ž„์‹œ ํ…Œ์ด๋ธ”/์ •๋ ฌ์ด ๋ฐœ์ƒํ–ˆ์Œ์„ ์˜๋ฏธ (์ฃผ์˜ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค)

4-2. ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋ฉด ์ข‹์„๊นŒ์š”?

  • type = ALL, rows๊ฐ€ ์•„์ฃผ ํฌ๋‹ค → ์ธ๋ฑ์Šค ๊ณ ๋ ค
  • key = NULL → ์ธ๋ฑ์Šค๊ฐ€ ์—†๊ฑฐ๋‚˜, ์žˆ์–ด๋„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ
  • Using temporary, Using filesort → GROUP BY / ORDER BY ์ตœ์ ํ™” ํ•„์š” ๊ฐ€๋Šฅ์„ฑ

์ฆ‰, EXPLAIN์€ ์ด ์ฟผ๋ฆฌ๋ฅผ DB๊ฐ€ ์–ด๋–ค ๊ฒฝ๋กœ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ๋Š”์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์ง€๋„์ž…๋‹ˆ๋‹ค.

์ด๋ฅผ ๋ณด๊ณ , ์ธ๋ฑ์Šค ์ถ”๊ฐ€๋‚˜ ์ฟผ๋ฆฌ ๊ตฌ์กฐ ๋ณ€๊ฒฝ์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.


5. ์ •๊ทœํ™”์™€ ๋ฐ˜์ •๊ทœํ™”

5-1. ์ •๊ทœํ™”๋ž€?

์ •๊ทœํ™”๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ค‘๋ณต ์—†์ด, ์ด์ƒ ํ˜„์ƒ์ด ์—†๋„๋ก ์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ”๋กœ ์ž˜๊ฒŒ ๋‚˜๋ˆ„์–ด ์„ค๊ณ„ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ํ•˜๋‚˜์˜ ํ…Œ์ด๋ธ”์— ํšŒ์›์ •๋ณด + ์ฃผ๋ฌธ์ •๋ณด + ์ƒํ’ˆ์ •๋ณด๊ฐ€ ๋‹ค ๋“ค์–ด์žˆ๋‹ค๋ฉด, ํšŒ์› ์ด๋ฆ„์„ ๋ฐ”๊พธ๋ฉด ๊ทธ ํšŒ์›์˜ ๋ชจ๋“  ์ฃผ๋ฌธ ํ–‰์„ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๊ตฌ์กฐ๋Š” ์ค‘๋ณต๋„ ๋งŽ๊ณ , ์ˆ˜์ • ์‹œ ์‹ค์ˆ˜์™€ ๋ถˆ์ผ์น˜๊ฐ€ ์ƒ๊ธฐ๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ์ด์ƒ(Anomaly)๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ํšŒ์›์€ members ํ…Œ์ด๋ธ”, ์ฃผ๋ฌธ์€ orders ํ…Œ์ด๋ธ”, ์ƒํ’ˆ์€ products ํ…Œ์ด๋ธ”์ฒ˜๋Ÿผ ๋‚˜๋ˆ„๊ณ , ๊ด€๊ณ„(์™ธ๋ž˜ํ‚ค)๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ์„ค๊ณ„๋ฅผ ํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ณผ์ •์„ ๋‹จ๊ณ„์ ์œผ๋กœ ์ด๋ก ํ™”ํ•ด ๋†“์„ ๊ฒƒ์ด 1์ •๊ทœํ˜•, 2์ •๊ทœํ˜•, 3์ •๊ทœํ˜• ๋“ฑ์ž…๋‹ˆ๋‹ค.

5-2. ์ •๊ทœํ™”์˜ ์žฅ์ ๊ณผ ๋‹จ์ 

์žฅ์ 

  • ๋ฐ์ดํ„ฐ ์ค‘๋ณต ๊ฐ์†Œ
  • ํ•œ ๊ณณ๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋˜๋ฏ€๋กœ ์ผ๊ด€์„ฑ ์œ ์ง€ ๊ฐ€๋Šฅ
  • ์ด์ƒ ํ˜•์ƒ(์‚ฝ์ž…/๊ฐฑ์‹ /์‚ญ์ œ ์ด์ƒ) ๊ฐ์†Œ

๋‹จ์ 

  • ๋„ˆ๋ฌด ์ž˜๊ฒŒ ์ชผ๊ฐœ๋ฉด, ๋ฐ์ดํ„ฐ ์กฐํšŒํ•  ๋•Œ ์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ”์„ JOIN ํ•ด์•ผ ํ•จ
    • JOIN์ด ๋งŽ์„์ˆ˜๋ก ์ฟผ๋ฆฌ๊ฐ€ ๋ณต์žกํ•ด์ง€๊ณ , ์„ฑ๋Šฅ ๊ฐ์†Œ

⇒ ์™„๋ฒฝํ•œ ์ •๊ทœํ™”๋ณด๋‹ค๋Š” ์ ๋‹นํ•œ ์ •๊ทœํ™” + ํ•„์š”์‹œ ๋ฐ˜์ •๊ทœํ™”๋ฅผ ์„ ํƒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

5-3. ๋ฐ˜์ •๊ทœํ™”๋ž€?

๋ฐ˜์ •๊ทœํ™”๋Š” ์กฐํšŒ ์„ฑ๋Šฅ์„ ์˜ฌ๋ฆฌ๊ธฐ ์œ„ํ•ด ์˜๋„์ ์œผ๋กœ ์ค‘๋ณต์„ ํ—ˆ์šฉํ•˜๋ฉด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•ฉ์น˜๋Š” ์ž‘์—…์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ฃผ๋ฌธ ๋‚ด์—ญ์„ ์กฐํšŒํ•  ๋•Œ๋งˆ๋‹ค orders + members + products๋ฅผ ํ•ญ์ƒ JOIN ํ•ด์•ผ ํ•ด ์กฐํšŒ๊ฐ€ ๋„ˆ๋ฌด ์žฆ๊ณ , ์„ฑ๋Šฅ์ด ๋ฌธ์ œ๊ฐ€ ๋œ๋‹ค๋ฉด ์ฃผ๋ฌธ ํ…Œ์ด๋ธ”์— “ํšŒ์› ์ด๋ฆ„, ์ƒํ’ˆ ์ด๋ฆ„, ๊ฐ€๊ฒฉ”์„ ๋„ฃ์–ด๋‘˜ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด, ํ•œ ํ…Œ์ด๋ธ”๋งŒ ์กฐํšŒํ•ด๋„ ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ํ•œ ๋ฒˆ์— ๋ณผ ์ˆ˜ ์žˆ์ง€๋งŒ ์ด๋ฆ„์ด ๋ฐ”๋€Œ๊ฑฐ๋‚˜ ๊ฐ€๊ฒฉ์ด ๋ฐ”๋€” ๋•Œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ผ๊ด€๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (๋Œ€์‹ , “์ฃผ๋ฌธ ์‹œ์ ์˜ ์Šค๋ƒ…์ƒท”์ด๋ผ๊ณ  ๋ณด๊ณ  ํ—ˆ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.)

๋ฐ˜์‘ํ˜•