معاملات الإجراء: IN و OUT و INOUT
الإجراء لا يُرجع قيمة، لكنه يُخرج ما تريد عبر معاملاته. ⏱ نحو 35 دقيقة.
- تفرّق بين معاملات
INوOUTوINOUT. - تستدعي إجراءً بمعاملات
OUTوتستقبل مخرجاته في متغيرات. - تستخدم القيم الافتراضية وتسمية المعاملات عند الاستدعاء.
- تكتب دالة بمعاملات
OUTتُرجع صفًّا.
١ثلاثة اتجاهات للمعامل
| الاتجاه | البيانات | مثال |
|---|---|---|
IN (الافتراضي) | تدخل إلى الإجراء فقط | رقم العميل |
OUT | تخرج من الإجراء فقط | عدد طلباته |
INOUT | تدخل بقيمة، وتخرج بقيمة جديدة | مبلغ يدخل ويخرج بعد الخصم |
CREATE PROCEDURE customer_stats( IN cid int, OUT n_orders int, OUT spent numeric)LANGUAGE plpgsqlAS $$BEGIN SELECT count(DISTINCT o.id), coalesce(sum(i.qty * i.unit_price), 0) INTO n_orders, spent FROM orders o LEFT JOIN order_items i ON i.order_id = o.id WHERE o.customer_id = cid;END;$$; CALL customer_stats(1, NULL, NULL);
| n_orders | spent |
|---|---|
| 3 | 3805 |
عند الاستدعاء بـ CALL نضع NULL مكان كل معامل OUT، فيُرجع الإجراء صفًّا بقيمها.
٢استقبال المخرجات في متغيرات
داخل كتلة أو إجراء آخر نمرّر متغيرات مكان معاملات OUT، فتمتلئ بعد الاستدعاء:
CREATE PROCEDURE customer_stats( IN cid int, OUT n_orders int, OUT spent numeric)LANGUAGE plpgsqlAS $$BEGIN SELECT count(DISTINCT o.id), coalesce(sum(i.qty * i.unit_price), 0) INTO n_orders, spent FROM orders o LEFT JOIN order_items i ON i.order_id = o.id WHERE o.customer_id = cid;END;$$; DO $$DECLARE n int; s numeric;BEGIN CALL customer_stats(3, n, s); RAISE NOTICE 'نورة: % طلبات بمجموع % ريال', n, s;END $$;
ومعامل INOUT يدخل بقيمة ويعود بأخرى:
CREATE PROCEDURE apply_discount(INOUT amount numeric, IN pct numeric DEFAULT 10)LANGUAGE plpgsqlAS $$BEGIN amount := round(amount * (1 - pct / 100), 2);END;$$; DO $$DECLARE bill numeric := 500;BEGIN CALL apply_discount(bill); -- pct أخذ القيمة الافتراضية 10 RAISE NOTICE 'بعد الخصم الافتراضي: %', bill; CALL apply_discount(bill, pct => 20); -- تسمية المعامل عند الاستدعاء RAISE NOTICE 'بعد خصم 20%% إضافي: %', bill;END $$;
DEFAULT يجعل المعامل اختياريًّا، وpct => 20 يسمّي المعامل فلا يهم ترتيبه. و%% داخل RAISE تطبع علامة % نفسها.
٣الدالة بمعاملات OUT
الدالة أيضًا تقبل OUT، فتُرجع صفًّا بعدة أعمدة دون تعريف نوع خاص:
CREATE FUNCTION price_range(cat int, OUT low numeric, OUT high numeric)LANGUAGE sqlAS $$ SELECT min(price), max(price) FROM products WHERE category_id = cat $$; SELECT c.name, r.low, r.highFROM categories c, price_range(c.id) rORDER BY c.id;
| name | low | high |
|---|---|---|
| أجهزة | 980 | 3200 |
| ملحقات | 75 | 210 |
| كتب | 60 | 85 |
| قرطاسية | 12 | 18 |
| ألعاب | 65 | 120 |
دالة بمعاملات OUT لا تحتاج RETURNS: نوع الإرجاع صف من أعمدة OUT.
لاحظ في Oracle: الاتجاه يُكتب بعد الاسم (b OUT NUMBER)، وفي PostgreSQL و MySQL قبله.
توقّع وتحقّق
معامل يدخل بقيمة ويخرج بقيمة جديدة:
عند CALL مباشرة نضع مكان معاملات OUT:
ما فائدة DEFAULT في المعامل؟
في MySQL تُستقبل مخرجات CALL في:
الخلاصة
INيدخل، وOUTيخرج، وINOUTيدخل ويخرج.CALLمباشرة:NULLمكانOUT. داخل كتلة: متغيرات تستقبل القيم.DEFAULTيجعل المعامل اختياريًّا، وname=> قيمة يسمّيه.- في
Oracleالاتجاه بعد الاسم:b OUT NUMBER.
تحدٍّ برمجي: ملخص الطلب
متوسطاكتب إجراء order_summary يستقبل رقم طلب ويُخرج عدد بنوده وإجماليه.
- المعاملات:
IN oid intوOUT items intوOUT total numeric. - items عدد البنود، و total مجموع
qty * unit_price. - طلب بلا بنود أو غير موجود يُخرج 0 و 0 لا NULL.
👀 اعرض الناتج المطلوب
- الكود يعمل بلا أخطاء
- الطلب 1 ← 2 بندان و 3350
- الطلب 8 ← 3 بنود و 210
- طلب غير موجود ← 0 و 0
- معامل OUT
💡 تلميح 1
💡 تلميح 2
SELECT count(*), coalesce(sum(…), 0) INTO items, total.حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
CREATE PROCEDURE order_summary(IN oid int, OUT items int, OUT total numeric)LANGUAGE plpgsqlAS $$BEGIN SELECT count(*), coalesce(sum(qty * unit_price), 0) INTO items, total FROM order_items WHERE order_id = oid;END;$$;