التعابير الجدولية الشائعة Common Table Expressions في SQL


التعابير الجدولية الشائعة Common Table Expressions في SQL

يلا بينا ندخل في الموضوع على طول! التعابير الجدولية الشائعة، أو زي ما بنسميها CTEs، هي ميزة رهيبة في SQL بتسمحلك تعرف "جدول مؤقت" - تقدر تستخدمه مرة واحدة بس - داخل نطاق استعلام واحد. يعني كأنك بتعمل VIEW بس للـ Query اللي أنت شغال عليها بس.

ليه أصلاً نستخدم CTEs؟

  • الوضوح (Readability): بتخلي الكويري المعقدة أسهل للقراءة والفهم، لأنك بتقسمها لأجزاء منطقية صغيرة.
  • إعادة الاستخدام (Reusability): بتسمحلك تعيد استخدام نفس النتيجة الفرعية أكتر من مرة في نفس الكويري بدون ما تعيد كتابتها.
  • الاستعلامات التكرارية (Recursive Queries): دي أهم ميزة، بتخليك تعمل استعلامات تكرارية، زي إنك تستعرض هيكل تنظيمي أو شجرة مسارات.

البنية الأساسية (Basic Syntax)

صيغة الـ CTE سهلة جداً، بتبدأ بـ WITH وبعدها بتدي اسم للـ CTE بتاعك، وبعدين AS وفي الأقواس بتحط الكويري اللي هتكون الـ CTE.

WITH اسم_الـCTE_بتاعك AS (
    -- الكويري اللي بتكون الجدول المؤقت
    SELECT عمود1, عمود2
    FROM جدول_ما
    WHERE شرط_ما
)
-- دلوقتي تقدر تستخدم الـ CTE ده كأنه جدول عادي
SELECT *
FROM اسم_الـCTE_بتاعك
WHERE شرط_آخر;

مثال 1: تبسيط استعلام معقد

لو عندك كويري بتجيب إجمالي المبيعات لكل منتج، وبعدين عايز تشوف المنتجات اللي إجمالي مبيعاتها أعلى من المتوسط العام. ممكن نعملها كده:

WITH اجمالي_مبيعات_المنتجات AS (
    SELECT
        ProductID,
        SUM(OrderQty * UnitPrice) AS TotalSales
    FROM Sales.SalesOrderDetail
    GROUP BY ProductID
),
متوسط_المبيعات_العام AS (
    SELECT AVG(TotalSales) AS AverageOverallSales
    FROM اجمالي_مبيعات_المنتجات
)
SELECT
    p.Name AS ProductName,
    TS.TotalSales
FROM اجمالي_مبيعات_المنتجات AS TS
JOIN Production.Product AS p
    ON TS.ProductID = p.ProductID
WHERE TS.TotalSales > (SELECT AverageOverallSales FROM متوسط_المبيعات_العام);

لاحظ هنا إزاي قسمنا الكويري لأجزاء منطقية: الأول جبنا إجمالي المبيعات لكل منتج، وبعدين حسبنا المتوسط العام للمبيعات بناءً على الـ CTE الأول. ده بيخلي الكود أنظف وأسهل للفهم.

CTEs متعددة ومتسلسلة (Multiple and Chained CTEs)

تقدر تعرف أكتر من CTE ورا بعض، وحتى ممكن CTE منهم يعتمد على CTE تاني تم تعريفه قبله. كل اللي عليك إنك تفصل بينهم بفاصلة ,.

WITH CTE_الاول AS (
    SELECT ...
    FROM ...
),
CTE_الثاني AS ( -- ده ممكن يستخدم نتائج CTE_الاول
    SELECT ...
    FROM CTE_الاول
    WHERE ...
),
CTE_الثالث AS (
    SELECT ...
    FROM CTE_الثاني
    WHERE ...
)
SELECT *
FROM CTE_الثالث;

الـ CTEs التكرارية (Recursive CTEs)

دي بقى اللعبة الكبيرة! الـ CTEs التكرارية بتسمحلك تستعلم عن بيانات هرمية أو شجرية، زي الهياكل التنظيمية للموظفين، مسارات الطيران، أو قوائم المواد (BOM). بتتكون من جزئين:

  1. الجزء الثابت (Anchor Member): ده الجزء اللي بيبدأ الكويري التكرارية، بيجيب الصفوف الأولية.
  2. الجزء التكراري (Recursive Member): ده الجزء اللي بيشير لنفس الـ CTE عشان يجيب الصفوف اللي بعدها، وبيكون فيه شرط توقف عشان ما يدخلش في حلقة لا نهائية.

مثال 2: هيكل تنظيمي (Organizational Chart)

تخيل عندك جدول للموظفين وكل موظف ليه ManagerID بيشير للمدير بتاعه. عايزين نجيب الهيكل التنظيمي كله.

WITH EmployeeHierarchy AS (
    -- الجزء الثابت (Anchor Member) - بيبدأ من الموظفين اللي مالهمش مدير (الرئيس التنفيذي مثلاً)
    SELECT
        EmployeeID,
        ManagerID,
        FullName,
        0 AS Level -- مستوى 0 للموظف الأعلى
    FROM Employees
    WHERE ManagerID IS NULL

    UNION ALL

    -- الجزء التكراري (Recursive Member) - بيجيب الموظفين اللي بيتبعوا للمدير اللي جبناه في الخطوة اللي فاتت
    SELECT
        e.EmployeeID,
        e.ManagerID,
        e.FullName,
        eh.Level + 1 AS Level
    FROM Employees AS e
    JOIN EmployeeHierarchy AS eh
        ON e.ManagerID = eh.EmployeeID
)
SELECT
    REPLICATE('  ', Level) + FullName AS EmployeePath, -- مسافة بناءً على المستوى
    Level
FROM EmployeeHierarchy
ORDER BY Level, EmployeeID;

في المثال ده، EmployeeHierarchy بتنادي نفسها لحد ما يوصل لكل المستويات في الهيكل التنظيمي. الـ UNION ALL هنا مهم جداً عشان يضيف النتائج الجديدة لكل تكرار.

ملخص وفوائد

الـ CTEs أداة قوية جداً في SQL بتخليك تكتب استعلامات أكتر تنظيم ووضوح، وبتفتح الباب لأنواع معقدة من الاستعلامات زي التكرارية. لو لسه مش بتستخدمها، ابدأ تتعلمها وتطبقها في شغلك وهتشوف الفرق!