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

دوال تُرجع جداول: RETURNS TABLE

دالة كأنها جدول له معاملات: تختار منها وتربطها وتجمّعها. ⏱ نحو 40 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تكتب دالة تُرجع جدولًا بـ RETURNS TABLE و RETURN QUERY.
  • تستخدم RETURNS SETOF و RETURN NEXT.
  • تستدعي الدالة الجدولية في FROM وتربطها بجداول أخرى.
  • تستدعي الدالة لكل صف بـ LATERAL.

١دالة تُرجع جدولًا

الدوال السابقة أرجعت قيمة واحدة أو صفًّا واحدًا. RETURNS TABLE تجعل الدالة تُرجع صفوفًا بأعمدة تحددها، فتستخدمها في FROM كأنها جدول له معاملات:

query.sql
CREATE FUNCTION customers_in(c text)RETURNS TABLE (cid int, cname text, since date)LANGUAGE plpgsqlAS $$BEGIN  RETURN QUERY    SELECT id, name, joined FROM customers WHERE city = c ORDER BY joined;END;$$; SELECT * FROM customers_in('الرياض');
✓ أُنشئت الدالة
النتيجة · 3 صفوف
cidcnamesince
1سارة2025-01-10
3نورة2025-03-15
7هند2025-08-09

RETURN QUERY يضيف نتيجة الاستعلام كلها إلى مخرجات الدالة. وأسماء الأعمدة cid و cname مختلفة عن أعمدة الجدول عمدًا، لأن أعمدة RETURNS TABLE تصبح متغيرات داخل الدالة.

query.sql
CREATE FUNCTION customers_in(c text)RETURNS TABLE (cid int, cname text, since date)LANGUAGE plpgsqlAS $$BEGIN  RETURN QUERY    SELECT id, name, joined FROM customers WHERE city = c ORDER BY joined;END;$$; SELECT r.cname, count(o.id) AS الطلباتFROM customers_in('جدة') r LEFT JOIN orders o ON o.customer_id = r.cidGROUP BY r.cname;
✓ أُنشئت الدالة
النتيجة · صفّان
cnameالطلبات
ريم1
خالد2

تُربط مع جداول أخرى وتُجمَّع مثل أي جدول.

٢SETOF وبناء النتيجة صفًّا صفًّا

إن كانت الصفوف بأعمدة جدول موجود، فاكتب RETURNS SETOF products. وتستطيع بناء النتيجة صفًّا صفًّا بـ RETURN NEXT حين تحتاج منطقًا لكل صف:

query.sql
CREATE FUNCTION low_stock(lim int) RETURNS SETOF productsLANGUAGE sql STABLEAS $$ SELECT * FROM products WHERE stock < lim ORDER BY stock $$; SELECT name, stock FROM low_stock(10);
✓ أُنشئت الدالة
النتيجة · 5 صفوف
namestock
سماعة رأس0
لغز ألف قطعة3
شاشة 27 بوصة4
حاسب محمول7
مكعبات بناء9
query.sql
CREATE FUNCTION price_steps(p numeric, steps int)RETURNS TABLE (step int, price numeric)LANGUAGE plpgsqlAS $$BEGIN  FOR i IN 1..steps LOOP    step  := i;    price := round(p * (1 - 0.1 * i), 2);   -- خصم 10% إضافي في كل خطوة    RETURN NEXT;                            -- أضف الصف الحالي إلى النتيجة  END LOOP;END;$$; SELECT * FROM price_steps(200, 4);
✓ أُنشئت الدالة
النتيجة · 4 صفوف
stepprice
1180
2160
3140
4120

٣الدالة لكل صف: LATERAL

حين تريد استدعاء الدالة الجدولية لكل صف من جدول آخر، تمرر إليها عمودًا منه. هذا ربط LATERAL، وهو ضمني عند وضع الدالة بعد الجدول في FROM:

query.sql
CREATE FUNCTION top_items(oid int, k int)RETURNS TABLE (product text, amount numeric)LANGUAGE sql STABLEAS $$  SELECT p.name, i.qty * i.unit_price  FROM order_items i JOIN products p ON p.id = i.product_id  WHERE i.order_id = oid  ORDER BY 2 DESC LIMIT k$$; SELECT o.id AS الطلب, t.product, t.amountFROM orders o, LATERAL top_items(o.id, 1) tWHERE o.id <= 4 ORDER BY o.id;
✓ أُنشئت الدالة
النتيجة · 4 صفوف
الطلبproductamount
1حاسب محمول3200
2تعلم SQL255
3لوحة مفاتيح140
4جهاز لوحي1450

لكل طلب: أغلى بند فيه.

توقّع وتحقّق

أين تُوضع الدالة الجدولية عادة؟

RETURN NEXT:

لإرجاع صفوف بأعمدة جدول products كاملة:

في Oracle تُستخدم الدالة الجدولية بـ:

الخلاصة

تحدٍّ برمجي: الأكثر مبيعًا في التصنيف

متقدم

اكتب دالة جدولية best_sellers(cat int, k int) تُرجع أكثر k منتجات مبيعًا (بالكميات) في التصنيف.

المطلوب:
  • RETURNS TABLE (product text, units bigint).
  • units مجموع qty من بنود الطلبات لكل منتج في التصنيف.
  • مرتبة تنازليًّا بـ units ثم بالاسم، و k صفوف فقط.
👀 اعرض الناتج المطلوب
✓ التحقق الآلي من حلّك:
  • الكود يعمل بلا أخطاء
  • best_sellers(1, 2)
  • best_sellers(4, 1) ← دفتر ملاحظات 34
  • تصنيف بلا مبيعات ← لا صفوف
  • RETURNS TABLE

💡 تلميح 1
داخل الدالة: RETURN QUERY SELECT … GROUP BY … ORDER BY 2 DESC, 1 LIMIT k;
💡 تلميح 2
الترقيم 2 و 1 في ORDER BY يتجنب غموض الأسماء مع أعمدة RETURNS TABLE.

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

solution.sql
CREATE FUNCTION best_sellers(cat int, k int)RETURNS TABLE (product text, units bigint)LANGUAGE plpgsqlAS $$BEGIN  RETURN QUERY    SELECT p.name, sum(i.qty)    FROM products p JOIN order_items i ON i.product_id = p.id    WHERE p.category_id = cat    GROUP BY p.name    ORDER BY 2 DESC, 1    LIMIT k;END;$$;
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

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