أول إجراء مخزن: CREATE PROCEDURE و CALL
برنامج محفوظ ينفّذ عملًا كاملًا بخطواته عند استدعائه بـ CALL. ⏱ نحو 40 دقيقة.
- تنشئ إجراءً مخزنًا بـ
CREATE PROCEDUREوتستدعيه بـCALL. - تكتب إجراءً من عدة خطوات يستخدم
RETURNING…INTO. - تفرّق بين الدالة والإجراء في الغرض والاستدعاء.
- تستخدم المتغير
FOUNDلمعرفة نجاح التعديل.
١الإجراء المخزن: أوامر تُنفَّذ باسم
الإجراء المخزن Stored Procedure برنامج محفوظ مثل الدالة، لكن غرضه تنفيذ عمل (إضافة، تعديل، حذف، عدة خطوات) لا إرجاع قيمة. يُستدعى بالأمر CALL:
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;
| id | name | stock |
|---|---|---|
| 6 | سماعة رأس | 15 |
لا يوجد RETURNS: الإجراء لا يُرجع قيمة. ولا يُستدعى داخل SELECT، بل بـ CALL وحده.
٢إجراء من عدة خطوات
قوة الإجراء أنه يجمع خطوات العمل في مكان واحد. «تسجيل عميل جديد مع أول طلب»: إضافة العميل، ثم أخذ رقمه، ثم إنشاء الطلب. RETURNING … INTO يأخذ الرقم الذي ولّده الجدول:
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 = 'لمى';
| name | order_id | status |
|---|---|---|
| لمى | 13 | new |
٣دالة أم إجراء؟
| الدالة FUNCTION | الإجراء PROCEDURE | |
|---|---|---|
| الغرض | حساب قيمة وإرجاعها | تنفيذ عمل وتعديل بيانات |
| الاستدعاء | SELECT f(…) | CALL p(…); |
| داخل استعلام؟ | نعم: في SELECT و WHERE | لا |
| RETURNS | إلزامي | لا يوجد (يمكن إرجاع قيم بمعاملات OUT، الدرس 17) |
المعاملات COMMIT/ROLLBACK | لا | نعم (الدرس 16) |
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);
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);
الإجراء لا يُستدعى بـ SELECT. ورسالة الخطأ تقول ذلك صراحة.
توقّع وتحقّق
كيف يُستدعى الإجراء المخزن في PostgreSQL؟
أي مما يلي يناسب الإجراء أكثر من الدالة؟
ما وظيفة RETURNING id INTO v؟
في MySQL نحتاج عند كتابة الإجراء في الطرفية إلى:
الخلاصة
- الإجراء ينفّذ عملًا ويُستدعى بـ
CALL، ولاRETURNSله. - يجمع خطوات العمل (عدة
INSERTوUPDATE) في مكان واحد. RETURNING id INTO vيأخذ المعرّف المولَّد.- الدالة للحساب داخل الاستعلامات، والإجراء للأفعال.
FOUND= هل أثّر آخر أمر في صف؟
تحدٍّ برمجي: إلغاء طلب
متوسطاكتب إجراء cancel_order(p_oid int) يلغي الطلب إن كانت حالته new أو paid فقط؛ أما المشحون أو الملغى فلا يتغير.
- يحدّث
statusإلىcancelledبشرط الحالة. - يطبع:
أُلغي الطلب 6عند الإلغاء، ولا يمكن إلغاء الطلب 1إن لم يتغير شيء. - يُستدعى بـ CALL.
👀 اعرض الناتج المطلوب
- الكود يعمل بلا أخطاء
- يطبع «أُلغي الطلب 6»
- يطبع «لا يمكن إلغاء الطلب 1»
- الطلب 6 صار ملغى والطلب 1 بقي مشحونًا
- الطلب 10 (مدفوع) يُلغى عند استدعائه
- إجراء لا دالة
💡 تلميح 1
WHERE id = p_oid AND status IN ('new', 'paid').💡 تلميح 2
FOUND يصبح true إن عدّل UPDATE صفًّا واحدًا على الأقل.حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
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);