إتقان 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 وتحسين الاستعلامات

خذ الاختبار