دوال تُرجع جداول: RETURNS TABLE
دالة كأنها جدول له معاملات: تختار منها وتربطها وتجمّعها. ⏱ نحو 40 دقيقة.
- تكتب دالة تُرجع جدولًا بـ
RETURNS TABLEوRETURN QUERY. - تستخدم
RETURNS SETOFوRETURN NEXT. - تستدعي الدالة الجدولية في
FROMوتربطها بجداول أخرى. - تستدعي الدالة لكل صف بـ
LATERAL.
١دالة تُرجع جدولًا
الدوال السابقة أرجعت قيمة واحدة أو صفًّا واحدًا. RETURNS TABLE تجعل الدالة تُرجع صفوفًا بأعمدة تحددها، فتستخدمها في FROM كأنها جدول له معاملات:
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('الرياض');
| cid | cname | since |
|---|---|---|
| 1 | سارة | 2025-01-10 |
| 3 | نورة | 2025-03-15 |
| 7 | هند | 2025-08-09 |
RETURN QUERY يضيف نتيجة الاستعلام كلها إلى مخرجات الدالة. وأسماء الأعمدة cid و cname مختلفة عن أعمدة الجدول عمدًا، لأن أعمدة RETURNS TABLE تصبح متغيرات داخل الدالة.
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 حين تحتاج منطقًا لكل صف:
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);
| name | stock |
|---|---|
| سماعة رأس | 0 |
| لغز ألف قطعة | 3 |
| شاشة 27 بوصة | 4 |
| حاسب محمول | 7 |
| مكعبات بناء | 9 |
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);
| step | price |
|---|---|
| 1 | 180 |
| 2 | 160 |
| 3 | 140 |
| 4 | 120 |
٣الدالة لكل صف: LATERAL
حين تريد استدعاء الدالة الجدولية لكل صف من جدول آخر، تمرر إليها عمودًا منه. هذا ربط LATERAL، وهو ضمني عند وضع الدالة بعد الجدول في FROM:
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;
| الطلب | product | amount |
|---|---|---|
| 1 | حاسب محمول | 3200 |
| 2 | تعلم SQL | 255 |
| 3 | لوحة مفاتيح | 140 |
| 4 | جهاز لوحي | 1450 |
لكل طلب: أغلى بند فيه.
توقّع وتحقّق
أين تُوضع الدالة الجدولية عادة؟
RETURN NEXT:
لإرجاع صفوف بأعمدة جدول products كاملة:
في Oracle تُستخدم الدالة الجدولية بـ:
الخلاصة
RETURNS TABLE(أعمدة) +RETURN QUERY SELECT…SETOFجدول لإرجاع صفوف بأعمدة جدول موجود.RETURN NEXTيبني النتيجة صفًّا صفًّا.- تُستدعى في
FROM، وLATERALلاستدعائها لكل صف.
تحدٍّ برمجي: الأكثر مبيعًا في التصنيف
متقدماكتب دالة جدولية 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
حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
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;$$;