الحلقات على النتائج والمؤشرات
حين تحتاج معالجة كل صف بمنطق خاص: الحلقة والمؤشر. ⏱ نحو 40 دقيقة.
- تمر على نتيجة استعلام بحلقة
FOR…IN SELECT. - تستخدم
recordو %ROWTYPE. - تستخدم المؤشر الصريح:
OPENوFETCHوCLOSE. - تعرّف مؤشرًا بمعاملات.
١المرور على نتيجة استعلام: FOR … IN SELECT
عرفت حلقة FOR i IN 1..10. والأكثر استخدامًا في قواعد البيانات حلقة تمرّ على صفوف استعلام، صفًّا صفًّا، في متغير من نوع record:
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 $$;
r.name و r.stock: أعمدة الصف الحالي.
وإن كان الصف من جدول معروف، فالنوع جدول%ROWTYPE يحمل كل أعمدته بأنواعها:
DO $$DECLARE c customers%ROWTYPE;BEGIN SELECT * INTO c FROM customers WHERE id = 3; RAISE NOTICE 'العميلة % من % انضمت في %', c.name, c.city, c.joined;END $$;
٢المؤشر الصريح: تحكم كامل
حلقة FOR تفتح مؤشرًا Cursor وتغلقه تلقائيًّا. وحين تحتاج التحكم بنفسك (جلب بعض الصفوف، أو التوقف بشرط، أو تمرير المؤشر) تستخدم المؤشر الصريح بأربع خطوات:
| الخطوة | الأمر |
|---|---|
| 1. التعريف | cur CURSOR FOR SELECT …; في DECLARE |
| 2. الفتح | OPEN cur; |
| 3. الجلب | FETCH cur INTO متغير; ثم EXIT WHEN NOT FOUND; |
| 4. الإغلاق | CLOSE cur; |
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 $$;
توقفنا عند أول منتج لا تكفيه الميزانية، دون قراءة بقية الصفوف.
٣مؤشر بمعاملات
المؤشر يقبل معاملات، فتفتحه بقيم مختلفة كل مرة:
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 $$;
توقّع وتحقّق
متغير يحمل صفًّا من أي استعلام:
بعد FETCH نعرف انتهاء الصفوف بـ:
في Oracle يُفحص انتهاء المؤشر بـ:
متى نفضّل المؤشر الصريح على FOR؟
الخلاصة
FOR r IN SELECT…LOOPيمر على الصفوف ويُدير المؤشر تلقائيًّا.recordلأي صف، و جدول%ROWTYPEلصف جدول معروف.- المؤشر الصريح:
DECLAREثمOPENثمFETCHمعEXIT WHEN NOT FOUNDثمCLOSE. - في
Oracle: cur%NOTFOUND، وفيMySQL: CONTINUE HANDLER FOR NOT FOUND.
تحدٍّ برمجي: تقرير المدن بمؤشر
متوسطاكتب كتلة DO تطبع لكل مدينة عدد عملائها، مرتبة بالعدد تنازليًّا ثم باسم المدينة، باستخدام مؤشر صريح.
- مؤشر على
SELECT city, count(*) … GROUP BY city ORDER BY 2 DESC, 1. - OPEN ثم حلقة FETCH مع
EXIT WHEN NOT FOUNDثم CLOSE. - كل سطر بالشكل:
الرياض: 3 عملاء.
👀 اعرض الناتج المطلوب
- الكود يعمل بلا أخطاء
- الرياض: 3 عملاء
- جدة: 2 عملاء
- أبها: 1 عملاء
- OPEN
- FETCH … INTO
- EXIT WHEN NOT FOUND
- CLOSE
💡 تلميح 1
💡 تلميح 2
RAISE NOTICE '%: % عملاء', c, n;حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
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 $$;