لغات البرمجة18 نوفمبر 20254 دقائق قراءة
SQL: لغة تجاوزت الخمسين وما زالت ترفض التقاعد
أسامـــة الدروشـــي

لغة SQL أقدم من TCP/IP، وأقدم من أول معيار للغة C، وأقدم من معظم من يكتبونها اليوم. عاصرت قواعد البيانات الكائنية وقواعد بيانات XML ودورة ضجيج كاملة على الأقل أعلنت موتها، ثم شيّعتها جميعًا. وبعد ست سنوات في بناء الأنظمة، منها فترة عمل حر بدأتها عام 2020 واطّلعت خلالها على عدد كبير من مشاريع الآخرين، أصل دائمًا إلى الخلاصة نفسها: أثمن مهارة في عدّتي ما زالت تلك التي وُلدت عام 1974.
كل ORM يتسرب من الموضع نفسه
لأكون واضحًا: أنا أحب أدوات ORM. وفّرت عليّ Hibernate و SQLAlchemy آلاف الأسطر من كود الربط الممل، وعلى مسار الكتابة في تطبيق اعتيادي تظل save() و findById() هما مستوى التجريد الصحيح تمامًا. التسرب لا يحدث هناك أبدًا؛ بل يحدث في اللحظة التي يطرح فيها أحدهم سؤالًا تحليليًا.
«أي المشغّلين هبط دون عتبة الجودة هذا الربع، وكيف يبدو ذلك مقارنةً بالربع نفسه من العام الماضي؟» لا وجود لدالة findQuarterOverQuarterDegradation() في أي واجهة repository. يمكنك تركيب الجواب من أدوات الـ ORM الأولية، لكن ما ستبنيه فعليًا هو حلقة تسحب نصف جدول إلى ذاكرة التطبيق وتعيد تنفيذ ما تجيده قاعدة البيانات أصلًا، لكن بصورة أسوأ. كتبتُ تلك الحلقة بنفسي، ورأيتها تُسقط خدمة كاملة في إقفال نهاية الشهر حين كبرت البيانات بما يكفي.
هذا ليس عيبًا في تصميم الـ ORM؛ بل هو حدّ طبيعي. فالـ ORM يربط الكائنات، أما التحليلات فتدور حول المجموعات والترتيب والنوافذ الزمنية. حين يصبح سؤالك عن شكل البيانات لا عن سجلات مفردة، تكون قد غادرت أرض الـ ORM، وعليك أن تبدّل اللغة.
الاستعلام الذي حلّ محل خدمة كاملة
في Byanat عملت على «مدى»، منصة تتعقّب تدهور الإشارة عبر أبراج Omantel. النسخة الأولى كانت مهمة مجدولة تفعل ما تتوقعه تمامًا: تسحب القياسات الخام إلى Python، وتجمّعها حسب البرج، وتحسب متوسطات متحركة، وتضع علامة على كل برج يتجه نحو الأسوأ. كانت تستغرق قرابة الساعة وتحتاج إلى مراقبة دائمة.
البديل كان استعلامًا واحدًا: CTE يجمّع القياسات باليوم، ثم AVG(metric) OVER (PARTITION BY tower_id ORDER BY day ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) للنافذة المتحركة، ثم LAG() لمقارنة كل برج بتاريخه هو. أنجزت PostgreSQL في ثوانٍ ما كانت المهمة تنجزه في ساعة، فحُذفت المهمة ومعها منطق إعادة المحاولة والمراقبة وتنبيهات الساعة الثانية فجرًا.
الدرس نفسه تكرر في منصة تحليلات M2M لدى Ooredoo؛ نحو 300 ألف شريحة IoT ترسل بيانات استهلاكها. لا تشحن صفوف 300 ألف جهاز عبر الشبكة لتحسب توزيعًا في كود التطبيق؛ بل ترسل السؤال إلى حيث تسكن البيانات: percentile_cont و FILTER (WHERE ...) وجولة واحدة ذهابًا وإيابًا.
أمضت قاعدة البيانات خمسين عامًا تتعلم الإجابة عن الأسئلة حول البيانات؛ أما حلقة for التي كتبتَها فلا.
الدوال النافذية وتعابير CTE هما اللغة الحقيقية
يتعلم معظم المطورين SELECT و JOIN و GROUP BY ثم يتوقفون، وهذا أشبه بحفظ أبجدية لغة ثم إعلان النصر. الأجزاء التي تستحق العناء تبدأ بعد ذلك:
- الدوال النافذية.
ROW_NUMBERوRANKوLAGوLEADوالمجاميع التراكمية عبرOVER. كل ما يتضمن «مقارنة بالفترة السابقة» أو «أفضل N في كل مجموعة» هو دالة نافذية، والبديل الإجرائي أقبح دائمًا. - تعابير CTE. تتيح لك عبارة
WITHتسمية النتائج الوسيطة وقراءة استعلام من مئة سطر من أعلى إلى أسفل كأنه خط أنابيب. حين بنينا «صبر»، منصة التحليلات المركزية لهيئة تنظيم الاتصالات في عُمان، كانت بعض استعلامات لوحات المتابعة طويلة، لكن أحدًا لم يخشها؛ لأن كل CTE كان يُقرأ كدالة ذات اسم واضح. - ملحقات التجميع. يمنحك
FILTER (WHERE ...)عدًّا شرطيًا دون ألاعيب CASE؛ ويمنحكpercentile_contالوسيط محسوبًا حيث تعيش البيانات.
لماذا تفوز قاعدة البيانات دائمًا؟
لسببين مملين. الأول: قرب الحوسبة من البيانات؛ فنقل الحساب إلى البيانات يتفوق على نقل البيانات إلى الحساب في كل مرة، وعند كل حجم بيانات عملتُ معه. والثاني: أن مخطط الاستعلامات ثمرة خمسين عامًا من البحث وملايين البيئات الإنتاجية، بينما حلقة التجميع التي أكتبها بيدي ثمرة sprint واحد.
وهناك سبب ثالث أقل مللًا: SQL لغة تصريحية، والتصريحي يبقى. الأطر التي استخدمتها حين بدأت العمل الحر عام 2020 صارت اليوم إرثًا قديمًا. ونسخ Angular و Spring Boot التي أسلّمها اليوم ستبدو قديمة بعد خمس سنوات. أما استعلام جيد الصياغة كُتب عام 1995 فما زال يعمل، وما زال يُقرأ، وسيجتاز مراجعة الكود اليوم.
لذا نصيحتي بلا بريق: أبقِ الـ ORM لمسار الكتابة، وانتقل إلى SQL في اللحظة التي تتضمن فيها أي قراءة تجميعًا، وامنح الدوال النافذية عطلة نهاية أسبوع كاملة من وقتك. بعد خمسين عامًا، اللغة العجوز لن تذهب إلى أي مكان؛ إنها فقط تنتظر أن تتسرب بقية مكدّسك.