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

أول دالة: CREATE FUNCTION

دالة محفوظة في قاعدة البيانات تستقبل قيمًا وتُرجع نتيجة، وتستدعيها من أي استعلام. ⏱ نحو 40 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تنشئ دالة بمعاملات ونوع إرجاع بـ CREATE FUNCTION.
  • تكتب جسم الدالة بـ plpgsql أو sql.
  • تستدعي الدالة داخل SELECT على كل صف.
  • تعدّل الدالة بـ CREATE OR REPLACE وتحذفها بـ DROP FUNCTION.

١من كتلة مؤقتة إلى دالة محفوظة

كتلة DO تُنفّذ مرة وتختفي. أما الدالة Function فتُحفظ في قاعدة البيانات باسم، وتستقبل معاملات، وتُرجع قيمة، ويستدعيها أي استعلام أو تطبيق في أي وقت.

query.sql
CREATE FUNCTION price_with_vat(p numeric)RETURNS numericLANGUAGE plpgsqlAS $$BEGIN  RETURN round(p * 1.15, 2);END;$$; SELECT name, price, price_with_vat(price) AS بعد_الضريبةFROM products WHERE category_id = 1;
✓ أُنشئت الدالة
النتيجة · 3 صفوف
namepriceبعد_الضريبة
حاسب محمول32003680
جهاز لوحي14501667.5
شاشة 27 بوصة9801127

الدالة تُستدعى داخل SELECT كأي دالة جاهزة مثل round، وتعمل على كل صف.

الجزءمعناه
CREATE FUNCTION price_with_vat(p numeric)اسم الدالة ومعاملها p ونوعه
RETURNS numericنوع القيمة التي تُرجعها
LANGUAGE plpgsqlلغة جسم الدالة
AS $$ … $$جسم الدالة: كتلة BEGIN … END
RETURN …;يُنهي الدالة ويُرجع القيمة

٢دالة تقرأ من الجداول

جسم الدالة كتلة كاملة: فيها متغيرات و SELECT INTO وشروط. هذه دالة تُرجع عدد طلبات عميل:

query.sql
CREATE FUNCTION order_count(cid int)RETURNS intLANGUAGE plpgsqlAS $$DECLARE  n int;BEGIN  SELECT count(*) INTO n FROM orders WHERE customer_id = cid;  RETURN n;END;$$; SELECT id, name, order_count(id) AS الطلباتFROM customers ORDER BY الطلبات DESC, id LIMIT 4;
✓ أُنشئت الدالة
النتيجة · 4 صفوف
idnameالطلبات
1سارة3
2خالد2
3نورة2
4فهد2

وإن كان جسم الدالة استعلامًا واحدًا فقط، فالأبسط أن تكتبها بلغة sql بلا BEGIN و END:

query.sql
CREATE FUNCTION customer_city(cid int) RETURNS textLANGUAGE sqlAS $$  SELECT city FROM customers WHERE id = cid;$$; SELECT customer_city(4);
✓ أُنشئت الدالة
النتيجة · صف واحد
customer_city
الدمام

٣التعديل والحذف

إنشاء دالة بالاسم نفسه مرتين يعطي خطأ. لتعديلها استخدم CREATE OR REPLACE FUNCTION، ولحذفها DROP FUNCTION مع أنواع معاملاتها:

✗ خطأ
query.sql
CREATE FUNCTION price_with_vat(p numeric)RETURNS numericLANGUAGE plpgsqlAS $$BEGIN  RETURN round(p * 1.15, 2);END;$$;CREATE FUNCTION price_with_vat(p numeric)RETURNS numericLANGUAGE plpgsqlAS $$BEGIN  RETURN round(p * 1.20, 2);END;$$;
✓ أُنشئت الدالة
✗ خطأ (السطر 9): الدالة price_with_vat موجودة من قبل؛ استخدم CREATE OR REPLACE
✓ صواب
query.sql
CREATE OR REPLACE FUNCTION price_with_vat(p numeric)RETURNS numericLANGUAGE plpgsqlAS $$BEGIN  RETURN round(p * 1.15, 2);END;$$;CREATE OR REPLACE FUNCTION price_with_vat(p numeric)RETURNS numericLANGUAGE plpgsqlAS $$BEGIN  RETURN round(p * 1.20, 2);END;$$;SELECT price_with_vat(100);
✓ أُنشئت الدالة
✓ أُنشئت الدالة
النتيجة · صف واحد
price_with_vat
120

في PostgreSQL قد توجد دالتان بالاسم نفسه إذا اختلفت أنواع معاملاتهما (التحميل الزائد Overloading)، لذلك نكتب الأنواع عند الحذف: DROP FUNCTION price_with_vat(numeric);

لاحظ في Oracle: RETURN بدل RETURNS، وIS بدل AS $$، وتنتهي بشرطة مائلة / في أداة SQL Developer.

توقّع وتحقّق

ما الفرق الأساسي بين DO والدالة؟

أين تُستدعى الدالة غالبًا؟

لتعديل دالة موجودة دون حذفها نكتب:

متى نكتب الدالة بـ LANGUAGE sql؟

الخلاصة

تحدٍّ برمجي: إجمالي الطلب

متوسط

اكتب دالة order_total(oid int) تُرجع إجمالي طلب: مجموع الكمية × سعر الوحدة لكل بنوده.

المطلوب:
  • الدالة تُرجع numeric.
  • تجمع qty * unit_price من order_items لهذا الطلب.
  • الطلب الذي لا بنود له يُرجع 0 لا NULL (استخدم coalesce).
👀 اعرض الناتج المطلوب
✓ التحقق الآلي من حلّك:
  • الكود يعمل بلا أخطاء
  • الطلب 1 إجماليه 3350
  • الطلب 8 إجماليه 210
  • طلب غير موجود ← 0
  • تعمل على كل الطلبات داخل SELECT

💡 تلميح 1
ابدأ من دالة order_count في الدرس وغيّر الاستعلام.
💡 تلميح 2
coalesce(sum(qty * unit_price), 0) تُرجع 0 إن لم توجد بنود.

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

solution.sql
CREATE OR REPLACE FUNCTION order_total(oid int)RETURNS numericLANGUAGE plpgsqlAS $$DECLARE  t numeric;BEGIN  SELECT coalesce(sum(qty * unit_price), 0) INTO t FROM order_items WHERE order_id = oid;  RETURN t;END;$$;
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

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