طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 122 · برمجة قواعد البيانات
الدرس 15 · الوحدة 7: مفاهيم الإجراءات المخزنة والدوال

الدالة أم الإجراء؟ الفوائد والاستخدامات

متى تضع المنطق في قاعدة البيانات، وبأي شكل: دالة أم إجراء؟ ⏱ نحو 35 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تشرح فوائد البرامج المخزنة وعيوبها.
  • تختار بين الدالة والإجراء حسب المهمة.
  • تحدد فئة التقلّب الصحيحة للدالة.
  • تستخدم دالة IMMUTABLE في فهرس.

١لماذا نضع المنطق داخل قاعدة البيانات؟

عرفت في الدرسين 3 و 4 كيف تكتب دالة وإجراءً. السؤال المهم في العمل: متى يستحق المنطق أن يسكن في قاعدة البيانات بدل التطبيق؟

الفائدةالمعنىمثال
أقل ذهابًا وإيابًاخطوات كثيرة تُنفَّذ قرب البيانات في طلب واحدإتمام طلب: فحص المخزون، وإنشاء الطلب، والخصم
قاعدة واحدة للجميعتطبيق الجوال والموقع ولوحة الإدارة يستدعون الإجراء نفسهحساب الضريبة والخصم
الأمانتمنح صلاحية تنفيذ الإجراء لا صلاحية الجداولالكاشير يستدعي «بيع» ولا يعدّل الأسعار
الأداءخطة التنفيذ تُحفظ وتُعادإجراء يُستدعى آلاف المرات يوميًّا
العيبكيف نخففه
صعوبة الاختبار وتتبع الإصداراتاحفظ ملفات CREATE OR REPLACE في Git مثل أي كود
الارتباط بمنتج قاعدة البياناتPL/pgSQL و PL/SQL متقاربتان، لكن النقل يحتاج تعديلًا (لذلك مربعات «المقابل»)
تضخّم منطق العرض داخل القاعدةضع في القاعدة قواعد البيانات فقط، لا تنسيق الواجهة

٢المقارنة التفصيلية

الدالة FUNCTIONالإجراء PROCEDURE
الاستدعاءSELECT f(…) أو داخل أي تعبيرCALL p(…)
ترجع قيمةنعم (RETURNS): قيمة أو صفًّا أو جدولًالا؛ تُخرج نتائجها بمعاملات OUT (الدرس 17)
داخل SELECT و WHERE والفهرسنعملا
COMMIT و ROLLBACK بداخلهالا: تعمل داخل معاملة من استدعاهانعم (الدرس 16)
الاستخدام المعتادحساب وتحويل وقراءةعملية تغيّر البيانات من عدة خطوات

قاعدة عملية: إن كان الجواب «قيمة أستخدمها في استعلام» فهي دالة، وإن كان «عملية أنفّذها» فهي إجراء.

٣فئة التقلّب: هل النتيجة ثابتة؟

تخبر PostgreSQL عن سلوك دالتك بإحدى ثلاث كلمات، فيستفيد منها المخطِّط:

الفئةالمعنىمثال
IMMUTABLEالمدخلات نفسها تعطي النتيجة نفسها دائمًا، ولا تقرأ الجداولlower(text)، حساب الضريبة بنسبة ثابتة
STABLEثابتة خلال الاستعلام الواحد، وقد تقرأ الجداولnow()، سعر منتج من جدول
VOLATILE (الافتراضي)قد تتغير في كل استدعاء أو تغيّر البياناتrandom()، دالة فيها INSERT

الفرق يظهر بوضوح في الفهارس: الفهرس على تعبير يقبل الدوال IMMUTABLE فقط، لأن الفهرس يحفظ النتيجة ويثق أنها لن تتغير:

✗ خطأ
query.sql
CREATE FUNCTION clean_name(t text) RETURNS textLANGUAGE sqlAS $$ SELECT lower(trim(t)) $$; CREATE INDEX ix_cust_clean ON customers (clean_name(name));
✓ أُنشئت الدالة
✓ أُنشئ الفهرس
✓ صواب
query.sql
CREATE FUNCTION clean_name(t text) RETURNS textLANGUAGE sql IMMUTABLEAS $$ SELECT lower(trim(t)) $$; CREATE INDEX ix_cust_clean ON customers (clean_name(name));SELECT name FROM customers WHERE clean_name(name) = clean_name('  سارة ');
✓ أُنشئت الدالة
✓ أُنشئ الفهرس
النتيجة · صف واحد
name
سارة

الدالة نفسها، أضفنا كلمة واحدة: IMMUTABLE.

توقّع وتحقّق

حساب «السعر بعد الخصم» لكل منتج في تقرير يناسبه:

«إتمام طلب» يفحص ويُدخل ويخصم المخزون يناسبه:

الفئة الافتراضية للدالة:

مقابل IMMUTABLE في Oracle و MySQL:

الخلاصة

تحدٍّ برمجي: بريد لا يتكرر بأي كتابة

متوسط

العملاء يكتبون بريدهم بأحرف كبيرة أو بمسافات زائدة، فيتكرر البريد نفسه بأشكال مختلفة. اكتب دالة توحّد البريد، واستخدمها في فهرس فريد يمنع التكرار.

المطلوب:
  • دالة norm_email(e text) تُرجع البريد بأحرف صغيرة وبلا مسافات في طرفيه.
  • الدالة IMMUTABLE.
  • فهرس فريد على customers (norm_email(email)).
👀 اعرض الناتج المطلوب
✓ التحقق الآلي من حلّك:
  • الكود يعمل بلا أخطاء
  • norm_email(' Sara@Mail.SA ') ← sara@mail.sa
  • الدالة IMMUTABLE
  • بريد سارة بأحرف كبيرة ومسافة يُرفض
  • بريد جديد يُقبل

💡 تلميح 1
جسم الدالة: SELECT lower(trim(e)).
💡 تلميح 2
الفهرس الفريد على تعبير: CREATE UNIQUE INDEX … ON customers (norm_email(email));

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

solution.sql
CREATE FUNCTION norm_email(e text) RETURNS textLANGUAGE sql IMMUTABLEAS $$ SELECT lower(trim(e)) $$; CREATE UNIQUE INDEX ux_customers_email ON customers (norm_email(email));
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس