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

أول إجراء مخزن: CREATE PROCEDURE و CALL

برنامج محفوظ ينفّذ عملًا كاملًا بخطواته عند استدعائه بـ CALL. ⏱ نحو 40 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تنشئ إجراءً مخزنًا بـ CREATE PROCEDURE وتستدعيه بـ CALL.
  • تكتب إجراءً من عدة خطوات يستخدم RETURNING … INTO.
  • تفرّق بين الدالة والإجراء في الغرض والاستدعاء.
  • تستخدم المتغير FOUND لمعرفة نجاح التعديل.

١الإجراء المخزن: أوامر تُنفَّذ باسم

الإجراء المخزن Stored Procedure برنامج محفوظ مثل الدالة، لكن غرضه تنفيذ عمل (إضافة، تعديل، حذف، عدة خطوات) لا إرجاع قيمة. يُستدعى بالأمر CALL:

query.sql
CREATE PROCEDURE restock(p_id int, amount int)LANGUAGE plpgsqlAS $$BEGIN  UPDATE products SET stock = stock + amount WHERE id = p_id;  RAISE NOTICE 'أُضيفت % قطعة للمنتج %', amount, p_id;END;$$; CALL restock(6, 15);SELECT id, name, stock FROM products WHERE id = 6;
✓ أُنشئ الإجراء المخزن
NOTICE: أُضيفت 15 قطعة للمنتج 6
✓ نُفّذ الإجراء
النتيجة · صف واحد
idnamestock
6سماعة رأس15

لا يوجد RETURNS: الإجراء لا يُرجع قيمة. ولا يُستدعى داخل SELECT، بل بـ CALL وحده.

٢إجراء من عدة خطوات

قوة الإجراء أنه يجمع خطوات العمل في مكان واحد. «تسجيل عميل جديد مع أول طلب»: إضافة العميل، ثم أخذ رقمه، ثم إنشاء الطلب. RETURNING … INTO يأخذ الرقم الذي ولّده الجدول:

query.sql
CREATE PROCEDURE new_customer_order(p_name text, p_city text)LANGUAGE plpgsqlAS $$DECLARE  v_cid int;  v_oid int;BEGIN  INSERT INTO customers (name, city, joined)  VALUES (p_name, p_city, current_date)  RETURNING id INTO v_cid;   INSERT INTO orders (customer_id, order_date) VALUES (v_cid, current_date)  RETURNING id INTO v_oid;   RAISE NOTICE 'العميل % برقم %، وطلبه الأول برقم %', p_name, v_cid, v_oid;END;$$; CALL new_customer_order('لمى', 'تبوك');SELECT c.name, o.id AS order_id, o.status FROM customers c JOIN orders o ON o.customer_id = c.id WHERE c.name = 'لمى';
✓ أُنشئ الإجراء المخزن
NOTICE: العميل لمى برقم 9، وطلبه الأول برقم 13
✓ نُفّذ الإجراء
النتيجة · صف واحد
nameorder_idstatus
لمى13new

٣دالة أم إجراء؟

الدالة FUNCTIONالإجراء PROCEDURE
الغرضحساب قيمة وإرجاعهاتنفيذ عمل وتعديل بيانات
الاستدعاءSELECT f(…)CALL p(…);
داخل استعلام؟نعم: في SELECT و WHEREلا
RETURNSإلزاميلا يوجد (يمكن إرجاع قيم بمعاملات OUT، الدرس 17)
المعاملات COMMIT/ROLLBACKلانعم (الدرس 16)
✗ خطأ
query.sql
CREATE PROCEDURE restock(p_id int, amount int)LANGUAGE plpgsqlAS $$BEGIN  UPDATE products SET stock = stock + amount WHERE id = p_id;  RAISE NOTICE 'أُضيفت % قطعة للمنتج %', amount, p_id;END;$$;SELECT restock(1, 5);
✓ أُنشئ الإجراء المخزن
✗ خطأ (السطر 9): restock(integer, integer) إجراء مخزن، يُستدعى بـ CALL لا بـ SELECT
💡 تلميح: To call a procedure, use CALL.
✓ صواب
query.sql
CREATE PROCEDURE restock(p_id int, amount int)LANGUAGE plpgsqlAS $$BEGIN  UPDATE products SET stock = stock + amount WHERE id = p_id;  RAISE NOTICE 'أُضيفت % قطعة للمنتج %', amount, p_id;END;$$;CALL restock(1, 5);
✓ أُنشئ الإجراء المخزن
NOTICE: أُضيفت 5 قطعة للمنتج 1
✓ نُفّذ الإجراء

الإجراء لا يُستدعى بـ SELECT. ورسالة الخطأ تقول ذلك صراحة.

توقّع وتحقّق

كيف يُستدعى الإجراء المخزن في PostgreSQL؟

أي مما يلي يناسب الإجراء أكثر من الدالة؟

ما وظيفة RETURNING id INTO v؟

في MySQL نحتاج عند كتابة الإجراء في الطرفية إلى:

الخلاصة

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

متوسط

اكتب إجراء cancel_order(p_oid int) يلغي الطلب إن كانت حالته new أو paid فقط؛ أما المشحون أو الملغى فلا يتغير.

المطلوب:
  • يحدّث status إلى cancelled بشرط الحالة.
  • يطبع: أُلغي الطلب 6 عند الإلغاء، ولا يمكن إلغاء الطلب 1 إن لم يتغير شيء.
  • يُستدعى بـ CALL.
👀 اعرض الناتج المطلوب
NOTICE: أُلغي الطلب 6
NOTICE: لا يمكن إلغاء الطلب 1
✓ التحقق الآلي من حلّك:
  • الكود يعمل بلا أخطاء
  • يطبع «أُلغي الطلب 6»
  • يطبع «لا يمكن إلغاء الطلب 1»
  • الطلب 6 صار ملغى والطلب 1 بقي مشحونًا
  • الطلب 10 (مدفوع) يُلغى عند استدعائه
  • إجراء لا دالة

💡 تلميح 1
الشرط داخل UPDATE: WHERE id = p_oid AND status IN ('new', 'paid').
💡 تلميح 2
المتغير الجاهز FOUND يصبح true إن عدّل UPDATE صفًّا واحدًا على الأقل.

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

solution.sql
CREATE PROCEDURE cancel_order(p_oid int)LANGUAGE plpgsqlAS $$BEGIN  UPDATE orders SET status = 'cancelled'  WHERE id = p_oid AND status IN ('new', 'paid');  IF FOUND THEN    RAISE NOTICE 'أُلغي الطلب %', p_oid;  ELSE    RAISE NOTICE 'لا يمكن إلغاء الطلب %', p_oid;  END IF;END;$$; CALL cancel_order(6);CALL cancel_order(1);
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

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