MariaDB / المتوسط: التزامن وSQL القابلة لإعادة الاستخدام / الدرس 10
الدوال النافذية والترتيب
أضف ترتيباً ومجاميع تراكمية لكل صف دون دمج التفاصيل، وحدد التقسيم والترتيب وإطار الحساب صراحة.
في هذه الصفحة
أهداف الدرس
- تمييز التجميع عن النوافذ التحليلية.
- حساب مجاميع تراكمية وصفوف أعلى بترتيب حتمي.
احتفظ بالصف مع حساب سياقه
تدمج GROUP BY الصفوف إلى صف لكل مجموعة، بينما تحتفظ النافذة بالتفاصيل وتحسب عبر تقسيم مرتبط. تتوفر الدوال النافذية في MariaDB منذ سلسلة 10.2، لكن تفاصيل الإطارات والدوال تحتاج فحص الإصدار. تحدد PARTITION BY المجموعات المستقلة، ويحدد ORDER BY داخل النافذة التسلسل التحليلي، ويحدد ORDER BY الخارجي العرض. هي قرارات منفصلة حتى لو استخدمت الأعمدة نفسها.
استخدم إطار صفوف صريحاً
للمسحات الاصطناعية معرفات فريدة ومقادير 5 و8 و2. يجمع إطار ROWS الصريح من أول صف مرتب إلى الحالي، فتكون النتائج 5 و13 و15. أما SUM(amount) OVER () فتعرض 15 في كل صف. إذا تعادلت الأوقات فأضف معرف حدث فريداً عندما يلزم تسلسل حدثي. لا تدع تجميع الأقران العرضي يقرر معنى الرصيد التراكمي.
SELECT scan_id, amount,
SUM(amount) OVER (ORDER BY scan_id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running
FROM (SELECT 1 AS scan_id, 5 AS amount
UNION ALL SELECT 2, 8
UNION ALL SELECT 3, 2) AS scans
ORDER BY scan_id;
scan_id | amount | running
1 | 5 | 5
2 | 8 | 13
3 | 2 | 15
ينتج إطار ROWS الصريح قيمة تراكمية لكل صف تفصيلي محفوظ.
اختر سياسة التعادل قبل الدالة
تعطي ROW_NUMBER أرقاماً مختلفة للقيم المتعادلة، ولا يكون الإسناد حتمياً دون كسر التعادل. تعطي RANK رتبة واحدة للتعادل وتترك فجوات، بينما لا تترك DENSE_RANK فجوات. للرسوم 30 و30 و20 تنازلياً تكون RANK هي 1 و1 و3 وDENSE_RANK هي 1 و1 و2. لذا يختلف أعلى قيمتي رتبة عن صفين بالضبط.
صفِّ بعد حساب النافذة
لا تتاح نتيجة النافذة لـ WHERE في مرحلة SELECT نفسها. احسبها داخل جدول مشتق أو CTE ثم صفها خارجياً. يرتب المثال حجوزات كل عميل ويختار أعلى رسم مع booking_id لكسر التعادل. يحتاج عينة bookings، ولا نحدد صفوفاً فعلية. يوضح النمط أن rn = 1 تختار صفاً واحداً بالضبط لكل تقسيم عميل غير فارغ.
WITH ranked AS (
SELECT booking_id, customer_id, fee,
ROW_NUMBER() OVER (PARTITION BY customer_id
ORDER BY fee DESC, booking_id) AS rn
FROM bookings
)
SELECT booking_id, customer_id, fee FROM ranked
WHERE rn = 1 ORDER BY customer_id;
تأتي التصفية خارج حساب النافذة، ويظل ORDER BY الخارجي مسؤولاً عن العرض.
افحص الحدود وكلفة التنفيذ
تقرأ LAG صفاً سابقاً وفق ترتيب التقسيم، وتكون نتيجة الصف الأول عادة NULL ما لم يُحدد بديل مناسب. تختلف هذه NULL عن قياس سابق صفري حقيقي. قد تفرز النافذة صفوفاً كثيرة رغم صغر التقرير النهائي، لذا افحص الخطط وقيد المصدر. تدعم MariaDB جزءاً من معيار النوافذ؛ لا تفترض تطابق RANGE وترتيب NULL واستبعاد الإطار مع MySQL أو غيرها.
الأخطاء الشائعة وإصلاحها
ERROR 1054: Unknown column rn in WHERE
- لماذا يحدث؟
- استُخدم اسم النافذة قبل مرحلة حسابه.
- كيف تصلحه؟
- ضع الحساب داخل CTE أو جدول مشتق وصفِّ خارجياً.
- كيف تتجنب تكراره؟
- حدد مراحل الاستعلام عند إضافة تصفية تحليلية.
يقفز المجموع التراكمي عند تعادل الأوقات
- لماذا يحدث؟
- لم يطابق سلوك الأقران أو الإطار الضمني متطلب حساب كل حدث.
- كيف تصلحه؟
- استخدم ROWS صريحة ومفتاح ترتيب فريداً مستقراً.
- كيف تتجنب تكراره؟
- ضمّن تعادلات في العينة وحدد ترتيبها التجاري.
يحوي تقرير أعلى صفين ثلاثة صفوف
- لماذا يحدث؟
- اختلطت سياسة الرتبة مع عدد صفوف ثابت.
- كيف تصلحه؟
- اختر ROW_NUMBER لصفين بالضبط أو وثق التعادلات مع RANK أو DENSE_RANK.
- كيف تتجنب تكراره؟
- حدد إدراج التعادلات قبل اختيار دالة الرتب.
اختبر فهمك
1. الرسوم 50 و50 و40. قارن RANK <= 2 وROW_NUMBER <= 2 مع كاسر تعادل فريد.
اعرض الحل المشروح
تعيدان صفين في هذه العينة مصادفة، لكن المعنى مختلف. تعطي RANK القيم 1 و1 و3 وتحفظ التعادل، وتعطي ROW_NUMBER الأرقام 1 و2 و3 فتختار صفين بحسب كاسر التعادل. تعيد DENSE_RANK <= 2 الثلاثة. أضف رسوماً عليا متعادلة لإظهار الفرق.
2. يحسب تقرير حساس current_value - LAG(current_value). ماذا يعني الصف الأول؟
اعرض الحل المشروح
لا يملك ملاحظة سابقة داخل التقسيم، فيبقى الفرق عادة NULL ويُعرض كغير متاح. استبدال السابق المفقود بصفر يختلق تغيراً من الصفر. قسم حسب الحساس ورتب بتسلسل أحداث مستقر وحدد معنى الفجوات والأوقات المتكررة.
أهم ما تعلمته
- تحفظ النافذة التفاصيل وتضيف سياقاً لكل صف.
- قواعد التعادل والإطارات جزء من تعريف النتيجة.