مواضيع متفرقة في SQL


مواضيع متفرقة في SQL

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

1. استخدام CASE للتصنيف والتحويل

تعبير CASE مثل جملة IF-ELSE اللي تعرفها في لغات البرمجة الثانية. يسمح لك بتطبيق منطق شرطي داخل استعلاماتك. تقدر تستخدمه عشان تصنف بيانات، تحول قيم، أو حتى تسوي حسابات معقدة بناءً على شروط معينة.

شوف المثال هذا:

SELECT
        ProductName,
        Price,
        CASE
            WHEN Price < 50 THEN 'رخيص'
            WHEN Price >= 50 AND Price < 200 THEN 'متوسط'
            ELSE 'غالي'
        END AS PriceCategory
    FROM
        Products;

أو ممكن تستخدمه بشكل أبسط (Simple CASE):

SELECT
        OrderStatus,
        CASE OrderStatus
            WHEN 'Pending' THEN 'في انتظار المراجعة'
            WHEN 'Completed' THEN 'تم الإنجاز'
            ELSE 'أخرى'
        END AS StatusDescription
    FROM
        Orders;

ملاحظة سريعة: CASE هو تعبير قوي جداً ويخليك تسوي شغل كثير كان ممكن يحتاج منك استعلامات فرعية معقدة أو حتى معالجة في التطبيق. لا تستهين فيه!

2. COALESCE للتعامل مع القيم الفارغة (NULL)

دالة COALESCE رهيبة لما تكون عندك أعمدة ممكن تحتوي على قيم NULL (فارغة) وتبغى تعرض قيمة بديلة بدالها. هي ترجع لك أول تعبير غير NULL من قائمة التعبيرات اللي تعطيها إياها.

مثلاً، عندك عمود Phone ممكن يكون فاضي، وتبغى تعرض "لا يوجد رقم" إذا كان NULL:

SELECT
        CustomerID,
        CustomerName,
        COALESCE(Phone, 'لا يوجد رقم هاتف') AS ContactPhone
    FROM
        Customers;

تقدر تحط أكثر من تعبير، وهي بتاخذ أول واحد غير NULL بالترتيب:

SELECT
        ProductID,
        ProductName,
        COALESCE(ProductDescription, ShortDescription, 'لا يوجد وصف متاح') AS DisplayDescription
    FROM
        Products;

ملاحظة سريعة: COALESCE مفيد جداً في التقارير ولما تعرض بيانات للمستخدمين عشان ما يشوفوا قيم NULL اللي ممكن تكون مربكة.

3. الدوال الإطارية (Window Functions) - ROW_NUMBER() كمثال

الدوال الإطارية (Window Functions) عالم ثاني في SQL، وتسمح لك تسوي عمليات حسابية على مجموعة من الصفوف المرتبطة بالصف الحالي، بدون ما تسوي تجميع للبيانات (يعني ما تخسر تفاصيل الصفوف الأصلية). ROW_NUMBER() هي أشهر مثال ومفيدة جداً للترتيب أو استخراج صف معين من كل مجموعة.

لو عندك سجلات طلبات لعملاء وتبغى تعرف آخر طلب لكل عميل:

WITH CustomerOrdersRanked AS (
        SELECT
            OrderID,
            CustomerID,
            OrderDate,
            TotalAmount,
            ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderDate DESC) AS rn
        FROM
            Orders
    )
    SELECT
        OrderID,
        CustomerID,
        OrderDate,
        TotalAmount
    FROM
        CustomerOrdersRanked
    WHERE
        rn = 1;

ملاحظة سريعة: الـ PARTITION BY تحدد المجموعات اللي راح يتم الترتيب داخلها، والـ ORDER BY تحدد كيف سيتم الترتيب داخل كل مجموعة. عالم الدوال الإطارية كبير وفيه دوال كثيرة مثل RANK(), DENSE_RANK(), LEAD(), LAG() وغيرها.

4. التعبيرات الجدولية المشتركة (CTEs) - WITH

الـ Common Table Expressions أو CTEs، واللي تبدأ بـ WITH، هي طريقة تخلي استعلاماتك المعقدة أكثر تنظيم وقراءة. تقدر تعتبرها جداول مؤقتة، موجودة فقط طول مدة تنفيذ الاستعلام، وتساعدك تقسم الاستعلام الكبير لأجزاء أصغر وأسهل للفهم.

هنا مثال يوضح كيف ممكن تستخدمها عشان تجيب متوسط المبيعات لكل منطقة، وبعدين تطلع المناطق اللي مبيعاتها فوق المتوسط العام:

WITH RegionalSales AS (
        SELECT
            Region,
            SUM(SalesAmount) AS TotalRegionalSales
        FROM
            Sales
        GROUP BY
            Region
    ),
    OverallAverageSales AS (
        SELECT
            AVG(TotalRegionalSales) AS AvgSales
        FROM
            RegionalSales
    )
    SELECT
        rs.Region,
        rs.TotalRegionalSales
    FROM
        RegionalSales rs,
        OverallAverageSales oas
    WHERE
        rs.TotalRegionalSales > oas.AvgSales;

ملاحظة سريعة: الـ CTEs تجعل استعلاماتك أنظف وأسهل للفهم والصيانة، خصوصاً لما تكون الاستعلامات فيها خطوات كثيرة متتالية أو تحتاج تستخدم نفس النتائج المؤقتة أكثر من مرة.

5. فهم NULL والتعامل معه

مفهوم NULL في SQL كثير يلخبط، لكنه مهم جداً. NULL يعني "غير معروف" أو "لا يوجد قيمة"، وهو ليس صفراً، وليس سلسلة نصية فارغة، وليس مسافة بيضاء. هو يمثل غياب القيمة.

هذا يعني إنك ما تقدر تستخدم عوامل المقارنة العادية (مثل =, !=, >, <) مع NULL. لازم تستخدم IS NULL أو IS NOT NULL.

مثال:

-- جلب العملاء الذين ليس لديهم بريد إلكتروني مسجل
    SELECT CustomerID, CustomerName
    FROM Customers
    WHERE Email IS NULL;

    -- جلب العملاء الذين لديهم بريد إلكتروني مسجل
    SELECT CustomerID, CustomerName
    FROM Customers
    WHERE Email IS NOT NULL;

إذا حاولت تقارن NULL بأي قيمة أخرى باستخدام = أو !=، النتيجة دايماً بتكون UNKNOWN (غير معروف)، وهذا ممكن يسبب لك مشاكل في الفلترة.

ملاحظة سريعة: فهم NULL بشكل صحيح هو مفتاح لتجنب الأخطاء المنطقية في استعلاماتك. تذكر دائماً: NULL لا يساوي NULL!

6. EXISTS vs IN

كثير من المرات تحتاج تستعلم عن وجود بيانات في جدول ثاني. عندك خيارين شائعين: EXISTS و IN. الفرق بينهم ممكن يكون جوهري في الأداء.

  • IN: يتم تقييم الاستعلام الفرعي أولاً، ويتم إرجاع قائمة من القيم. بعدين، الاستعلام الرئيسي يشيك إذا كانت قيمة العمود موجودة في هذه القائمة.
  • EXISTS: يعمل بشكل مختلف. الاستعلام الفرعي يتم تقييمه لكل صف في الاستعلام الخارجي. إذا وجد الاستعلام الفرعي صفاً واحداً على الأقل، فإن EXISTS ترجع TRUE، وإلا فـ FALSE.

مثال لاستخدام IN (جلب العملاء الذين لديهم طلبات):

SELECT CustomerID, CustomerName
    FROM Customers
    WHERE CustomerID IN (SELECT CustomerID FROM Orders);

مثال لاستخدام EXISTS (نفس الغرض):

SELECT CustomerID, CustomerName
    FROM Customers c
    WHERE EXISTS (SELECT 1 FROM Orders o WHERE o.CustomerID = c.CustomerID);

ملاحظة سريعة: بشكل عام، EXISTS يميل لأن يكون أسرع عندما يكون الاستعلام الفرعي يرجع عدد كبير من الصفوف أو عندما يكون الجدول الخارجي كبير. IN ممكن يكون أسرع لما تكون القائمة اللي يرجعها الاستعلام الفرعي صغيرة. لكن الأداء الحقيقي يعتمد على نظام قاعدة البيانات، الفهارس، وحجم البيانات.

أتمنى إن هالمواضيع المتفرقة كانت مفيدة لك. كل واحدة منها تستاهل درس كامل بحد ذاتها، بس الهدف هنا كان إننا نفتح لك آفاق جديدة وتعرف إن فيه أدوات قوية تحت يدك في SQL!