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

الحلقات على النتائج والمؤشرات

حين تحتاج معالجة كل صف بمنطق خاص: الحلقة والمؤشر. ⏱ نحو 40 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تمر على نتيجة استعلام بحلقة FOR … IN SELECT.
  • تستخدم record و %ROWTYPE.
  • تستخدم المؤشر الصريح: OPEN و FETCH و CLOSE.
  • تعرّف مؤشرًا بمعاملات.

١المرور على نتيجة استعلام: FOR … IN SELECT

عرفت حلقة FOR i IN 1..10. والأكثر استخدامًا في قواعد البيانات حلقة تمرّ على صفوف استعلام، صفًّا صفًّا، في متغير من نوع record:

query.sql
DO $$DECLARE  r record;BEGIN  FOR r IN SELECT name, stock FROM products WHERE stock < 10 ORDER BY stock LOOP    IF r.stock = 0 THEN      RAISE NOTICE '% : نفد — اطلب فورًا', r.name;    ELSE      RAISE NOTICE '% : باقٍ %', r.name, r.stock;    END IF;  END LOOP;END $$;
NOTICE: سماعة رأس : نفد — اطلب فورًا
NOTICE: لغز ألف قطعة : باقٍ 3
NOTICE: شاشة 27 بوصة : باقٍ 4
NOTICE: حاسب محمول : باقٍ 7
NOTICE: مكعبات بناء : باقٍ 9
✓ نُفّذت الكتلة

r.name و r.stock: أعمدة الصف الحالي.

وإن كان الصف من جدول معروف، فالنوع جدول%ROWTYPE يحمل كل أعمدته بأنواعها:

query.sql
DO $$DECLARE  c customers%ROWTYPE;BEGIN  SELECT * INTO c FROM customers WHERE id = 3;  RAISE NOTICE 'العميلة % من % انضمت في %', c.name, c.city, c.joined;END $$;
NOTICE: العميلة نورة من الرياض انضمت في 2025-03-15
✓ نُفّذت الكتلة

٢المؤشر الصريح: تحكم كامل

حلقة FOR تفتح مؤشرًا Cursor وتغلقه تلقائيًّا. وحين تحتاج التحكم بنفسك (جلب بعض الصفوف، أو التوقف بشرط، أو تمرير المؤشر) تستخدم المؤشر الصريح بأربع خطوات:

الخطوةالأمر
1. التعريفcur CURSOR FOR SELECT …; في DECLARE
2. الفتحOPEN cur;
3. الجلبFETCH cur INTO متغير; ثم EXIT WHEN NOT FOUND;
4. الإغلاقCLOSE cur;
query.sql
DO $$DECLARE  cur CURSOR FOR SELECT name, price FROM products ORDER BY price DESC;  n   text;  p   numeric;  budget numeric := 5000;BEGIN  OPEN cur;  LOOP    FETCH cur INTO n, p;    EXIT WHEN NOT FOUND;          -- انتهت الصفوف    EXIT WHEN p > budget;         -- أو تجاوزنا الميزانية    budget := budget - p;    RAISE NOTICE 'اشترِ % بـ % (يبقى %)', n, p, budget;  END LOOP;  CLOSE cur;END $$;
NOTICE: اشترِ حاسب محمول بـ 3200.00 (يبقى 1800.00)
NOTICE: اشترِ جهاز لوحي بـ 1450.00 (يبقى 350.00)
✓ نُفّذت الكتلة

توقفنا عند أول منتج لا تكفيه الميزانية، دون قراءة بقية الصفوف.

٣مؤشر بمعاملات

المؤشر يقبل معاملات، فتفتحه بقيم مختلفة كل مرة:

query.sql
DO $$DECLARE  by_city CURSOR (c text) FOR SELECT name FROM customers WHERE city = c ORDER BY name;  r record;BEGIN  FOR r IN by_city('جدة') LOOP    RAISE NOTICE 'جدة: %', r.name;  END LOOP;  FOR r IN by_city('الرياض') LOOP    RAISE NOTICE 'الرياض: %', r.name;  END LOOP;END $$;
NOTICE: جدة: خالد
NOTICE: جدة: ريم
NOTICE: الرياض: سارة
NOTICE: الرياض: نورة
NOTICE: الرياض: هند
✓ نُفّذت الكتلة

توقّع وتحقّق

متغير يحمل صفًّا من أي استعلام:

بعد FETCH نعرف انتهاء الصفوف بـ:

في Oracle يُفحص انتهاء المؤشر بـ:

متى نفضّل المؤشر الصريح على FOR؟

الخلاصة

تحدٍّ برمجي: تقرير المدن بمؤشر

متوسط

اكتب كتلة DO تطبع لكل مدينة عدد عملائها، مرتبة بالعدد تنازليًّا ثم باسم المدينة، باستخدام مؤشر صريح.

المطلوب:
  • مؤشر على SELECT city, count(*) … GROUP BY city ORDER BY 2 DESC, 1.
  • OPEN ثم حلقة FETCH مع EXIT WHEN NOT FOUND ثم CLOSE.
  • كل سطر بالشكل: الرياض: 3 عملاء.
👀 اعرض الناتج المطلوب
NOTICE: الرياض: 3 عملاء
NOTICE: جدة: 2 عملاء
NOTICE: أبها: 1 عملاء
NOTICE: الدمام: 1 عملاء
NOTICE: المدينة: 1 عملاء
✓ التحقق الآلي من حلّك:
  • الكود يعمل بلا أخطاء
  • الرياض: 3 عملاء
  • جدة: 2 عملاء
  • أبها: 1 عملاء
  • OPEN
  • FETCH … INTO
  • EXIT WHEN NOT FOUND
  • CLOSE

💡 تلميح 1
ابدأ من مثال الميزانية في الدرس.
💡 تلميح 2
نص الرسالة: RAISE NOTICE '%: % عملاء', c, n;

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

solution.sql
DO $$DECLARE  cur CURSOR FOR SELECT city, count(*) FROM customers GROUP BY city ORDER BY 2 DESC, 1;  c text;  n int;BEGIN  OPEN cur;  LOOP    FETCH cur INTO c, n;    EXIT WHEN NOT FOUND;    RAISE NOTICE '%: % عملاء', c, n;  END LOOP;  CLOSE cur;END $$;
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

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