0Pricing
SQL Academy · درس

استعراض شجرة الفئات

وسّع أشجار الأصل والفرع بالكامل

استعراض شجرة الفئات درس مجاني في SQL Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في SQL Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة SQL Academy 4 دروس في المجموع.

ما المقصود بشجرة الفئات؟

تحتوي العديد من مجموعات البيانات الواقعية على علاقة بين الأصل والفرع. فقد يتضمن كتالوج المنتجات فئات مثل الإلكترونيات → الهواتف → الهواتف الذكية. لكل عقدة أصل، مما يشكل بنية شجرية.

في SQL، يُخزَّن ذلك عادةً في جدول يشير إلى نفسه: يحتوي كل صف على id وparent_id يشير إلى صف آخر في الجدول نفسه.

CREATE TABLE categories (
  id       INT PRIMARY KEY,
  name     VARCHAR(100) NOT NULL,
  parent_id INT REFERENCES categories(id)
);

بيانات فئات نموذجية

لنملأ شجرة فئات صغيرة. تحتوي العقدة الجذرية على parent_id = NULL لأنها لا تملك أصلًا. وتشير كل عقدة أخرى إلى أصلها باستخدام قيمة غير فارغة في parent_id.

INSERT INTO categories (id, name, parent_id) VALUES
  (1, 'Electronics',   NULL),
  (2, 'Phones',         1),
  (3, 'Laptops',        1),
  (4, 'Smartphones',    2),
  (5, 'Feature Phones', 2),
  (6, 'Gaming Laptops', 3),
  (7, 'Ultrabooks',     3);

مشكلة الاستعلامات البسيطة

لا يستطيع SELECT عادي جلب أكثر من مستوى واحد في كل مرة. وللوصول إلى عمق ثلاثة مستويات، ستحتاجون إلى ثلاثة استعلامات منفصلة أو ثلاث عمليات ربط ذاتي، وهو ما يصبح صعب الإدارة كلما كبرت الشجرة.

تحل WITH RECURSIVE هذه المشكلة من خلال السماح للاستعلام بالإشارة إلى مخرجاته، واجتياز المستويات واحدًا تلو الآخر حتى لا يعثر على صفوف جديدة.

-- This only shows direct children of Electronics (level 1)
SELECT id, name
FROM   categories
WHERE  parent_id = 1;

بنية WITH RECURSIVE

يتكوّن CTE التكراري من جزأين يفصل بينهما UNION ALL:

1. العضو الأساسي — عبارة SELECT عادية توفّر الصفوف الابتدائية.

2. العضو التكراري — عبارة SELECT تعيد ربط CTE بنفسه، وتنتج المستوى التالي في كل تكرار.

يكرّر المحرّك تنفيذ العضو التكراري حتى يعيد صفوفًا تساوي صفرًا.

WITH RECURSIVE cte AS (
  -- Anchor: starting rows
  SELECT ...
  UNION ALL
  -- Recursive: join cte to base table
  SELECT ... FROM base_table JOIN cte ON ...
)
SELECT * FROM cte;

التنقل في الشجرة الكاملة من الجذر

ابدأ من الجذر، أي عندما يكون parent_id IS NULL، ثم انتقل نزولًا إلى كل عقدة تابعة. يعيد العضو التكراري ربط كل صف متراكم بالجدول categories وفق علاقة الأصل بالابن.

WITH RECURSIVE category_tree AS (
  -- Anchor: root nodes
  SELECT id, name, parent_id, 1 AS depth
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  -- Recursive: children of current level
  SELECT c.id, c.name, c.parent_id, ct.depth + 1
  FROM   categories      c
  JOIN   category_tree   ct ON ct.id = c.parent_id
)
SELECT id, name, depth
FROM   category_tree
ORDER  BY depth, id;

تتبّع المسار

من المفيد تسجيل المسار الكامل من الجذر إلى كل عقدة. يمكنكم إنشاء سلسلة path من خلال وصل أسماء الأسلاف أثناء التعمق في التكرار.

يسهّل ذلك عرض مسارات التنقل مثل الإلكترونيات / الهواتف / الهواتف الذكية.

WITH RECURSIVE category_tree AS (
  SELECT id, name, parent_id,
         name AS path
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  SELECT c.id, c.name, c.parent_id,
         ct.path || ' / ' || c.name
  FROM   categories    c
  JOIN   category_tree ct ON ct.id = c.parent_id
)
SELECT id, name, path
FROM   category_tree
ORDER  BY path;

البدء من عقدة محددة

لا يلزم أن تبدأوا من الجذر. بتغيير عبارة WHERE في العضو الأساسي، يمكنكم التنقل في الشجرة الفرعية لأي عقدة. نبدأ هنا من الهواتف (id = 2) ونسترجع جميع العقد التابعة لها.

WITH RECURSIVE subtree AS (
  SELECT id, name, parent_id, 0 AS depth
  FROM   categories
  WHERE  id = 2          -- start at Phones

  UNION ALL

  SELECT c.id, c.name, c.parent_id, s.depth + 1
  FROM   categories c
  JOIN   subtree    s ON s.id = c.parent_id
)
SELECT id, name, depth
FROM   subtree
ORDER  BY depth, id;

التنقل إلى الأعلى: العثور على جميع الأسلاف

يمكنكم أيضًا التنقل في الشجرة بالعكس، أي صعودًا من عقدة طرفية إلى الجذر. ما عليكم سوى عكس عملية الربط بحيث تتبعوا parent_id صعودًا بدلًا من النزول. يفيد ذلك عند الحاجة إلى مسار التنقل الكامل لعقدة طرفية معروفة.

WITH RECURSIVE ancestors AS (
  SELECT id, name, parent_id
  FROM   categories
  WHERE  id = 4          -- start at Smartphones

  UNION ALL

  SELECT c.id, c.name, c.parent_id
  FROM   categories c
  JOIN   ancestors  a ON a.parent_id = c.id
)
SELECT id, name
FROM   ancestors
ORDER  BY id;

إضافة عرض بمسافات بادئة

من الأنماط الشائعة في واجهات المستخدم إزاحة العقد الابنة بصريًا. يمكنكم استخدام REPEAT (أو LPAD) مع عمود depth لإضافة مسافات قبل كل اسم، وإنشاء عرض شجري نصي.

WITH RECURSIVE category_tree AS (
  SELECT id, name, parent_id, 0 AS depth
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  SELECT c.id, c.name, c.parent_id, ct.depth + 1
  FROM   categories    c
  JOIN   category_tree ct ON ct.id = c.parent_id
)
SELECT
  REPEAT('    ', depth) || name AS indented_name,
  depth
FROM   category_tree
ORDER  BY path;

الحماية من الحلقات اللانهائية

إذا احتوت بياناتكم على دورة، مثل أن يكون A أصلًا لـ B وأن يكون B أصلًا لـ A، فسيستمر التكرار إلى ما لا نهاية ويتسبب في تعطل العملية. يمكنكم الحماية من ذلك بتتبّع معرّفات العقد التي تمت زيارتها في مصفوفة، وإيقاف التكرار عندما يكون المعرّف الحالي موجودًا فيها مسبقًا.

WITH RECURSIVE safe_tree AS (
  SELECT id, name, parent_id,
         ARRAY[id] AS visited
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  SELECT c.id, c.name, c.parent_id,
         st.visited || c.id
  FROM   categories c
  JOIN   safe_tree  st ON st.id = c.parent_id
  WHERE  c.id <> ALL(st.visited)   -- stop if already seen
)
SELECT id, name FROM safe_tree;

عدّ العقد التابعة لكل عقدة

بعد إنشاء الشجرة الكاملة، يمكنكم تجميع بياناتها. نعدّ هنا عدد العقد التابعة لكل عقدة من خلال تجميع صفوف الأبناء مرة أخرى مقابل قائمة الأسلاف. يفيد ذلك في عرض عدد العناصر بجوار أسماء الفئات في قائمة التنقل.

WITH RECURSIVE category_tree AS (
  SELECT id, name, parent_id, id AS root_id
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  SELECT c.id, c.name, c.parent_id, ct.root_id
  FROM   categories    c
  JOIN   category_tree ct ON ct.id = c.parent_id
)
SELECT
  root_id,
  COUNT(*) - 1 AS descendant_count
FROM   category_tree
GROUP  BY root_id
ORDER  BY root_id;

تحقق سريع

اختبروا مدى فهمكم لاستعلامات شجرة الفئات التكرارية.

مراجعة الدرس

تعلّمتم في هذا الدرس كيفية التنقل في جدول فئات يشير إلى نفسه باستخدام WITH RECURSIVE.

أهم النقاط:

- يحدّد العضو الأساسي العقد الابتدائية، وعادةً ما تكون الجذر.

- يعيد العضو التكراري ربط CTE بالجدول الأساسي للعثور على المستوى التالي.

- أضيفوا عمود depth لتتبّع عدد المستويات التي تفصل كل عقدة عن البداية.

- أنشئوا سلسلة path لتوليد مسارات التنقل.

- تنقّلوا صعودًا باتباع parent_id بالعكس للعثور على جميع الأسلاف.

- استخدموا مصفوفة visited للحماية من الدورات في البيانات غير النظيفة.

الأسئلة الشائعة

هل درس «استعراض شجرة الفئات» مجاني؟

نعم — نص درس «استعراض شجرة الفئات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة SQL Academy، انتقل إلى CoddyKit PRO. تتضمن دورة SQL Academy 4 دروس في المجموع.

ماذا ستتعلم في «استعراض شجرة الفئات»؟

وسّع أشجار الأصل والفرع بالكامل تتمرن على SQL Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ SQL Academy؟

لا تُشترط خبرة سابقة. SQL Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.

كم من الوقت يستغرق درس «استعراض شجرة الفئات»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس SQL Academy هذا؟

نعم. كل درس في SQL Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. كيف تعمل CTEs التكرارية
  2. استعراض شجرة الفئات
  3. إنشاء السلاسل والتسلسلات
  4. تجنّب الحلقات اللانهائية
← العودة إلى SQL Academy