ROW_NUMBER, LAG




๐ก ์ฝ๋ฉํ ์คํธ ๋จ๊ณจ! ์๋์ฐ ํจ์๋ฅผ ์๋ฒฝํ๊ฒ ์ ๋ฆฌํ๋ค.
GROUP BY ์์ด ์ง๊ณํ๋ฉด์ ๊ฐ๋ณ ํ๋ ์ ์งํ๋ ํจ์.
-- GROUP BY: ํ์ด ํฉ์ณ์ง
SELECT user_id, SUM(amount)
FROM orders
GROUP BY user_id;
-- ๊ฒฐ๊ณผ: 3ํ (์ ์ ์๋งํผ)
-- ์๋์ฐ ํจ์: ํ์ด ์ ์ง๋จ
SELECT
user_id,
amount,
SUM(amount) OVER(PARTITION BY user_id) AS user_total
FROM orders;
-- ๊ฒฐ๊ณผ: 6ํ (์๋ณธ ๊ทธ๋๋ก)
โ ์๋ณธ ๋ฐ์ดํฐ + ์ง๊ณ ๊ฒฐ๊ณผ๋ฅผ ๋์์!
[orders]
| order_id | user_id | amount | order_date |
|----------|---------|--------|------------|
| 1 | 101 | 150000 | 2024-01-10 |
| 2 | 101 | 50000 | 2024-01-20 |
| 3 | 101 | 100000 | 2024-02-05 |
| 4 | 102 | 200000 | 2024-01-15 |
| 5 | 102 | 80000 | 2024-02-10 |
| 6 | 103 | 300000 | 2024-02-15 |
ํจ์๋ช
() OVER (
PARTITION BY ๊ทธ๋ฃน_์ปฌ๋ผ -- ๊ทธ๋ฃน ๋๋๊ธฐ (์ ํ)
ORDER BY ์ ๋ ฌ_์ปฌ๋ผ -- ์ ๋ ฌ ๊ธฐ์ค (์ ํ)
)
| ํจ์ | ์ค๋ช |
|---|---|
| ROW_NUMBER() | ์์ฐจ ๋ฒํธ (1,2,3,4,5) |
| RANK() | ๋์ ์ ๊ฐ์ ์์, ๋ค์ ์์ ๊ฑด๋๋ (1,2,2,4,5) |
| DENSE_RANK() | ๋์ ์ ๊ฐ์ ์์, ๋ค์ ์์ ์ฐ์ (1,2,2,3,4) |
| ํจ์ | ์ค๋ช |
|---|---|
| SUM() | ํฉ๊ณ |
| AVG() | ํ๊ท |
| COUNT() | ๊ฐ์ |
| MAX() / MIN() | ์ต๋/์ต์ |
| ํจ์ | ์ค๋ช |
|---|---|
| LAG() | ์ด์ ํ ๊ฐ |
| LEAD() | ๋ค์ ํ ๊ฐ |
| FIRST_VALUE() | ์ฒซ ๋ฒ์งธ ๊ฐ |
| LAST_VALUE() | ๋ง์ง๋ง ๊ฐ |
SELECT
*,
ROW_NUMBER() OVER(ORDER BY amount DESC) AS rn
FROM orders;
| order_id | user_id | amount | rn |
|---|---|---|---|
| 6 | 103 | 300000 | 1 |
| 4 | 102 | 200000 | 2 |
| 1 | 101 | 150000 | 3 |
| 3 | 101 | 100000 | 4 |
| 5 | 102 | 80000 | 5 |
| 2 | 101 | 50000 | 6 |
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY amount DESC) AS rn
FROM orders;
| order_id | user_id | amount | rn |
|---|---|---|---|
| 1 | 101 | 150000 | 1 |
| 3 | 101 | 100000 | 2 |
| 2 | 101 | 50000 | 3 |
| 4 | 102 | 200000 | 1 |
| 5 | 102 | 80000 | 2 |
| 6 | 103 | 300000 | 1 |
โ ์ ์ ๋ณ๋ก ๊ธ์ก ์์!
-- ๊ฐ ์ ์ ์ ๊ฐ์ฅ ํฐ ์ฃผ๋ฌธ๋ง ๊ฐ์ ธ์ค๊ธฐ
WITH ranked AS (
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY amount DESC) AS rn
FROM orders
)
SELECT * FROM ranked WHERE rn = 1;
| order_id | user_id | amount | rn |
|---|---|---|---|
| 1 | 101 | 150000 | 1 |
| 4 | 102 | 200000 | 1 |
| 6 | 103 | 300000 | 1 |
์ฝํ ๋จ๊ณจ ํจํด! ๐ฅ
SELECT
amount,
ROW_NUMBER() OVER(ORDER BY amount DESC) AS row_num,
RANK() OVER(ORDER BY amount DESC) AS rank,
DENSE_RANK() OVER(ORDER BY amount DESC) AS dense_rank
FROM (
SELECT 300000 AS amount UNION ALL
SELECT 200000 UNION ALL
SELECT 200000 UNION ALL
SELECT 100000 UNION ALL
SELECT 50000
) t;
| amount | row_num | rank | dense_rank |
|---|---|---|---|
| 300000 | 1 | 1 | 1 |
| 200000 | 2 | 2 | 2 |
| 200000 | 3 | 2 | 2 |
| 100000 | 4 | 4 | 3 |
| 50000 | 5 | 5 | 4 |
์ฐจ์ด์ :
SELECT
*,
SUM(amount) OVER() AS total
FROM orders;
| order_id | amount | total |
|---|---|---|
| 1 | 150000 | 880000 |
| 2 | 50000 | 880000 |
| ... | ... | 880000 |
SELECT
*,
SUM(amount) OVER(PARTITION BY user_id) AS user_total
FROM orders;
| order_id | user_id | amount | user_total |
|---|---|---|---|
| 1 | 101 | 150000 | 300000 |
| 2 | 101 | 50000 | 300000 |
| 3 | 101 | 100000 | 300000 |
| 4 | 102 | 200000 | 280000 |
| ... | ... | ... | ... |
SELECT
*,
SUM(amount) OVER(ORDER BY order_date) AS running_total
FROM orders;
| order_id | order_date | amount | running_total |
|---|---|---|---|
| 1 | 2024-01-10 | 150000 | 150000 |
| 4 | 2024-01-15 | 200000 | 350000 |
| 2 | 2024-01-20 | 50000 | 400000 |
| 3 | 2024-02-05 | 100000 | 500000 |
| 5 | 2024-02-10 | 80000 | 580000 |
| 6 | 2024-02-15 | 300000 | 880000 |
โ ๋ ์ง ์์ผ๋ก ๋์ !
SELECT
order_id,
order_date,
amount,
LAG(amount) OVER(ORDER BY order_date) AS prev_amount
FROM orders;
| order_id | order_date | amount | prev_amount |
|---|---|---|---|
| 1 | 2024-01-10 | 150000 | NULL |
| 4 | 2024-01-15 | 200000 | 150000 |
| 2 | 2024-01-20 | 50000 | 200000 |
| 3 | 2024-02-05 | 100000 | 50000 |
| ... | ... | ... | ... |
SELECT
order_id,
amount,
LEAD(amount) OVER(ORDER BY order_date) AS next_amount
FROM orders;
SELECT
order_date,
amount,
LAG(amount) OVER(ORDER BY order_date) AS prev_amount,
amount - LAG(amount) OVER(ORDER BY order_date) AS diff
FROM orders;
| order_date | amount | prev_amount | diff |
|---|---|---|---|
| 2024-01-10 | 150000 | NULL | NULL |
| 2024-01-15 | 200000 | 150000 | 50000 |
| 2024-01-20 | 50000 | 200000 | -150000 |
LAG(์ปฌ๋ผ, N, ๊ธฐ๋ณธ๊ฐ)
-- N: ๋ช ํ ์ ? (๊ธฐ๋ณธ 1)
-- ๊ธฐ๋ณธ๊ฐ: NULL ๋์ ์ฌ์ฉํ ๊ฐ
SELECT
amount,
LAG(amount, 1, 0) OVER(ORDER BY order_date) AS prev_1,
LAG(amount, 2, 0) OVER(ORDER BY order_date) AS prev_2
FROM orders;
SELECT
user_id,
order_date,
amount,
FIRST_VALUE(amount) OVER(
PARTITION BY user_id
ORDER BY order_date
) AS first_order_amount
FROM orders;
| user_id | order_date | amount | first_order_amount |
|---|---|---|---|
| 101 | 2024-01-10 | 150000 | 150000 |
| 101 | 2024-01-20 | 50000 | 150000 |
| 101 | 2024-02-05 | 100000 | 150000 |
SELECT
order_date,
amount,
AVG(amount) OVER(
ORDER BY order_date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS moving_avg_3
FROM orders;
โ ํ์ฌ ํ ํฌํจ ์ต๊ทผ 3๊ฐ์ ํ๊ท
ROWS BETWEEN ์์ AND ๋
-- ์์/๋ ์ต์
UNBOUNDED PRECEDING -- ์ฒ์๋ถํฐ
N PRECEDING -- Nํ ์
CURRENT ROW -- ํ์ฌ ํ
N FOLLOWING -- Nํ ํ
UNBOUNDED FOLLOWING -- ๋๊น์ง
์์:
-- ํ์ฌ ํ ๊ธฐ์ค ์๋ค 1ํ์ฉ ํฌํจ
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
-- ์ฒ์๋ถํฐ ํ์ฌ๊น์ง (๋์ )
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
์ ๋ต ๋ณด๊ธฐ๊ฐ ์ ์ ๋ณ ์ฃผ๋ฌธ ๊ธ์ก ์์ 2๊ฐ๋ฅผ ๊ตฌํ์ธ์.
WITH ranked AS (
SELECT
*,
ROW_NUMBER() OVER(
PARTITION BY user_id
ORDER BY amount DESC
) AS rn
FROM orders
)
SELECT * FROM ranked WHERE rn <= 2;
์ ๋ต ๋ณด๊ธฐ๋ ์ง ์์ผ๋ก ๋์ ๋งค์ถ์ ๊ตฌํ์ธ์.
SELECT
order_date,
amount,
SUM(amount) OVER(ORDER BY order_date) AS cumulative_sum
FROM orders;
์ ๋ต ๋ณด๊ธฐ๊ฐ ์ฃผ๋ฌธ์ ์ ์ผ ์ฃผ๋ฌธ ๋๋น ๊ธ์ก ์ฐจ์ด๋ฅผ ๊ตฌํ์ธ์.
SELECT
order_date,
amount,
LAG(amount) OVER(ORDER BY order_date) AS prev_amount,
amount - LAG(amount) OVER(ORDER BY order_date) AS diff
FROM orders;
์ ๋ต ๋ณด๊ธฐ๊ฐ ์ฃผ๋ฌธ์ด ํด๋น ์ ์ ์ด ์ฃผ๋ฌธ๊ธ์ก์์ ์ฐจ์งํ๋ ๋น์จ์ ๊ตฌํ์ธ์.
SELECT
user_id,
order_id,
amount,
SUM(amount) OVER(PARTITION BY user_id) AS user_total,
ROUND(amount * 100.0 / SUM(amount) OVER(PARTITION BY user_id), 2) AS pct
FROM orders;
์ ๋ต ๋ณด๊ธฐ์ฃผ๋ฌธ ๊ธ์ก์ด ์ด์ ๋ณด๋ค ์ฆ๊ฐํ ์ฃผ๋ฌธ์ ์ฐพ์ผ์ธ์.
WITH with_prev AS (
SELECT
*,
LAG(amount) OVER(ORDER BY order_date) AS prev_amount
FROM orders
)
SELECT *
FROM with_prev
WHERE amount > prev_amount;
-- โ ์๋ฌ
SELECT
SUM(amount) OVER() AS total,
amount / total AS ratio -- total ์ฌ์ฉ ๋ถ๊ฐ!
FROM orders;
-- โ
CTE ์ฌ์ฉ
WITH totals AS (
SELECT *, SUM(amount) OVER() AS total
FROM orders
)
SELECT *, amount * 1.0 / total AS ratio
FROM totals;
-- โ ์๋ฌ
SELECT *
FROM orders
WHERE ROW_NUMBER() OVER(ORDER BY amount) <= 3;
-- โ
CTE ์ฌ์ฉ
WITH ranked AS (
SELECT *, ROW_NUMBER() OVER(ORDER BY amount) AS rn
FROM orders
)
SELECT * FROM ranked WHERE rn <= 3;
[์์]
ROW_NUMBER: ์์ฐจ ๋ฒํธ
RANK: ๋์ ์ ๊ฐ์ ์์, ๊ฑด๋๋
DENSE_RANK: ๋์ ์ ๊ฐ์ ์์, ์ฐ์
[์ง๊ณ]
SUM/AVG/COUNT OVER(): ๊ทธ๋ฃน ์ ์งํ๋ฉฐ ์ง๊ณ
[๊ฐ ์ฐธ์กฐ]
LAG: ์ด์ ํ
LEAD: ๋ค์ ํ
FIRST_VALUE: ์ฒซ ๋ฒ์งธ ๊ฐ
[๋ฌธ๋ฒ]
OVER(PARTITION BY ๊ทธ๋ฃน ORDER BY ์ ๋ ฌ)
[SQL ์๋ฆฌ์ฆ #5] ์ค์ ํจํด โ ์ฝํ ์์ฃผ ๋์ค๋ ์ ํ ์ด์ ๋ฆฌ
๐ ์๋์ฐ ํจ์๋ ์ฝ๋ฉํ ์คํธ์ ํต์ฌ์ด๋ค.
ROW_NUMBER + CTE ํจํด๋ง ํ์คํ ์์๋ ๋ง์ ๋ฌธ์ ๋ฅผ ํ ์ ์๋ค!