استخدام الاستعلامات المتداخلة Nested Queries في SQL


استخدام الاستعلامات المتداخلة Nested Queries في SQL

يا هلا! اليوم بنتكلم عن الاستعلامات المتداخلة، أو Subqueries، أو Nested Queries. هذي طريقة قوية جداً عشان تكتب استعلامات معقدة بطريقة واضحة وفعالة. باختصار، الاستعلام المتداخل هو استعلام SQL موجود داخل استعلام SQL آخر.

ليش نستخدمها؟

  • عشان نسوي فلترة للبيانات بناءً على نتائج استعلام ثاني.
  • عشان نستخدم نتائج استعلام كجدول مؤقت.
  • عشان نحسب قيم مجمعة (Aggregate) ونستخدمها في استعلام خارجي.

فين ممكن نلاقيها؟

الـ Subqueries ممكن تكون في أماكن كثيرة داخل جملة SELECT، مثل:

  • SELECT clause (كـ Scalar Subquery)
  • FROM clause (كـ Derived Table)
  • WHERE clause (الأكثر شيوعاً)
  • HAVING clause
  • INSERT, UPDATE, DELETE statements

أمثلة عملية:

1. في جملة WHERE (الأكثر استخداماً)

هنا نستخدم نتائج الاستعلام الداخلي عشان نفلتر نتائج الاستعلام الخارجي. غالباً نستخدم معها الكلمات المفتاحية IN, NOT IN, EXISTS, NOT EXISTS, ANY, ALL.

مثال 1.1: استخدام IN

لو عندنا جدول Products وجدول OrderDetails. نبغى نجيب كل المنتجات اللي تم طلبها مرة واحدة على الأقل.

SELECT ProductName, Price
FROM Products
WHERE ProductID IN (SELECT ProductID FROM OrderDetails);

ملاحظة: الاستعلام الداخلي لازم يرجع عمود واحد بس (Single Column) لما نستخدم IN.

مثال 1.2: استخدام NOT IN

نبغى المنتجات اللي ما انطلبت أبداً.

SELECT ProductName, Price
FROM Products
WHERE ProductID NOT IN (SELECT ProductID FROM OrderDetails);
مثال 1.3: استخدام EXISTS

EXISTS تتحقق إذا كان الاستعلام الداخلي يرجع أي صفوف (True) أو لا (False). هذي مفيدة جداً خصوصاً مع الاستعلامات المترابطة (Correlated Subqueries).

SELECT ProductName
FROM Products p
WHERE EXISTS (SELECT 1 FROM OrderDetails od WHERE od.ProductID = p.ProductID);

ملاحظة: الاستعلام الداخلي هنا هو Correlated Subquery لأنه يعتمد على الاستعلام الخارجي (p.ProductID). وهنا SELECT 1 بس عشان نقول للنظام "لقيت شي؟" مو "ايش لقيت؟".

مثال 1.4: استخدام NOT EXISTS

نفس فكرة NOT IN بس باستخدام EXISTS، نجيب المنتجات اللي ما انطلبت أبداً.

SELECT ProductName
FROM Products p
WHERE NOT EXISTS (SELECT 1 FROM OrderDetails od WHERE od.ProductID = p.ProductID);
مثال 1.5: استخدام ANY أو SOME (نفس الشي)

ANY بترجع True إذا كانت القيمة تطابق أي قيمة من القيم اللي رجعها الاستعلام الداخلي. مثلاً، نبغى المنتجات اللي سعرها أعلى من أي سعر منتج في الفئة رقم 3.

SELECT ProductName, Price
FROM Products
WHERE Price > ANY (SELECT Price FROM Products WHERE CategoryID = 3);
مثال 1.6: استخدام ALL

ALL بترجع True إذا كانت القيمة تطابق كل القيم اللي رجعها الاستعلام الداخلي. مثلاً، نبغى المنتجات اللي سعرها أعلى من كل أسعار المنتجات في الفئة رقم 3.

SELECT ProductName, Price
FROM Products
WHERE Price > ALL (SELECT Price FROM Products WHERE CategoryID = 3);

2. في جملة FROM (Derived Tables)

هنا نستخدم الاستعلام الداخلي كـ "جدول مؤقت" أو "جدول مشتق" (Derived Table) نقدر نسوي له JOIN أو نفلتره. لازم نعطيه اسم مستعار (Alias).

SELECT c.CustomerName, o.TotalOrderAmount
FROM Customers c
JOIN (
    SELECT CustomerID, SUM(Amount) AS TotalOrderAmount
    FROM Orders
    GROUP BY CustomerID
) AS o ON c.CustomerID = o.CustomerID
WHERE o.TotalOrderAmount > 1000;

ملاحظة: هذي طريقة ممتازة لتبسيط الاستعلامات المعقدة وتجميع البيانات قبل ما نسوي عليها عمليات ثانية.

3. في جملة SELECT (Scalar Subquery)

هنا الاستعلام الداخلي لازم يرجع قيمة واحدة فقط (Single Value) وصف واحد فقط (Single Row). نقدر نستخدمها عشان نجيب قيمة مرتبطة بكل صف من الاستعلام الخارجي.

SELECT ProductName, Price,
       (SELECT AVG(Price) FROM Products) AS AveragePrice,
       (SELECT CategoryName FROM Categories c WHERE c.CategoryID = p.CategoryID) AS CategoryName
FROM Products p;

ملاحظة: الاستعلام الداخلي الثاني هو Correlated Subquery لأنه يعتمد على p.CategoryID. هذي مفيدة لما تبغى تضيف معلومات إضافية لكل صف من جدول ثاني بدون JOIN مباشر.

4. في جمل INSERT, UPDATE, DELETE

مثال 4.1: INSERT مع Subquery

نقدر ندخل بيانات في جدول بناءً على نتائج استعلام من جدول أو جداول ثانية.

INSERT INTO ArchivedOrders (OrderID, CustomerID, OrderDate, TotalAmount)
SELECT OrderID, CustomerID, OrderDate, TotalAmount
FROM Orders
WHERE OrderDate < '2023-01-01';
مثال 4.2: UPDATE مع Subquery

نحدث قيم في جدول بناءً على نتائج استعلام.

UPDATE Products
SET Price = Price * 1.10
WHERE CategoryID IN (SELECT CategoryID FROM Categories WHERE CategoryName = 'Electronics');
مثال 4.3: DELETE مع Subquery

نحذف صفوف بناءً على نتائج استعلام.

DELETE FROM Customers
WHERE CustomerID NOT IN (SELECT CustomerID FROM Orders); -- حذف العملاء اللي ما عندهم طلبات

نصائح سريعة:

  • الأداء: أحياناً الـ Subqueries ممكن تكون أبطأ من الـ JOINs، خصوصاً الـ Correlated Subqueries. جرب وشوف الأفضل لحالتك.
  • الوضوح: الـ Subqueries ممكن تخلي الكود أوضح وأسهل للقراءة في بعض الحالات المعقدة.
  • الـ Correlated vs Non-Correlated:
    • Non-Correlated: الاستعلام الداخلي يتنفذ مرة واحدة ويرجع نتيجة يستخدمها الاستعلام الخارجي.
    • Correlated: الاستعلام الداخلي يتنفذ لكل صف من الاستعلام الخارجي، وهذا ممكن يأثر على الأداء.

وبكذا نكون غطينا أساسيات الاستعلامات المتداخلة في SQL. هي أداة قوية جداً في ترسانة أي مبرمج قواعد بيانات!