SQL Window Functions: شغل تحليلي بسطر واحد بدل Subquery متشعّب
لو عندك جدول مبيعات وعايز تحسب الإجمالي التراكمي لكل يوم، أو ترتيب أعلى 3 منتجات داخل كل تصنيف، Self Join أو Correlated Subquery هيشتغلوا — بس هيبطّؤوا الاستعلام بشكل ملحوظ على ملايين الصفوف. الـ Window Functions بتحل نفس المشكلة بسطر واحد وبخطة تنفيذ أنضف.
المشكلة باختصار
الـ GROUP BY بيطوي الصفوف في صف واحد لكل مجموعة. لكن أحيانًا محتاج الصف الأصلي يفضل موجود ومعاه قيمة محسوبة على مجموعة حواليه — زي "إجمالي مبيعات هذا الشهر لحد النهارده" أو "ترتيب هذا المنتج داخل تصنيفه". ده بالظبط اللي الـ Window Functions اتعملت عشانه.
يعني إيه Window Function بالظبط؟
دالة تجميع (أو ترتيب) بتتنفذ على "نافذة" من الصفوف المرتبطة بالصف الحالي، بدون ما تدمج الصفوف. الصيغة العامة:
FUNCTION(column) OVER (
PARTITION BY group_column
ORDER BY sort_column
ROWS BETWEEN ... AND ...
)الكلمة المفتاحية هنا OVER. هي اللي بتفرق بين SUM(amount) العادي اللي بيحتاج GROUP BY، وبين SUM(amount) OVER (...) اللي بيسيب كل صف مكانه.
مثال عملي: Running Total لمبيعات يومية
افترض جدول sales(sale_date, amount) فيه مبيعات يوم بيوم. عايز نعرض لكل يوم: المبيعات، والإجمالي التراكمي من أول الشهر.
SELECT
sale_date,
amount,
SUM(amount) OVER (
ORDER BY sale_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total
FROM sales
WHERE sale_date >= DATE_TRUNC('month', CURRENT_DATE)
ORDER BY sale_date;بدون Window Function كان لازم Self Join على نفس الجدول بشرط s2.sale_date <= s1.sale_date، وده بتعقيد O(n²) تقريبًا. على جدول فيه 500 ألف صف، Self Join ممكن ياخد 8–12 ثانية؛ الاستعلام فوق بيخلص في أقل من ثانية على نفس البيانات في PostgreSQL 16 مع فهرس على sale_date (قياس تقريبي على جهاز تطوير 8GB RAM).
PARTITION BY: قسّم الحساب حسب مجموعة
لو عايز الإجمالي التراكمي لكل عميل على حدة، ضيف PARTITION BY: