LAG(col, n) lấy giá trị của dòng trước trong cùng partition, LEAD(col, n) lấy dòng sau.
Nhờ đó so sánh kỳ này với kỳ trước mà không cần self join.
sql
WITH monthly AS (
SELECT date_trunc('month', created_at) AS month,
SUM(total) AS revenue
FROM orders
WHERE status = 'paid'
GROUP BY 1
)
SELECT
month,
revenue,
LAG(revenue) OVER (ORDER BY month) AS prev_revenue,
ROUND(
100.0 * (revenue - LAG(revenue) OVER (ORDER BY month))
/ NULLIF(LAG(revenue) OVER (ORDER BY month), 0), 2
) AS growth_pct
FROM monthly
ORDER BY month;Ba điểm hay bị hỏi thêm:
- Dòng đầu tiên không có dòng trước → LAG trả về NULL, nên growth_pct là NULL. Muốn hiện 0 thì LAG(revenue, 1, 0) (tham số thứ ba là giá trị mặc định).
- Chia cho tháng trước bằng 0 sẽ lỗi → bọc NULLIF(..., 0).
- Tính theo từng nhóm (mỗi cửa hàng) thì thêm PARTITION BY store_id vào OVER.
So với cách self join m1.month = m2.month + interval '1 month', window function chỉ quét bảng một lần và tự xử lý biên.