MySQL / التطبيقات الموثوقة والاستعلامات التحليلية / الدرس 11
دوال النوافذ والأطر التحليلية
احسب الرتب والقيم التراكمية مع إبقاء صفوف الطلبات الفردية.
في هذه الصفحة
أهداف الدرس
- ميّز التجميع من حسابات النوافذ.
- حدد رتبا حتمية وأقساماً وأطراً صريحة.
احتفظ بالتفاصيل واحسب سياقها
استخدم الطلبات الثلاثة الأصلية. تختزل GROUP BY الصفوف إلى مجموعات، بينما تحسب دالة النافذة عبر صفوف مرتبطة وتبقي كل صف. تحدد OVER المجموعة المرتبطة وتعيد PARTITION BY الحساب لكل عميل؛ وبدونها يشكل الجميع قسماً واحداً. تتم WHERE قبل تقييم النوافذ، فتغير تصفية المدفوع مجتمع التحليل.
رتّب الرتب مع تعادل صريح
تسند ROW_NUMBER تسلسلاً وتترك RANK فجوات بعد التعادل ولا تتركها DENSE_RANK. إن رتبت ROW_NUMBER بوقت غير فريد تصبح رتب المتعادلين غير ثابتة. أضف id فريداً عند الحاجة لفائز واحد. لكن إضافته إلى RANK تلغي تعادل الأوقات وقد تغير المعنى التجاري؛ فطريقة التعادل تتبع السؤال.
WITH ranked AS (
SELECT id,customer_id,total,
ROW_NUMBER() OVER (PARTITION BY customer_id
ORDER BY placed_at DESC,id DESC) AS rn
FROM orders
)
SELECT id,customer_id,total FROM ranked WHERE rn=1 ORDER BY customer_id;
id | customer_id | total
102 | 1 | 30.00
103 | 2 | 10.00
أحدث طلب للعميل 1 هو 102 بإجمالي 30.00 وللعميل 2 هو 103 بإجمالي 10.00. لا تملك Noor طلباً مدخلاً فلا قسم لها. لإبقائها اربط النتيجة بالعملاء ربطاً أيسر. تصفي العبارة الخارجية رقم الصف المحسوب سابقاً.
القسم يختلف عن الإطار
يختار الإطار صفوفاً نسبة إلى الصف الحالي داخل القسم. مع ORDER BY للنافذة يكون الافتراضي RANGE من بداية القسم حتى مجموعة نظراء الصف الحالي. تشكل قيم الترتيب المتساوية نظراء قد يدخلون SUM التراكمية معاً. ويوفر إطار ROWS الصريح مع ترتيب فريد تراكمًا صفاً بعد صف. ويهم الإطار خصوصاً مع LAST_VALUE التي قد تعيد قيمة نهاية الإطار الحالي.
SELECT id,customer_id,total,
SUM(total) OVER (PARTITION BY customer_id ORDER BY placed_at,id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
FROM orders ORDER BY customer_id,placed_at,id;
id | customer_id | total | running_total
101 | 1 | 25.00 | 25.00
102 | 1 | 30.00 | 55.00
103 | 2 | 10.00 | 10.00
يبلغ أول صف لـ Amina مقدار 25.00 والثاني 55.00، ويبدأ قسم Leo المستقل بـ 10.00. يحدد ترتيب النافذة تسلسل الحساب، ويحدد ORDER BY الأخير العرض. كُتبا منفصلين لأن النافذة لا تضمن ترتيب المخرجات.
صفِّ في استعلام خارجي
لا تستخدم نتائج النافذة مباشرة في WHERE بالمستوى نفسه، ولا يوفر MySQL8.4 عبارة QUALIFY. استخدم CTE أوجدولاً مشتقاً كما في مثال الرتب. قد يساعد الفهرس على الوصول أوالترتيب لكنه لا يلغي كل فرز أوعمل مؤقت. افحص الخطة والذاكرة بحجم ممثل؛ فوضوح التحليل لا يثبت انخفاض تكلفة التنفيذ.
الأخطاء الشائعة وإصلاحها
ERROR 3593 (HY000): You cannot use the window function 'row_number' in this context.
- لماذا يحدث؟
- وضعت ROW_NUMBER() مباشرة في WHERE.
- كيف تصلحه؟
- احسبها في CTE وصفِّ SELECT الخارجية.
- كيف تتجنب تكراره؟
- افصل مجتمع المدخل عن الاختيار بعد النافذة.
تقفز الإجماليات التراكمية عند الأوقات المتساوية.
- لماذا يحدث؟
- يضم إطار RANGE الافتراضي النظراء معاً.
- كيف تصلحه؟
- استخدم ROWS صريحة وترتيباً فريداً مبرراً للتراكم الفردي.
- كيف تتجنب تكراره؟
- حدد معنى الإطار كلما كانت التراكمات مهمة.
يتغير أحدث طلب بين مرات التشغيل.
- لماذا يحدث؟
- استخدمت ROW_NUMBER ترتيباً غير فريد دون حسم تعادل.
- كيف تصلحه؟
- أضف id DESC بعد الوقت المقصود.
- كيف تتجنب تكراره؟
- حدد في التقرير طريقة حسم الأحداث المتزامنة.
اختبر فهمك
1. احسب فرق كل طلب عن سابقه لكل عميل.
اعرض الحل المشروح
استخدم total - LAG(total) OVER (PARTITION BY customer_id ORDER BY placed_at,id). يعطي 101 قيمة NULL و 102 قيمة 5.00 و 103 قيمة NULL. تعني NULL غياب الطلب السابق داخل القسم وليست فشلاً حسابياً أوفرقاً صفرياً.
2. كيف تؤثر تصفية المدفوع أولاً في إجمالي Amina التراكمي؟
اعرض الحل المشروح
مع WHERE status='paid' في مدخل النافذة يبقى 101 فقط لـ Amina ويبلغ التراكم 25.00. أما الحساب على الجميع ثم التصفية فقد يحفظ مساهمات صفوف لا تعرضها النتيجة. لذا يعبر الشكلان عن مجتمعين تحليليين مختلفين.
أهم ما تعلمته
- ميّز الصفوف الداخلة والأقسام والأطر وترتيب العرض.
- استخدم استعلاماً خارجياً لتصفية نتائج النوافذ في MySQL8.4.