مواضيع متفرقة في 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!