



๐ก ์ค์ ์ฝ๋ฉํ ์คํธ์์ ๋ฐ๋ณต ์ถ์ ๋๋ ํจํด์ ์ ๋ฆฌํ๋ค.
์ฝ๋ฉํ
์คํธ์์ ์์ฃผ ๋์ค๋ ํจํด์ ์ตํ๋ฉด,
์๋ก์ด ๋ฌธ์ ๋ "์, ์ด๊ฑด ์ ํจํด์ด๋ค!" ํ๊ณ ํ ์ ์๋ค.
[users]
| user_id | name | created_at |
|---------|------|------------|
| 1 | ๊น์ฒ ์ | 2024-01-01 |
| 2 | ์ด์ํฌ | 2024-01-15 |
| 3 | ๋ฐ๋ฏผ์ | 2024-02-01 |
| 4 | ์ต์ง์ | 2024-02-15 |
[orders]
| order_id | user_id | amount | order_date |
|----------|---------|--------|------------|
| 101 | 1 | 150000 | 2024-01-10 |
| 102 | 1 | 50000 | 2024-01-20 |
| 103 | 2 | 200000 | 2024-02-05 |
| 104 | 2 | 80000 | 2024-02-10 |
| 105 | 1 | 100000 | 2024-02-15 |
| 106 | 3 | 300000 | 2024-02-20 |
"๊ฐ ์ ์ ๋ณ ๊ฐ์ฅ ํฐ ์ฃผ๋ฌธ", "๋ถ์๋ณ ์ฐ๋ด 1์", "์นดํ ๊ณ ๋ฆฌ๋ณ ํ๋งค ์์ 3๊ฐ"
WITH ranked AS (
SELECT
*,
ROW_NUMBER() OVER(
PARTITION BY ๊ทธ๋ฃน์ปฌ๋ผ
ORDER BY ์ ๋ ฌ์ปฌ๋ผ DESC
) AS rn
FROM ํ
์ด๋ธ
)
SELECT * FROM ranked WHERE rn <= N;
WITH ranked AS (
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY amount DESC) AS rn
FROM orders
)
SELECT order_id, user_id, amount
FROM ranked
WHERE rn = 1;
| order_id | user_id | amount |
|---|---|---|
| 101 | 1 | 150000 |
| 103 | 2 | 200000 |
| 106 | 3 | 300000 |
"์ฃผ๋ฌธ ์๋ ์ ์ ", "ํ๋งค ์ ๋ ์ํ", "์์ฝ ์๋ ๋ ์ง"
SELECT a.*
FROM ๊ธฐ์คํ
์ด๋ธ a
LEFT JOIN ์กฐ๊ฑดํ
์ด๋ธ b ON a.key = b.key
WHERE b.key IS NULL;
SELECT *
FROM ๊ธฐ์คํ
์ด๋ธ a
WHERE NOT EXISTS (
SELECT 1 FROM ์กฐ๊ฑดํ
์ด๋ธ b
WHERE b.key = a.key
);
-- ๋ฐฉ๋ฒ 1: LEFT JOIN
SELECT u.*
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE o.order_id IS NULL;
-- ๋ฐฉ๋ฒ 2: NOT EXISTS
SELECT *
FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.user_id
);
"3์ผ ์ฐ์ ๋ก๊ทธ์ธํ ์ ์ ", "์ฐ์ ๋งค์ถ ์์น ๊ธฐ๊ฐ"
WITH numbered AS (
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY date) AS rn,
DATE_SUB(date, INTERVAL ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY date) DAY) AS grp
FROM ํ
์ด๋ธ
)
SELECT user_id, MIN(date), MAX(date), COUNT(*) AS consecutive_days
FROM numbered
GROUP BY user_id, grp
HAVING COUNT(*) >= N;
[logins]
| user_id | login_date |
|---------|------------|
| 1 | 2024-01-01 |
| 1 | 2024-01-02 |
| 1 | 2024-01-03 |
| 1 | 2024-01-05 |
| 2 | 2024-01-01 |
WITH numbered AS (
SELECT
user_id,
login_date,
DATE_SUB(login_date, INTERVAL ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY login_date) DAY) AS grp
FROM logins
)
SELECT
user_id,
MIN(login_date) AS start_date,
MAX(login_date) AS end_date,
COUNT(*) AS consecutive_days
FROM numbered
GROUP BY user_id, grp
HAVING COUNT(*) >= 3;
ํต์ฌ ์์ด๋์ด:
"์ ์ผ ๋๋น ์ฆ๊ฐํ ๋ ", "์ด์ ์ฃผ๋ฌธ๋ณด๋ค ๊ธ์ก์ด ํฐ ์ฃผ๋ฌธ"
WITH with_prev AS (
SELECT
*,
LAG(๋น๊ต์ปฌ๋ผ) OVER(ORDER BY ์ ๋ ฌ์ปฌ๋ผ) AS prev_value
FROM ํ
์ด๋ธ
)
SELECT *
FROM with_prev
WHERE ๋น๊ต์ปฌ๋ผ > prev_value;
WITH daily_sales AS (
SELECT
order_date,
SUM(amount) AS daily_amount
FROM orders
GROUP BY order_date
),
with_prev AS (
SELECT
*,
LAG(daily_amount) OVER(ORDER BY order_date) AS prev_amount
FROM daily_sales
)
SELECT *
FROM with_prev
WHERE daily_amount > prev_amount;
"๋์ ๋งค์ถ", "์์ N%์ ํด๋นํ๋ ๋ฐ์ดํฐ"
SELECT
*,
SUM(amount) OVER(ORDER BY date) AS cumulative_sum
FROM ํ
์ด๋ธ;
SELECT
*,
SUM(amount) OVER(ORDER BY amount DESC) * 100.0 / SUM(amount) OVER() AS cumulative_pct
FROM ํ
์ด๋ธ;
WITH product_sales AS (
SELECT
product,
SUM(amount) AS total_amount
FROM orders
GROUP BY product
),
with_cumulative AS (
SELECT
*,
SUM(total_amount) OVER(ORDER BY total_amount DESC) * 100.0
/ SUM(total_amount) OVER() AS cumulative_pct
FROM product_sales
)
SELECT *
FROM with_cumulative
WHERE cumulative_pct <= 80;
"์๋ณ ๋งค์ถ", "์ฃผ์ฐจ๋ณ ๊ฐ์ ์", "์๊ฐ๋๋ณ ์ฃผ๋ฌธ"
SELECT
DATE_FORMAT(date_column, 'ํฌ๋งท') AS period,
์ง๊ณํจ์()
FROM ํ
์ด๋ธ
GROUP BY DATE_FORMAT(date_column, 'ํฌ๋งท');
| ํฌ๋งท | ์๋ฏธ | ์์ |
|---|---|---|
| %Y | ์ฐ๋ 4์๋ฆฌ | 2024 |
| %m | ์ 2์๋ฆฌ | 01, 12 |
| %d | ์ผ 2์๋ฆฌ | 01, 31 |
| %Y-%m | ์ฐ-์ | 2024-01 |
| %Y-%U | ์ฐ-์ฃผ์ฐจ | 2024-01 |
SELECT
DATE_FORMAT(order_date, '%Y-%m') AS month,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM orders
GROUP BY DATE_FORMAT(order_date, '%Y-%m')
ORDER BY month;
"์ค๋ณต ์ ๊ฑฐ", "์ค๋ณต ๋ฐ์ดํฐ ์ค ์ต์ ๊ฒ๋ง"
SELECT column, COUNT(*)
FROM ํ
์ด๋ธ
GROUP BY column
HAVING COUNT(*) > 1;
WITH ranked AS (
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY ์ค๋ณต๊ธฐ์ค ORDER BY ์ฐ์ ์์) AS rn
FROM ํ
์ด๋ธ
)
SELECT * FROM ranked WHERE rn = 1;
WITH ranked AS (
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY order_date DESC) AS rn
FROM orders
)
SELECT * FROM ranked WHERE rn = 1;
"์ฑ๋ณ๋ณ ์ง๊ณ", "์ํ๋ณ ์นด์ดํธ", "์กฐ๊ฑด๋ณ ํฉ๊ณ"
SELECT
SUM(CASE WHEN ์กฐ๊ฑด1 THEN 1 ELSE 0 END) AS count_1,
SUM(CASE WHEN ์กฐ๊ฑด2 THEN amount ELSE 0 END) AS sum_2
FROM ํ
์ด๋ธ;
SELECT
SUM(CASE WHEN amount < 100000 THEN 1 ELSE 0 END) AS '10๋ง์ ๋ฏธ๋ง',
SUM(CASE WHEN amount >= 100000 AND amount < 200000 THEN 1 ELSE 0 END) AS '10~20๋ง์',
SUM(CASE WHEN amount >= 200000 THEN 1 ELSE 0 END) AS '20๋ง์ ์ด์'
FROM orders;
"๊ฐ์ ๋ ์ฃผ๋ฌธํ ๋ค๋ฅธ ์ ์ ", "๋งค๋์ -์ง์ ๊ด๊ณ"
SELECT a.*, b.*
FROM ํ
์ด๋ธ a
JOIN ํ
์ด๋ธ b ON a.๊ณตํต์ปฌ๋ผ = b.๊ณตํต์ปฌ๋ผ
WHERE a.id != b.id; -- ์๊ธฐ ์์ ์ ์ธ
SELECT
a.user_id AS user1,
b.user_id AS user2,
a.order_date
FROM orders a
JOIN orders b ON a.order_date = b.order_date
WHERE a.user_id < b.user_id; -- ์ค๋ณต ์ ๊ฑฐ + ์๊ธฐ ์์ ์ ์ธ
"์๋ณ ๋ฐ์ดํฐ๋ฅผ ์ด๋ก ๋ณํ", "์ธ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ๋ก๋ก"
SELECT
๊ทธ๋ฃน์ปฌ๋ผ,
SUM(CASE WHEN ํผ๋ฒ์กฐ๊ฑด = '๊ฐ1' THEN amount END) AS ๊ฐ1,
SUM(CASE WHEN ํผ๋ฒ์กฐ๊ฑด = '๊ฐ2' THEN amount END) AS ๊ฐ2
FROM ํ
์ด๋ธ
GROUP BY ๊ทธ๋ฃน์ปฌ๋ผ;
SELECT
user_id,
SUM(CASE WHEN DATE_FORMAT(order_date, '%Y-%m') = '2024-01' THEN amount ELSE 0 END) AS '2024-01',
SUM(CASE WHEN DATE_FORMAT(order_date, '%Y-%m') = '2024-02' THEN amount ELSE 0 END) AS '2024-02'
FROM orders
GROUP BY user_id;
| user_id | 2024-01 | 2024-02 |
|---|---|---|
| 1 | 200000 | 100000 |
| 2 | 0 | 280000 |
| 3 | 0 | 300000 |
| ํจํด | ํต์ฌ ํค์๋ | ์ฌ์ฉ ๊ธฐ์ |
|---|---|---|
| ๊ทธ๋ฃน๋ณ Top N | "~๋ณ 1์", "~๋ณ ์์ N๊ฐ" | ROW_NUMBER + PARTITION BY |
| ์๋ ๊ฒ ์ฐพ๊ธฐ | "~์๋", "~์ ํ" | LEFT JOIN + IS NULL |
| ์ฐ์ N์ผ | "์ฐ์", "N์ผ ์ฐ์" | ROW_NUMBER + DATE ์ฐ์ฐ |
| ์ด์ ๋น๊ต | "์ ์ผ ๋๋น", "์ด์ ๋ณด๋ค" | LAG |
| ๋์ | "๋์ ", "Running" | SUM OVER(ORDER BY) |
| ๊ธฐ๊ฐ๋ณ | "์๋ณ", "์ฃผ๋ณ", "์ผ๋ณ" | DATE_FORMAT + GROUP BY |
| ์ค๋ณต ์ฒ๋ฆฌ | "์ค๋ณต", "์ต์ ๊ฒ๋ง" | ROW_NUMBER |
| ์กฐ๊ฑด๋ณ ์ง๊ณ | "~๋ณ๋ก ์นด์ดํธ" | CASE WHEN |
| ํผ๋ฒ | "์ด๋ก ๋ณํ", "๊ฐ๋ก๋ก" | CASE WHEN + GROUP BY |
"๊ฐ ๋ถ์๋ณ ์ต๊ณ ์ฐ๋ด์"
โ ๊ทธ๋ฃน๋ณ Top 1 ํจํด!
โ ROW_NUMBER + PARTITION BY
๋ณต์กํ ๋ฌธ์ = ์ฌ๋ฌ ๋จ์ํ ๋จ๊ณ
1๋จ๊ณ: ํ์ํ ๋ฐ์ดํฐ JOIN
2๋จ๊ณ: ์กฐ๊ฑด ํํฐ๋ง (WHERE)
3๋จ๊ณ: ๊ทธ๋ฃนํ (GROUP BY)
4๋จ๊ณ: ์๋์ฐ ํจ์ ์ ์ฉ
5๋จ๊ณ: ์ต์ข
ํํฐ๋ง
-- ํ ๋ฒ์ ์ฐ๋ฉด ๋ณต์ก
SELECT * FROM (SELECT * FROM (SELECT ...)) ...
-- CTE๋ก ๋จ๊ณ๋ณ๋ก
WITH step1 AS (...),
step2 AS (...),
step3 AS (...)
SELECT * FROM step3;
[์์ฃผ ๋์ค๋ ์์]
1. ๊ทธ๋ฃน๋ณ Top N (ROW_NUMBER)
2. ์๋ ๊ฒ ์ฐพ๊ธฐ (LEFT JOIN + NULL)
3. ๊ธฐ๊ฐ๋ณ ์ง๊ณ (DATE_FORMAT + GROUP BY)
4. ์ด์ ๊ฐ ๋น๊ต (LAG)
5. ์กฐ๊ฑด๋ณ ์ง๊ณ (CASE WHEN)
[ํต์ฌ ๊ธฐ์ ]
- CTE (WITH์ ): ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ๋จ๊ณ๋ณ๋ก
- ROW_NUMBER: ์์ ๋งค๊ธฐ๊ธฐ์ ๊ธฐ๋ณธ
- LAG/LEAD: ์ด์ /๋ค์ ํ ์ฐธ์กฐ
- CASE WHEN: ์กฐ๊ฑด๋ณ ์ฒ๋ฆฌ
๐ ํจํด์ ๋ง์ด ์์๋ก ์๋ก์ด ๋ฌธ์ ๋ ์ฝ๊ฒ ํ ์ ์๋ค.
์ 10๊ฐ์ง ํจํด๋ง ํ์คํ ์ตํ๋ ๋๋ถ๋ถ์ SQL ์ฝ๋ฉํ ์คํธ์ ๋์ ๊ฐ๋ฅ!