الدالة أم الإجراء؟ الفوائد والاستخدامات
متى تضع المنطق في قاعدة البيانات، وبأي شكل: دالة أم إجراء؟ ⏱ نحو 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 فقط، لأن الفهرس يحفظ النتيجة ويثق أنها لن تتغير:
CREATE FUNCTION clean_name(t text) RETURNS textLANGUAGE sqlAS $$ SELECT lower(trim(t)) $$; CREATE INDEX ix_cust_clean ON customers (clean_name(name));
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:
الخلاصة
- البرامج المخزنة: طلبات أقل، وقاعدة واحدة لكل التطبيقات، وصلاحيات أدق.
- قيمة تُستخدم في استعلام ← دالة؛ عملية تغيّر البيانات ← إجراء.
IMMUTABLEثابتة دائمًا، وSTABLEثابتة في الاستعلام، وVOLATILE(الافتراضي) متغيرة.- الفهرس على دالة يقبل
IMMUTABLEفقط.
تحدٍّ برمجي: بريد لا يتكرر بأي كتابة
متوسطالعملاء يكتبون بريدهم بأحرف كبيرة أو بمسافات زائدة، فيتكرر البريد نفسه بأشكال مختلفة. اكتب دالة توحّد البريد، واستخدمها في فهرس فريد يمنع التكرار.
- دالة
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));حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
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));