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

استراتيجيات الفهرسة

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

تصميم الفهرس مهارة هندسة بيانات حرجة. أسئلة المقابلات غالباً تطلب منك تصميم فهارس لأنماط استعلام محددة أو استكشاف أخطاء الاستعلامات البطيئة.

أنواع الفهارس وحالات الاستخدام

فهارس B-Tree (الافتراضي)

نوع الفهرس الأكثر شيوعاً، مثالي لاستعلامات المساواة والنطاق.

-- إنشاء فهرس B-Tree
CREATE INDEX idx_orders_customer ON orders(customer_id);

-- جيد لـ:
SELECT * FROM orders WHERE customer_id = 123;
SELECT * FROM orders WHERE customer_id > 100 AND customer_id < 200;
SELECT * FROM orders WHERE customer_id IN (1, 2, 3);
العملية أداء B-Tree
= المساواة ممتاز
<, >, BETWEEN ممتاز
LIKE 'prefix%' جيد
LIKE '%suffix' ضعيف (مسح كامل)
IS NULL جيد

فهارس Hash

محسنة لعمليات البحث عن المساواة الدقيقة فقط.

-- فهرس hash في PostgreSQL
CREATE INDEX idx_orders_status_hash ON orders USING HASH (status);

-- فعال فقط لـ:
SELECT * FROM orders WHERE status = 'pending';

-- غير مفيد لـ:
SELECT * FROM orders WHERE status != 'pending';  -- مسح كامل
SELECT * FROM orders WHERE status IN ('a', 'b');  -- عمليات بحث متعددة

الفهارس المركبة (متعددة الأعمدة)

حرجة للاستعلامات التي تفلتر على أعمدة متعددة.

-- فهرس مركب
CREATE INDEX idx_orders_customer_date
ON orders(customer_id, order_date);

قاعدة البادئة الأيسر: الفهرس يُستخدم عندما تفلتر الاستعلامات على الأعمدة الأيسر.

الاستعلام يستخدم الفهرس؟
WHERE customer_id = 1 ✅ نعم
WHERE customer_id = 1 AND order_date = '2024-01-01' ✅ نعم
WHERE order_date = '2024-01-01' ❌ لا
WHERE order_date = '2024-01-01' AND customer_id = 1 ✅ نعم (المحسن يعيد الترتيب)

سؤال المقابلة: "صمم فهارس لأنماط الاستعلام هذه"

-- استعلام 1: فلترة بالعميل، ترتيب بالتاريخ
SELECT * FROM orders WHERE customer_id = ? ORDER BY order_date DESC;

-- استعلام 2: فلترة بنطاق التاريخ والحالة
SELECT * FROM orders WHERE order_date BETWEEN ? AND ? AND status = ?;

-- استعلام 3: الحصول على آخر طلب لكل عميل
SELECT * FROM orders WHERE customer_id = ? ORDER BY order_date DESC LIMIT 1;

الجواب:

-- للاستعلام 1 و 3: customer_id أولاً، ثم order_date للترتيب
CREATE INDEX idx_customer_date ON orders(customer_id, order_date DESC);

-- للاستعلام 2: اعتبر الانتقائية - إذا كانت الحالة أكثر انتقائية
CREATE INDEX idx_status_date ON orders(status, order_date);
-- أو إذا كان نطاق التاريخ أكثر انتقائية
CREATE INDEX idx_date_status ON orders(order_date, status);

الفهارس الشاملة

تتضمن كل الأعمدة المطلوبة للاستعلام لتجنب البحث في الجدول.

-- الاستعلام
SELECT customer_id, order_date, total
FROM orders
WHERE customer_id = 123;

-- فهرس شامل
CREATE INDEX idx_covering ON orders(customer_id)
INCLUDE (order_date, total);

-- الآن الاستعلام يقرأ من الفهرس فقط، لا وصول للجدول

الفهارس الجزئية (المفلترة)

فهرس فقط الصفوف التي تطابق شرط—أصغر وأسرع.

-- فهرس فقط الطلبات المعلقة (مجموعة فرعية صغيرة)
CREATE INDEX idx_pending_orders ON orders(customer_id)
WHERE status = 'pending';

-- فعال لـ:
SELECT * FROM orders WHERE status = 'pending' AND customer_id = 123;

مبادئ تصميم الفهرس

1. تحليل الاستعلام أولاً

قبل إنشاء الفهارس، حلل أنماط الاستعلام الفعلية:

-- PostgreSQL: إيجاد الاستعلامات البطيئة
SELECT query, calls, mean_time, total_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;

-- حدد الأعمدة في:
-- 1. عبارات WHERE
-- 2. شروط JOIN
-- 3. عبارات ORDER BY
-- 4. عبارات GROUP BY

2. الانتقائية مهمة

الانتقائية العالية (القيم الفريدة) تجعل الفهارس أكثر فعالية.

-- تحقق من الانتقائية
SELECT
    COUNT(DISTINCT status) as status_cardinality,      -- منخفضة (مثلاً 5)
    COUNT(DISTINCT customer_id) as customer_cardinality, -- عالية (مثلاً 100K)
    COUNT(*) as total_rows
FROM orders;

-- فهرس customer_id أولاً (انتقائية عالية)
-- الحالة قد لا تحتاج فهرس وحدها

3. ترتيب الأعمدة في الفهارس المركبة

رتب حسب:

  1. شروط المساواة أولاً (تطابق دقيق)
  2. شروط النطاق أخيراً (>, <, BETWEEN)
  3. اعتبر ترتيب الفرز لـ ORDER BY
-- استعلام: WHERE status = 'active' AND created_at > '2024-01-01' ORDER BY created_at
-- أفضل فهرس:
CREATE INDEX idx_optimized ON table(status, created_at);
-- status (مساواة) → created_at (نطاق + ترتيب)

4. الوعي بتكلفة الفهرس

الاعتبار التأثير
أداء الكتابة كل فهرس يبطئ INSERT/UPDATE/DELETE
التخزين الفهارس تستهلك مساحة القرص
الصيانة تجزئة الفهرس بمرور الوقت
Vacuum/Analyze فهارس أكثر = صيانة أطول

قاعدة عامة: استهدف 3-5 فهارس لكل جدول كحد أقصى. كل فهرس إضافي يحتاج تبرير قوي.

سيناريوهات المقابلة الشائعة

السيناريو 1: استعلام لوحة قيادة بطيء

المشكلة: "استعلام لوحة القيادة هذا يأخذ 30 ثانية"

SELECT
    DATE(created_at) as date,
    COUNT(*) as orders,
    SUM(total) as revenue
FROM orders
WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'
AND status = 'completed'
GROUP BY DATE(created_at);

الحل:

-- 1. إنشاء فهرس مركب
CREATE INDEX idx_orders_status_created ON orders(status, created_at)
WHERE status = 'completed';  -- فهرس جزئي إذا مدعوم

-- 2. اعتبر materialized view للوحة القيادة
CREATE MATERIALIZED VIEW daily_completed_orders AS
SELECT
    DATE(created_at) as date,
    COUNT(*) as orders,
    SUM(total) as revenue
FROM orders
WHERE status = 'completed'
GROUP BY DATE(created_at);

السيناريو 2: أداء الربط

المشكلة: "الربط بين الطلبات والعملاء بطيء"

SELECT c.name, o.total
FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE c.country = 'USA';

الحل:

-- تأكد أن المفتاح الخارجي لديه فهرس
CREATE INDEX idx_orders_customer ON orders(customer_id);

-- فهرس لشرط الفلترة
CREATE INDEX idx_customers_country ON customers(country);

-- أو فهرس شامل
CREATE INDEX idx_customers_country_covering ON customers(country)
INCLUDE (id, name);

السيناريو 3: كشف الفهرس غير المستخدم

سؤال المقابلة: "كيف تحدد الفهارس غير المستخدمة؟"

-- PostgreSQL: إيجاد الفهارس غير المستخدمة
SELECT
    schemaname || '.' || relname AS table,
    indexrelname AS index,
    pg_size_pretty(pg_relation_size(i.indexrelid)) AS size,
    idx_scan AS scans
FROM pg_stat_user_indexes ui
JOIN pg_index i ON ui.indexrelid = i.indexrelid
WHERE idx_scan = 0
AND NOT indisunique
ORDER BY pg_relation_size(i.indexrelid) DESC;

نظرة المقابلة: عند مناقشة الفهارس، دائماً اعتبر المقايضة بين أداء القراءة (استعلامات أسرع) وأداء الكتابة (إدراج/تحديث أبطأ). أنظمة الإنتاج تحتاج هذا التوازن.

بعد ذلك، سنتدرب على أنماط مقابلات SQL الشائعة والحلول. :::

اختبار

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

خذ الاختبار