إتقان SQL وتحسين الاستعلامات

تحسين الاستعلامات وخطط التنفيذ

4 دقيقة للقراءة

فهم تحسين الاستعلامات يُظهر الفرق بين مهندسي البيانات المبتدئين والكبار. يختبر المحاورون هذا لتقييم جاهزية الإنتاج.

قراءة خطط التنفيذ

أمر EXPLAIN

-- PostgreSQL
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 123;

-- MySQL
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE customer_id = 123;

-- Snowflake
EXPLAIN SELECT * FROM orders WHERE customer_id = 123;

المقاييس الرئيسية للفهم

المقياس المعنى جيد مقابل سيئ
الصفوف المفحوصة السجلات الممسوحة أقل أفضل
الوقت الفعلي مدة التنفيذ يعتمد على السياق
التكلفة استخدام الموارد المقدر مقارنة نسبية
ضربات المخزن استخدام الذاكرة المؤقتة أعلى أفضل

أنواع المسح (من الأفضل للأسوأ)

نوع المسح السرعة متى يُستخدم
Index Unique Scan الأسرع البحث بالمفتاح الأساسي
Index Range Scan سريع شروط النطاق مع فهرس
Index Full Scan متوسط كل الصفوف عبر الفهرس
Full Table Scan الأبطأ لا فهرس مناسب

سؤال المقابلة: "هذا الاستعلام بطيء. كيف ستشخصه؟"

-- الخطوة 1: احصل على خطة التنفيذ
EXPLAIN ANALYZE
SELECT * FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE c.country = 'USA'
AND o.order_date > '2024-01-01';

-- ابحث عن:
-- 1. مسح كامل للجداول على الجداول الكبيرة
-- 2. تقديرات صفوف عالية مقابل الفعلي
-- 3. عمليات الفرز بدون فهارس
-- 4. ربط الحلقات المتداخلة على مجموعات بيانات كبيرة

أنماط الأداء المضادة الشائعة

1. SELECT * بدلاً من أعمدة محددة

-- سيئ: يجلب كل الأعمدة
SELECT * FROM orders WHERE status = 'pending';

-- جيد: فقط الأعمدة المطلوبة
SELECT order_id, customer_id, total
FROM orders WHERE status = 'pending';

لماذا مهم: الجداول الواسعة مع أعمدة كثيرة تهدر I/O والذاكرة.

2. الدوال على الأعمدة المفهرسة

-- سيئ: الدالة تمنع استخدام الفهرس
SELECT * FROM orders
WHERE YEAR(order_date) = 2024;

-- جيد: يحافظ على استخدام الفهرس
SELECT * FROM orders
WHERE order_date >= '2024-01-01'
AND order_date < '2025-01-01';

3. تحويلات النوع الضمنية

-- سيئ: customer_id هو INT، مقارنة بسلسلة
SELECT * FROM customers WHERE customer_id = '123';

-- جيد: أنواع متطابقة
SELECT * FROM customers WHERE customer_id = 123;

4. شروط OR على أعمدة مختلفة

-- سيئ: غالباً ينتج مسح كامل
SELECT * FROM orders
WHERE customer_id = 123 OR product_id = 456;

-- جيد: اتحاد استعلامات مفهرسة
SELECT * FROM orders WHERE customer_id = 123
UNION
SELECT * FROM orders WHERE product_id = 456;

5. الاستعلامات الفرعية المرتبطة

-- سيئ: ينفذ الاستعلام الفرعي لكل صف
SELECT o.order_id,
    (SELECT COUNT(*) FROM order_items oi
     WHERE oi.order_id = o.order_id) as item_count
FROM orders o;

-- جيد: ربط مع تجميع
SELECT o.order_id, COUNT(oi.item_id) as item_count
FROM orders o
LEFT JOIN order_items oi ON o.order_id = oi.order_id
GROUP BY o.order_id;

تحسين الربط

فهم خوارزميات الربط

الخوارزمية الأفضل لـ الذاكرة
Nested Loop الجداول الصغيرة، البحث المفهرس منخفضة
Hash Join الجداول الكبيرة، لا فهرس عالية
Merge Join البيانات المرتبة، ربط المساواة متوسطة

نمط المقابلة: "لماذا قد يُختار hash join على nested loop؟"

الجواب: Hash joins فعالة للجداول الكبيرة بدون فهارس. المحسن يبني جدول hash من الجدول الأصغر ويستقصيه بالجدول الأكبر، متجنباً المسح المتكرر.

ترتيب الربط مهم

-- استعلام بربط متعدد
SELECT *
FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN products p ON o.product_id = p.id
WHERE c.country = 'USA';

-- التحسين: فلتر مبكراً لتقليل حجم الربط
SELECT *
FROM (SELECT * FROM customers WHERE country = 'USA') c
JOIN orders o ON o.customer_id = c.id
JOIN products p ON o.product_id = p.id;

الإحصائيات والعددية

لماذا الإحصائيات مهمة

محسن الاستعلام يعتمد على إحصائيات الجدول لاختيار خطط التنفيذ.

-- PostgreSQL: تحديث الإحصائيات
ANALYZE table_name;

-- MySQL: تحديث الإحصائيات
ANALYZE TABLE table_name;

-- تحقق من تقديرات العددية
EXPLAIN (ANALYZE, BUFFERS) SELECT ...

مشاكل تقدير العددية

المشكلة العرض الحل
إحصائيات قديمة تقديرات صفوف خاطئة شغل ANALYZE
بيانات منحرفة ترتيب ربط سيئ إحصائيات الرسم البياني
أعمدة مرتبطة تقديرات منخفضة إحصائيات موسعة

سؤال المقابلة: "خطة التنفيذ تُظهر 100 صف مقدر لكن 1M صف فعلي. ما الخطأ؟"

الجواب: الإحصائيات غالباً قديمة أو هناك انحراف بيانات. الحلول:

  1. شغل ANALYZE لتحديث الإحصائيات
  2. أنشئ إحصائيات رسم بياني للأعمدة المنحرفة
  3. استخدم تلميحات الاستعلام إذا لزم الأمر

تقنيات إعادة كتابة الاستعلام

Exists مقابل IN مقابل JOIN

-- IN: جيد لنتائج استعلام فرعي صغيرة
SELECT * FROM orders
WHERE customer_id IN (SELECT id FROM premium_customers);

-- EXISTS: أفضل للشروط المرتبطة
SELECT * FROM orders o
WHERE EXISTS (
    SELECT 1 FROM premium_customers p
    WHERE p.id = o.customer_id
);

-- JOIN: غالباً الأفضل لمجموعات البيانات الكبيرة
SELECT DISTINCT o.* FROM orders o
JOIN premium_customers p ON o.customer_id = p.id;

تحسين ترقيم الصفحات

-- سيئ: OFFSET يمسح كل الصفوف السابقة
SELECT * FROM orders ORDER BY id LIMIT 10 OFFSET 100000;

-- جيد: ترقيم صفحات keyset (طريقة البحث)
SELECT * FROM orders
WHERE id > 100000
ORDER BY id LIMIT 10;

نظرة المقابلة: عندما تُسأل عن تحسين الاستعلام، دائماً هيكل ردك: (1) حدد المشكلة باستخدام EXPLAIN، (2) حلل عنق الزجاجة، (3) اقترح حلول محددة، (4) قس التحسين.

بعد ذلك، سنستكشف استراتيجيات الفهرسة لأداء استعلام مثالي. :::

اختبار

الوحدة 2: إتقان SQL وتحسين الاستعلامات

خذ الاختبار