إتقان SQL وتحسين الاستعلامات
تحسين الاستعلامات وخطط التنفيذ
فهم تحسين الاستعلامات يُظهر الفرق بين مهندسي البيانات المبتدئين والكبار. يختبر المحاورون هذا لتقييم جاهزية الإنتاج.
قراءة خطط التنفيذ
أمر 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 صف فعلي. ما الخطأ؟"
الجواب: الإحصائيات غالباً قديمة أو هناك انحراف بيانات. الحلول:
- شغل ANALYZE لتحديث الإحصائيات
- أنشئ إحصائيات رسم بياني للأعمدة المنحرفة
- استخدم تلميحات الاستعلام إذا لزم الأمر
تقنيات إعادة كتابة الاستعلام
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) قس التحسين.
بعد ذلك، سنستكشف استراتيجيات الفهرسة لأداء استعلام مثالي. :::