๋ฐ์ดํฐ ๋ถ์๊ฐ๋ฅผ ์ํ SQL: ์๋์ฐ ํจ์, CTE, ๊ณ ๊ธ ์ฟผ๋ฆฌ ๊ธฐ๋ฒ
SQL ์๋์ฐ ํจ์, CTE(๊ณตํต ํ ์ด๋ธ ์), ๊ณ ๊ธ ๋ถ์ ์ฟผ๋ฆฌ๋ฅผ ์ค์ฉ์ ์ธ ์ฝ๋ ์์ ์ ํจ๊ป ์ค๋ช ํฉ๋๋ค. ๋ฐ์ดํฐ ๋ถ์๊ฐ ๋ฉด์ ์ค๋น์ ์ค๋ฌด์ ํ์์ ์ธ ๊ธฐ๋ฒ์ ๋๋ค.

SQL ์๋์ฐ ํจ์, CTE(๊ณตํต ํ ์ด๋ธ ์), ๊ณ ๊ธ ์ฟผ๋ฆฌ ํจํด์ ๋ถ์ SQL์ ํต์ฌ ๊ธฐ๋ฐ์ ๊ตฌ์ฑํฉ๋๋ค. ๋ฐ์ดํฐ ๋ถ์๊ฐ ๋ฉด์ ์ ์ค๋นํ๋ , ๋ณต์กํ ๋ฆฌํฌํ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ , ์ด๋ฌํ ๊ธฐ๋ฒ์ ์๋ฌํ๋ฉด ์ฅํฉํ๊ณ ์ฝ๊ธฐ ์ด๋ ค์ด ์๋ธ์ฟผ๋ฆฌ๋ฅผ ๊ฐ๊ฒฐํ๊ณ ์ฑ๋ฅ์ด ์ฐ์ํ SQL๋ก ๋ณํํ ์ ์์ต๋๋ค.
์๋์ฐ ํจ์๋ ํ์ฌ ํ๊ณผ ๊ด๋ จ๋ ํ ์งํฉ์ ๋ํด ๊ณ์ฐ์ ์ํํฉ๋๋ค. GROUP BY์ฒ๋ผ ๊ฒฐ๊ณผ๋ฅผ ๋จ์ผ ํ์ผ๋ก ์ถ์ํ์ง ์๊ณ , ๊ฐ ํ์ ์ ์งํ ์ฑ ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ด ํน์ง์ ๋๋ค. CTE์ ๊ฒฐํฉํ๋ฉด ๋ณต์กํ ๋ถ์ ์ฟผ๋ฆฌ๋ ์ฝ๊ธฐ ์ฝ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ด ํํ๋ก ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
SQL ์๋์ฐ ํจ์์ OVER ์ ์ ๊ธฐ๋ณธ ์ดํด
์๋์ฐ ํจ์๋ ์ ์๋ "์๋์ฐ"(ํ์ ๋ฒ์)์ ๋ํด ๊ณ์ฐ์ ์ ์ฉํฉ๋๋ค. OVER ์ ์ ํด๋น ์๋์ฐ์ ํฌํจ๋๋ ํ๊ณผ ์ ๋ ฌ ์์๋ฅผ ์ ์ดํฉ๋๋ค. GROUP BY๋ฅผ ์ฌ์ฉํ๋ ์ง๊ณ ํจ์์ ๋ฌ๋ฆฌ, ์๋์ฐ ํจ์๋ ๊ฒฐ๊ณผ ์งํฉ์ ๋ชจ๋ ๊ฐ๋ณ ํ์ ์ ์งํฉ๋๋ค.
์๋์ ๊ตฌ๋ฌธ์ PostgreSQL, MySQL 8 ์ด์, BigQuery, SQL Server, Snowflake ๋ฑ ์ฃผ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ณตํต์ ์ผ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
-- window_function_syntax.sql
SELECT
employee_id,
department,
salary,
-- Ranks employees within each department by salary
RANK() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS dept_salary_rank,
-- Running total of salaries within department
SUM(salary) OVER (
PARTITION BY department
ORDER BY salary DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_salary_total
FROM employees;PARTITION BY๋ ๋ฐ์ดํฐ๋ฅผ ๊ทธ๋ฃน์ผ๋ก ๋ถํ ํฉ๋๋ค(GROUP BY์ ์ ์ฌํ์ง๋ง ํ์ ์ถ์ํ์ง ์์ต๋๋ค). ORDER BY๋ ๊ฐ ํํฐ์ ๋ด์ ์์๋ฅผ ์ ์ํฉ๋๋ค. ์ ํ์ ํ๋ ์ ์ (ROWS BETWEEN ...)์ ์๋์ฐ๋ฅผ ํน์ ํ ๋ฒ์๋ก ์ ํํฉ๋๋ค.
ROW_NUMBER, RANK, DENSE_RANK ๋น๊ต
์ด ์ธ ๊ฐ์ง ์์ ํจ์๋ ์ธ๊ด์ ์ ์ฌํ์ง๋ง, ๋์ผ ์์(ํ์ด)๊ฐ ์กด์ฌํ ๋ ๋์ ๋ฐฉ์์ด ๋ค๋ฆ ๋๋ค. ์๋ชป๋ ํจ์๋ฅผ ์ ํํ๋ ๊ฒ์ ๋ถ์ ์ฟผ๋ฆฌ์์ ์์ฃผ ๋ฐ์ํ๋ ๋ฒ๊ทธ์ ์์ธ์ ๋๋ค.
-- ranking_comparison.sql
SELECT
product_name,
category,
revenue,
ROW_NUMBER() OVER (ORDER BY revenue DESC) AS row_num,
RANK() OVER (ORDER BY revenue DESC) AS rank_val,
DENSE_RANK() OVER (ORDER BY revenue DESC) AS dense_rank_val
FROM products;| product_name | revenue | row_num | rank_val | dense_rank_val |
|---|---|---|---|---|
| Widget A | 50000 | 1 | 1 | 1 |
| Widget B | 50000 | 2 | 1 | 1 |
| Widget C | 40000 | 3 | 3 | 2 |
| Widget D | 30000 | 4 | 4 | 3 |
ROW_NUMBER๋ ํญ์ ๊ณ ์ ํ ์์ฐจ ์ ์๋ฅผ ํ ๋นํฉ๋๋ค. ๋์ผํ ๊ฐ์ ํ์ด ์์ด๋ ํ๋์ ํ์ ์์๋ก ๋ ์์ ๋ฒํธ๊ฐ ๋ถ์ฌ๋ฉ๋๋ค. RANK๋ ๋์ผ ์์ ํ์ ๊ฐ์ ๋ฒํธ๋ฅผ ํ ๋นํ์ง๋ง ํ์ ๋ฒํธ๋ฅผ ๊ฑด๋๋๋๋ค(1, 1, 3). DENSE_RANK๋ ๋์ผ ์์๋ฅผ ์ฒ๋ฆฌํ์ง๋ง ๋ฒํธ๋ฅผ ๊ฑด๋๋ฐ์ง ์์ต๋๋ค(1, 1, 2). ์์ N๊ฐ ์ฟผ๋ฆฌ์์ ์ค๋ณต ๊ฐ์ด ๊ฐ์ ์์๋ฅผ ๊ณต์ ํด์ผ ํ๋ ๊ฒฝ์ฐ, DENSE_RANK๊ฐ ์ ์ ํ ์ ํ์ ๋๋ค.
LAG์ LEAD๋ฅผ ํ์ฉํ ๊ธฐ๊ฐ ๋๋น ๋ถ์
LAG์ LEAD๋ ์ ํ ์กฐ์ธ ์์ด ์ด์ ๋๋ ์ดํ ํ์ ๋ฐ์ดํฐ์ ์ ๊ทผํ ์ ์๊ฒ ํด์ค๋๋ค. ์ ๊ธฐ ๋๋น ์ฑ์ฅ๋ฅ ๊ณ์ฐ, ํธ๋ ๋ ๊ฐ์ง, ๋ณํ ์๋ณ์ ํ์์ ์ธ ํจ์์ ๋๋ค.
-- period_over_period.sql
SELECT
month,
revenue,
-- Previous month revenue
LAG(revenue, 1) OVER (ORDER BY month) AS prev_month_revenue,
-- Month-over-month growth rate
ROUND(
(revenue - LAG(revenue, 1) OVER (ORDER BY month)) * 100.0
/ NULLIF(LAG(revenue, 1) OVER (ORDER BY month), 0),
2
) AS mom_growth_pct,
-- Next month revenue (forward-looking)
LEAD(revenue, 1) OVER (ORDER BY month) AS next_month_revenue
FROM monthly_sales
ORDER BY month;LAG/LEAD์ ๋ ๋ฒ์งธ ์ธ์๋ ์คํ์ (๊ธฐ๋ณธ๊ฐ 1)์ ์ง์ ํฉ๋๋ค. ์ธ ๋ฒ์งธ ์ ํ์ ์ธ์๋ ํด๋น ์คํ์ ์ ํ์ด ์กด์ฌํ์ง ์์ ๋์ ๊ธฐ๋ณธ๊ฐ์ ์ ๊ณตํ๋ฉฐ, ์ฒซ ๋ฒ์งธ/๋ง์ง๋ง ํ์์ NULL์ ๋ฐฉ์งํ๋ ๋ฐ ์ ์ฉํฉ๋๋ค. ์ฑ์ฅ๋ฅ ๊ณ์ฐ์ NULLIF๋ 0์ผ๋ก ๋๋๊ธฐ ์ค๋ฅ๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
NTILE์ ํ์ฉํ ๋ฐฑ๋ถ์ ๋ถํ
NTILE์ ์ง์ ๋ ์์ ๊ฑฐ์ ๋์ผํ ๊ทธ๋ฃน์ผ๋ก ํ์ ๋ถ๋ฐฐํฉ๋๋ค. ๋ฐ์ดํฐ ๋ถ์๊ฐ๋ ์ฌ๋ถ์ ๋ถ์, ์ญ๋ถ์ ์ค์ฝ์ด๋ง, ๊ณ ๊ฐ ์ธ๋ถํ์ ์ด ํจ์๋ฅผ ํ์ฉํฉ๋๋ค.
-- customer_segmentation.sql
SELECT
customer_id,
total_spend,
-- Split customers into 4 quartiles by spend
NTILE(4) OVER (ORDER BY total_spend DESC) AS spend_quartile,
-- Decile scoring for finer granularity
NTILE(10) OVER (ORDER BY total_spend DESC) AS spend_decile
FROM (
SELECT
customer_id,
SUM(order_total) AS total_spend
FROM orders
WHERE order_date >= '2025-01-01'
GROUP BY customer_id
) customer_totals;์ฌ๋ถ์ 1์๋ ์ง์ถ ๊ธ์ก์ด ๊ฐ์ฅ ๋์ ๊ณ ๊ฐ์ด, ์ฌ๋ถ์ 4์๋ ๊ฐ์ฅ ๋ฎ์ ๊ณ ๊ฐ์ด ํฌํจ๋ฉ๋๋ค. ์ด ํจํด์ ๋ง์ผํ ๋ถ์์์ ์ฌ์ฉ๋๋ RFM(์ต๊ทผ์ฑ, ๋น๋, ๊ธ์ก) ์ธ๋ถํ์ ์ง์ ๋์๋ฉ๋๋ค.
Data Analytics ๋ฉด์ ์ค๋น๊ฐ ๋์ จ๋์?
์ธํฐ๋ํฐ๋ธ ์๋ฎฌ๋ ์ดํฐ, flashcards, ๊ธฐ์ ํ ์คํธ๋ก ์ฐ์ตํ์ธ์.
๊ณตํต ํ ์ด๋ธ ์(CTE): ์ค์ฒฉ ์๋ธ์ฟผ๋ฆฌ์ ๋์ฒด
CTE(WITH ์ )๋ ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ์ด๋ฆ์ด ์ง์ ๋ ์ฝ๊ธฐ ์ฌ์ด ๋จ๊ณ๋ก ๋ถํดํฉ๋๋ค. ๊ฐ CTE๋ ํด๋น ์ฟผ๋ฆฌ์ ์คํ ๊ธฐ๊ฐ ๋์์๋ง ์กด์ฌํ๋ ์์ ๋ช ๋ช ๊ฒฐ๊ณผ ์งํฉ์ผ๋ก ๊ธฐ๋ฅํฉ๋๋ค. ๊ฐ๋ ์ฑ ํฅ์ ์ธ์๋, ๊ฐ ๋จ๊ณ๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ํ ์คํธํ ์ ์์ด ๋๋ฒ๊น ์ด ์ฉ์ดํด์ง๋๋ค.
-- cte_sales_analysis.sql
WITH monthly_revenue AS (
-- Step 1: Aggregate raw orders into monthly totals
SELECT
DATE_TRUNC('month', order_date) AS month,
product_category,
SUM(amount) AS revenue,
COUNT(DISTINCT customer_id) AS unique_customers
FROM orders
WHERE order_date >= '2025-01-01'
GROUP BY DATE_TRUNC('month', order_date), product_category
),
ranked_categories AS (
-- Step 2: Rank categories within each month
SELECT
month,
product_category,
revenue,
unique_customers,
RANK() OVER (
PARTITION BY month
ORDER BY revenue DESC
) AS category_rank
FROM monthly_revenue
)
-- Step 3: Final output โ top 3 categories per month
SELECT
month,
product_category,
revenue,
unique_customers,
category_rank
FROM ranked_categories
WHERE category_rank <= 3
ORDER BY month, category_rank;์ด 3๋จ๊ณ ์ ๊ทผ ๋ฐฉ์์ ๊น์ด ์ค์ฒฉ๋ ์๋ธ์ฟผ๋ฆฌ๋ฅผ ๋์ฒดํฉ๋๋ค. ๊ฐ CTE๋ ์ง๊ณ, ์์ ๋งค๊ธฐ๊ธฐ, ํํฐ๋ง์ด๋ผ๋ ๋ช ํํ ์ฑ ์์ ๊ฐ์ง๊ณ ์์ต๋๋ค.
์ฌ๊ท CTE๋ฅผ ํ์ฉํ ๊ณ์ธต ๋ฐ์ดํฐ ์ฒ๋ฆฌ
์ฌ๊ท CTE๋ ๊ณ์ธต ๊ตฌ์กฐ ๋๋ ๊ทธ๋ํํ ๋ฐ์ดํฐ์ ๊ด๋ จ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํฉ๋๋ค. ์กฐ์ง๋, ์นดํ ๊ณ ๋ฆฌ ํธ๋ฆฌ, BOM(์์ฌ๋ช ์ธ์), ๊ฒฝ๋ก ํ์ ์ฟผ๋ฆฌ ๋ฑ์ด ๋ํ์ ์ธ ์ฌ์ฉ ์ฌ๋ก์ ๋๋ค.
-- recursive_org_chart.sql
WITH RECURSIVE org_hierarchy AS (
-- Base case: top-level managers (no manager above them)
SELECT
employee_id,
employee_name,
manager_id,
1 AS depth,
employee_name AS management_chain
FROM employees
WHERE manager_id IS NULL
UNION ALL
-- Recursive case: join employees to their managers
SELECT
e.employee_id,
e.employee_name,
e.manager_id,
oh.depth + 1,
oh.management_chain || ' > ' || e.employee_name
FROM employees e
INNER JOIN org_hierarchy oh ON e.manager_id = oh.employee_id
)
SELECT
employee_id,
employee_name,
depth,
management_chain
FROM org_hierarchy
ORDER BY management_chain;๋ฒ ์ด์ค ์ผ์ด์ค๋ ๋ฃจํธ ๋ ธ๋(์์ ๊ด๋ฆฌ์๊ฐ ์๋ ์ง์)๋ฅผ ์ ํํฉ๋๋ค. ์ฌ๊ท ์ผ์ด์ค๋ CTE ์์ ์ ๋ค์ ์กฐ์ธํ์ฌ ๊ณ์ธต์ ๋ ๋ฒจ๋ณ๋ก ๊ตฌ์ถํฉ๋๋ค. management_chain ์ด์ ์ด๋ฆ์ ์ฐ๊ฒฐํ์ฌ ์ ์ฒด ๋ณด๊ณ ๊ฒฝ๋ก๋ฅผ ํ์ํฉ๋๋ค. ๋๋ถ๋ถ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๋ฌดํ ๋ฃจํ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ์ฌ๊ท ๊น์ด๋ฅผ ์ ํํ๋ฉฐ, PostgreSQL์ ๊ธฐ๋ณธ๊ฐ์ 100ํ์ ๋๋ค.
์ฌ๊ท CTE๋ ๋๊ท๋ชจ ๋ฐ์ดํฐ์ ์์ ์ฒ๋ฆฌ ์๋๊ฐ ๋๋ ค์ง ์ ์์ต๋๋ค. ๋ฐ๋์ ๊น์ด ์ ํ(WHERE depth < 10)์ ํฌํจํ๊ณ , ์กฐ์ธ ์ด(manager_id)์ ์ธ๋ฑ์ค๊ฐ ์ค์ ๋์ด ์๋์ง ํ์ธํด์ผ ํฉ๋๋ค. ๋งค์ฐ ๊น์ ๊ณ์ธต ๊ตฌ์กฐ์ ๊ฒฝ์ฐ, ๊ตฌ์ฒดํ๋ ๊ฒฝ๋ก(Materialized Path) ๋๋ ์ค์ฒฉ ์งํฉ(Nested Set) ํจํด ๋์ ์ ๊ฒํ ํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
๊ณ ๊ธ ๋ถ์ ํจํด: ๊ฐญ๊ณผ ์์ผ๋๋, ๋์ ํฉ๊ณ
๊ฐญ๊ณผ ์์ผ๋๋ ํจํด์ ๋ฐ์ดํฐ ๋ด์ ์ฐ์์ ์ธ ์ํ์ค๋ฅผ ์๋ณํฉ๋๋ค. ํ์ฑ ๊ตฌ๋ ๊ธฐ๊ฐ, ์ฐ์ ๋ก๊ทธ์ธ ์ผ์, ์ค๋จ ์๋ ๊ฐ๋ ๊ธฐ๊ฐ ๋ฑ์ด ํด๋น๋ฉ๋๋ค. ์ด ๊ธฐ๋ฒ์ ROW_NUMBER์ ๋ ์ง ์ฐ์ฐ์ ๊ฒฐํฉํฉ๋๋ค.
-- consecutive_login_streaks.sql
WITH login_days AS (
SELECT DISTINCT
user_id,
DATE(login_timestamp) AS login_date
FROM user_sessions
),
streaks AS (
SELECT
user_id,
login_date,
-- Subtracting row_number from date creates a constant for consecutive days
login_date - (ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY login_date
))::int AS streak_group
FROM login_days
)
SELECT
user_id,
MIN(login_date) AS streak_start,
MAX(login_date) AS streak_end,
COUNT(*) AS streak_length
FROM streaks
GROUP BY user_id, streak_group
HAVING COUNT(*) >= 3
ORDER BY streak_length DESC;์ด ๊ธฐ๋ฒ์ ํต์ฌ ์๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. ์ฐ์๋ ๋ ์ง์์ ์ฆ๊ฐํ๋ ํ ๋ฒํธ๋ฅผ ๋นผ๋ฉด ํญ์ ๋์ผํ ๊ฐ์ด ์ฐ์ถ๋ฉ๋๋ค. ๊ฐญ์ด ๋ฐ์ํ๋ฉด ๊ฒฐ๊ณผ ๊ฐ์ด ๋ณ๊ฒฝ๋์ด ์๋ก์ด ๊ทธ๋ฃน์ด ํ์ฑ๋ฉ๋๋ค. ์ด ์ฟผ๋ฆฌ๋ ์ฌ์ฉ์๋ณ๋ก 3์ผ ์ด์์ ์ฐ์ ๋ก๊ทธ์ธ ์คํธ๋ฆญ์ ๋ชจ๋ ๊ฒ์ถํฉ๋๋ค.
์๋์ฐ ํจ์์ CASE ์์ ๊ฒฐํฉ์ ํตํ ์กฐ๊ฑด๋ถ ๋ถ์
์ค๋ฌด ๋ถ์ ์ฟผ๋ฆฌ์์๋ ์๋์ฐ ํจ์์ CASE ์์ ๊ฒฐํฉํ์ฌ ๋์ผํ ์ฟผ๋ฆฌ ๋ด์์ ์กฐ๊ฑด๋ถ ๋ฉํธ๋ฆญ์ ๊ณ์ฐํ๋ ๊ฒฝ์ฐ๊ฐ ๋น๋ฒํฉ๋๋ค.
-- conditional_analytics.sql
SELECT
order_date,
product_category,
amount,
-- 7-day moving average
AVG(amount) OVER (
PARTITION BY product_category
ORDER BY order_date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS moving_avg_7d,
-- Cumulative sum resetting each quarter
SUM(amount) OVER (
PARTITION BY product_category, DATE_TRUNC('quarter', order_date)
ORDER BY order_date
) AS qtd_cumulative,
-- Flag if current row exceeds 2x the category average
CASE
WHEN amount > 2 * AVG(amount) OVER (PARTITION BY product_category)
THEN 'OUTLIER'
ELSE 'NORMAL'
END AS outlier_flag
FROM daily_sales
ORDER BY product_category, order_date;์ด ๋จ์ผ ์ฟผ๋ฆฌ๋ก 7์ผ ์ด๋ ํ๊ท , ๋ถ๊ธฐ๋ณ ๋ฆฌ์ ๋์ ํฉ๊ณ, ์ด์์น ํ๋๊ทธ๋ฅผ ๋์์ ๊ณ์ฐํ ์ ์์ต๋๋ค. ์๋ธ์ฟผ๋ฆฌ๋ ์ ํ ์กฐ์ธ์ด ์ ํ ํ์ํ์ง ์์ต๋๋ค.
์๋์ฐ ํจ์๋ WHERE, GROUP BY, HAVING ์ดํ์ ์คํ๋๋ฉฐ, ORDER BY์ LIMIT ์ด์ ์ ์คํ๋ฉ๋๋ค. ๋ฐ๋ผ์ WHERE ์ ์์ ์๋์ฐ ํจ์๋ฅผ ์ง์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์๋์ฐ ํจ์ ๊ฒฐ๊ณผ๋ก ํํฐ๋งํ๋ ค๋ฉด ์ฟผ๋ฆฌ๋ฅผ CTE ๋๋ ์๋ธ์ฟผ๋ฆฌ๋ก ๋ํํด์ผ ํฉ๋๋ค.
๋ถ์ ์ฟผ๋ฆฌ์ ์ฑ๋ฅ ์ต์ ํ
์๋์ฐ ํจ์์ CTE๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ด์ง๋ง ๋๊ท๋ชจ ํ ์ด๋ธ์์๋ ๋ณ๋ชฉ์ด ๋ ์ ์์ต๋๋ค. ๊ท๋ชจ์ ๊ด๊ณ์์ด ์ฑ๋ฅ์ ์ ์งํ๊ธฐ ์ํ ์ฃผ์ ๊ธฐ๋ฒ์ ์๊ฐํฉ๋๋ค.
์ฒซ์งธ, PARTITION BY ๋ฐ ORDER BY ์ ์์ ์ฌ์ฉ๋๋ ์ด์ ์ธ๋ฑ์ค๋ฅผ ์์ฑํฉ๋๋ค. ์๋์ฐ ์ ์์ ์ผ์นํ๋ ๋ณตํฉ ์ธ๋ฑ์ค๊ฐ ์์ผ๋ฉด ์ ๋ ฌ ์ฐ์ฐ์ ์๋ตํ ์ ์์ต๋๋ค.
๋์งธ, ์๋์ฐ ํจ์๋ฅผ ์ ์ฉํ๊ธฐ ์ ์ ๋ฐ์ดํฐ๋ฅผ ํํฐ๋งํฉ๋๋ค. WHERE ์ (์๋์ฐ ํจ์ ํ๊ฐ ์ ์ ์คํ๋จ)์ ์กฐ๊ฑด์ ๋ฐฐ์นํ๋ฉด ์๋์ฐ๊ฐ ์ฒ๋ฆฌํด์ผ ํ๋ ๋ฐ์ดํฐ์ ํฌ๊ธฐ๋ฅผ ์ค์ผ ์ ์์ต๋๋ค.
์ ์งธ, ์ค๋ณต๋ ์๋์ฐ ์ ์๋ฅผ ํผํฉ๋๋ค. ๋ช ๋ช ๋ ์๋์ฐ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ๋ณต์ ์ค์ด๊ณ , ์ตํฐ๋ง์ด์ ์๊ฒ ์ฌ๋ฌ ํจ์๊ฐ ๋์ผํ ํ๋ ์์ ๊ณต์ ํ๋ค๋ ๊ฒ์ ์๋ฆด ์ ์์ต๋๋ค.
-- named_window.sql
SELECT
department,
employee_id,
salary,
RANK() OVER dept_window AS dept_rank,
SUM(salary) OVER dept_window AS dept_total,
AVG(salary) OVER dept_window AS dept_avg
FROM employees
WINDOW dept_window AS (
PARTITION BY department
ORDER BY salary DESC
);WINDOW ์ (PostgreSQL, MySQL 8 ์ด์, BigQuery์์ ์ง์)์ ์๋์ฐ๋ฅผ ํ ๋ฒ ์ ์ํ๊ณ ์ฌ๋ฌ ํจ์์์ ์ฌ์ฌ์ฉํ ์ ์๊ฒ ํฉ๋๋ค. ๊ฐ๋ ์ฑ ํฅ์๊ณผ ์ฟผ๋ฆฌ ํ๋ ์ต์ ํ์ ๋ชจ๋ ๊ธฐ์ฌํฉ๋๋ค.
Data Analytics ๋ฉด์ ์ค๋น๊ฐ ๋์ จ๋์?
์ธํฐ๋ํฐ๋ธ ์๋ฎฌ๋ ์ดํฐ, flashcards, ๊ธฐ์ ํ ์คํธ๋ก ์ฐ์ตํ์ธ์.
๊ฒฐ๋ก
- ์๋์ฐ ํจ์(ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, NTILE)๋ GROUP BY ์ง๊ณ์ ๋ฌ๋ฆฌ ํ์ ์ถ์ํ์ง ์๊ณ ํ ์งํฉ์ ๋ํด ๊ณ์ฐ์ ์ํํฉ๋๋ค
- CTE๋ ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ๋ช ๋ช ๋, ํ ์คํธ ๊ฐ๋ฅํ ๋จ๊ณ๋ก ๋ถํดํ๊ณ ๊น์ด ์ค์ฒฉ๋ ์๋ธ์ฟผ๋ฆฌ๋ฅผ ๋์ฒดํฉ๋๋ค
- ์ฌ๊ท CTE๋ ์กฐ์ง๋๋ ์นดํ ๊ณ ๋ฆฌ ํธ๋ฆฌ์ ๊ฐ์ ๊ณ์ธต ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ์ง๋ง, ๊น์ด ์ ํ๊ณผ ์ธ๋ฑ์ค๋ ์กฐ์ธ ์ด์ด ํ์ํฉ๋๋ค
- ๊ฐญ๊ณผ ์์ผ๋๋ ๊ธฐ๋ฒ(ROW_NUMBER + ๋ ์ง ์ฐ์ฐ)์ ์๊ณ์ด ๋ฐ์ดํฐ์ ์ฐ์ ์ํ์ค๋ฅผ ์๋ณํฉ๋๋ค
- ๋ช ๋ช ๋ ์๋์ฐ(WINDOW ์ )๋ ์ฝ๋ ์ค๋ณต์ ์ค์ด๊ณ ์ฟผ๋ฆฌ ํ๋ ์ต์ ํ๋ฅผ ๊ฐ์ ํ ์ ์์ต๋๋ค
- WHERE ์ ์์ ์๋์ฐ ํจ์ ํ๊ฐ ์ ์ ๋ฐ์ดํฐ๋ฅผ ํํฐ๋งํ์ฌ ๋๊ท๋ชจ ํ ์ด๋ธ์์์ ์ฑ๋ฅ์ ์ ์งํด์ผ ํฉ๋๋ค
- ์ด๋ฌํ ํจํด์ ๋ฐ์ดํฐ ๋ถ์๊ฐ ๋ฉด์ ์์ ๋น๋ฒํ๊ฒ ์ถ์ ๋๋ฉฐ, ์ผ์์ ์ธ ๋ฆฌํฌํ , ์ธ๋ถํ, ํธ๋ ๋ ๋ถ์์๋ ์ง์ ํ์ฉ๋ฉ๋๋ค
์ฐ์ต์ ์์ํ์ธ์!
๋ฉด์ ์๋ฎฌ๋ ์ดํฐ์ ๊ธฐ์ ํ ์คํธ๋ก ์ง์์ ํ ์คํธํ์ธ์.
Data Analytics ์ฝ๋์ ๋ฒ๊ทธ๋ฅผ ์ฐพ์ ์ ์๋์
์ค์ ์ฝ๋ ํ ์กฐ๊ฐ, ์จ์ ๋ฒ๊ทธ ํ๋, ํ๋ฃจ ํ ๋ฒ. ๊ณ์ ์์ด ๋ฐ๋ก ๋์ ํ ์ ์์ต๋๋ค.

์์ฑ์
Anthony Fillion-MailletSharpSkill ์ฐฝ์ ์
10๋ ์ด์ ํ์คํ ๊ฐ๋ฐ์ ํด์์ต๋๋ค. SharpSkill์ ์ด์ํ๋ฉฐ ์ด๊ณณ์ ๊ฒ์๋๋ ๋ชจ๋ ๋ด์ฉ์ ์ฑ ์์ ์ง๋๋ค.
2026๋ 4์ 9์ผ ์ ๋ฐ์ดํธ
ํ๊ทธ
๊ณต์
๊ด๋ จ ๊ธฐ์ฌ

2026๋ ์ดํ๋ฆฌ์ ๋ฐ์ดํฐ ๋ถ์๊ฐ ๋ฉด์ ์ง๋ฌธ: SQL, Python, ๋ถ์ ์คํฌ ์์ ๊ฐ์ด๋
2026๋ ์ดํ๋ฆฌ์์์ ๋ฐ์ดํฐ ๋ถ์๊ฐ๋ก ์ทจ์ ํ๊ธฐ ์ํ ๋ฉด์ ์ค๋น ๊ฐ์ด๋์ ๋๋ค. SQL, Python, pandas, ๋น์ฆ๋์ค ๋ถ์์ ๊ดํ ์์ฃผ ์ถ์ ๋๋ ์ง๋ฌธ๊ณผ ๋ชจ๋ฒ ๋ต๋ณ์ ์์ธํ ์ค๋ช ํฉ๋๋ค.

๋ฐ์ดํฐ ๋ถ์๊ฐ ๋ฉด์ ์ง๋ฌธ 2026๋ ์๋ฒฝ ๊ฐ์ด๋: SQL, Python, ๋ถ์ ์คํฌ
2026๋ ๋ฐ์ดํฐ ๋ถ์๊ฐ ๋ฉด์ ์์ ์์ฃผ ์ถ์ ๋๋ SQL, Python, ํต๊ณ ๋ถ์ ์ง๋ฌธ๊ณผ ๋ชจ๋ฒ ๋ต๋ณ์ ์์ธํ ๋ค๋ฃน๋๋ค. ์ค์ ์ฝ๋ ์์ ์ ํต์ฌ ํฌ์ธํธ๋ก ์ทจ์ ์ฑ๊ณต์ ์ค๋นํฉ๋๋ค.

2026๋ ๋ฐ์ดํฐ ๋ถ์๊ฐ๋ฅผ ์ํ dbt ์๋ฒฝ ๊ฐ์ด๋: ๋ชจ๋ธ๋ง, ํ ์คํธ, ๋ฉด์ ์ง๋ฌธ
dbt ํ๋ก์ ํธ ๊ตฌ์กฐ, ๋จธํฐ๋ฆฌ์ผ๋ผ์ด์ ์ด์ ์ ๋ต, ๋ฐ์ดํฐ ํ์ง ํ ์คํธ, Jinja ๋งคํฌ๋ก, ๊ทธ๋ฆฌ๊ณ ์ค๋ฌด ๋ฉด์ ์์ ์์ฃผ ๋ฑ์ฅํ๋ ์ง๋ฌธ๊น์ง ํฌ๊ด์ ์ผ๋ก ๋ค๋ฃน๋๋ค.