PostgreSQL: التصفية المتقدمة ومطابقة الأنماط في PostgreSQL

آخر تحديث: 2026-08-26

1. ما ستتعلمه


2. القصة

Alice هي مطورة ميزة البحث في منصة تجارة إلكترونية. عندما يكتب المستخدم كلمة مفتاحية في صندوق البحث، يحتاج النظام إلى:

  1. مطابقة تقريبية ضد أسماء المنتجات والأوصاف
  2. دعم بحث غير حساس لحالة الأحرف
  3. السماح للمستخدمين المتقدمين باستخدام التعابير النمطية للبحث الدقيق
  4. تصفية المنتجات المسحوبة
  5. ترتيب النتائج حسب الصلة

تحتاج Alice إلى إتقان أدوات التصفية ومطابقة الأنماط المتنوعة التي تقدمها PostgreSQL لبناء نظام البحث هذا.


3. المفهوم: تركيبات AND / OR / NOT

(1) أولوية المعاملات المنطقية

المعامل الأولوية الوصف
NOT الأعلى النفي
AND المتوسط كلا الشرطين صحيح
OR الأدنى أي من الشرطين صحيح

(1) ▶ مثال

SQL
SELECT product_name, unit_price, category
FROM products
WHERE category = 'Electronics'
  AND unit_price > 500
  AND is_active = true;

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) ▶ مثال

SQL
SELECT product_name, unit_price, category
FROM products
WHERE category = 'Electronics'
   OR category = 'Books'
   OR category = 'Toys';

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) التحكم في المنطق بالأقواس

OR لها أولوية أقل من AND؛ إهمال الأقواس قد يعطي نتائج غير متوقعة.

الشكل المعنى المنطقي
a AND b OR c (a AND b) OR c
a AND (b OR c) a AND (b OR c) — عادة ما تقصده

(3) ▶ مثال

SQL
SELECT product_name, unit_price, category
FROM products
WHERE is_active = true
  AND (category = 'Electronics' OR category = 'Books');

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(3) NOT النفي

NOT تنفي أي تعبير منطقي وغالباً ما تقترن مع IN، BETWEEN، LIKE، إلخ.

(4) ▶ مثال

SQL
SELECT product_name, unit_price
FROM products
WHERE NOT category = 'Electronics'
  AND is_active = true;

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

4. المفهوم: IN / BETWEEN / IS NULL

(1) IN و NOT IN

IN تتحقق مما إذا كانت القيمة في قائمة معينة — مكافئة لـ OR متعددة، لكن أكثر إيجازاً وأداءً.

الشكل الوصف
col IN (a, b, c) مكافئ لـ col = a OR col = b OR col = c
col NOT IN (a, b, c) مكافئ لـ col <> a AND col <> b AND col <> c

(5) ▶ مثال

SQL
SELECT product_name, unit_price, category
FROM products
WHERE category IN ('Electronics', 'Books', 'Toys')
  AND is_active = true;

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) تصفية النطاق BETWEEN

BETWEEN شاملة لحدودها، مكافئة لـ >= AND <=.

الشكل المكافئ
col BETWEEN a AND b col >= a AND col <= b
col NOT BETWEEN a AND b col < a OR col > b

(6) ▶ مثال

SQL
SELECT product_name, unit_price
FROM products
WHERE unit_price BETWEEN 100 AND 500
ORDER BY unit_price;

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(7) ▶ مثال

SQL
SELECT order_id, customer_id, order_date, total_amount
FROM orders
WHERE order_date BETWEEN '2025-01-01' AND '2025-06-30'
  AND order_status = 'completed';

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(3) IS NULL / IS NOT NULL

NULL لا تساوي أي شيء (بما في ذلك نفسها)؛ يجب استخدام IS NULL أو IS NOT NULL لاختبارها.

الشكل النتيجة
NULL = NULL NULL (ليس TRUE)
NULL <> NULL NULL (ليس TRUE)
col IS NULL اختبار NULL الصحيح
col IS NOT NULL اختبار غير NULL الصحيح

(8) ▶ مثال

SQL
SELECT product_name, unit_price, discount_rate
FROM products
WHERE discount_rate IS NULL
  AND is_active = true;

Output:

TEXT 📖 للعرض فقط
 count 
-------
     5
(1 row)

(4) فخ NOT IN و NULL

عندما تحتوي قائمة NOT IN على NULL، قد يعيد التعبير بالكامل مجموعة نتائج فارغة.

التعبير النتيجة السبب
3 NOT IN (1, 2, NULL) NULL 3 <> NULL هي NULL؛ NULL AND ... هي NULL
3 IN (1, 2, NULL) NULL 3 = NULL هي NULL؛ FALSE OR NULL هي NULL

(9) ▶ مثال

SQL
-- خطر: NULL في الاستعلام الفرعي يمكن أن يكسر NOT IN
SELECT product_name
FROM products
WHERE category NOT IN (
  SELECT category FROM categories WHERE is_active = false
);

--▶�من: استخدم NOT EXISTS بدلاً من ذلك
SELECT p.product_name
FROM products p
WHERE NOT EXISTS (
  SELECT 1 FROM categories c
  WHERE c.category = p.category AND c.is_active = false
);

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

5. المفهوم: مطابقة الأنماط LIKE / ILIKE

(1) أحرف البدل في LIKE

حرف البدل المعنى مثال
% يطابق أي سلسلة بأي طول (بما في ذلك الفارغة) 'Phone%' يطابق Phone، Phone Case
_ يطابق حرفاً واحداً 'A_c' يطابق Arc، ABC

(10) ▶ مثال

SQL
SELECT product_name, unit_price
FROM products
WHERE product_name LIKE 'Wireless%'
  AND is_active = true;

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(11) ▶ مثال

SQL
SELECT product_name, unit_price
FROM products
WHERE product_name LIKE '%Battery%'
  AND is_active = true;

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) ILIKE: غير حساسة لحالة الأحرف (ميزة PostgreSQL)

ILIKE هي امتداد PostgreSQL يطابق بدون حساسية لحالة الأحرف، مكافئة لـ LIKE + LOWER().

المعامل حساس لحالة الأحرف SQL قياسي
LIKE حساس نعم
ILIKE غير حساس لا (PG فقط)

(12) ▶ مثال

SQL
SELECT product_name, unit_price, description
FROM products
WHERE (product_name ILIKE '%iphone%'
   OR description ILIKE '%iphone%')
  AND is_active = true;

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(3) تهريب أحرف البدل

عندما تحتاج للبحث عن % أو _ حرفياً، استخدم ESCAPE لتحديد حرف تهريب.

(13) ▶ مثال

SQL
SELECT product_name, description
FROM products
WHERE description LIKE '%100\%%' ESCAPE '\';

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

6. المفهوم: مطابقة التعابير النمطية

(1) نظرة عامة على معاملات التعابير النمطية

توفر PostgreSQL أربعة معاملات مطابقة للتعابير النمطية، بالإضافة إلى صيغة SIMILAR TO.

المعامل حساس لحالة الأحرف الوصف
~ حساس يطابق التعبي�� النمطي
~* غير حساس يطابق التعبي�� النمطي (متجاهل الحالة)
!~ حساس لا يطابق التعبي�� النمطي
!~* غير حساس لا يطابق التعبي�� النمطي (متجاهل الحالة)
SIMILAR TO حساس مجموعة فرعية من التعابير النمطية بمعيار SQL (تدعم % _ `

(14) ▶ مثال

SQL
SELECT product_name, unit_price
FROM products
WHERE product_name ~ '^(Wireless|Bluetooth)'
  AND is_active = true;

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(15) ▶ مثال

SQL
SELECT product_name, unit_price
FROM products
WHERE product_name ~* 'iphone\s*(1[0-9])?'
  AND is_active = true;

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) SIMILAR TO

SIMILAR TO تقع بين LIKE والتعابير النمطية POSIX: تدعم |، []، *، +، إلخ، لكنها تستخدم أحرف البدل بنمط SQL % و _.

الميزة LIKE SIMILAR TO POSIX ~
حرف البدل % نعم نعم لا (استخدم .*)
حرف البدل _ نعم نعم لا (استخدم .)
التبديل ` ` لا نعم
فئة الأحرف [] لا نعم نعم
المحدد الكمي +*{m,n} لا جزئي كامل

(16) ▶ مثال

SQL
SELECT product_name, unit_price
FROM products
WHERE product_name SIMILAR TO '(Wireless|Bluetooth)%'
  AND is_active = true;

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(17) ▶ مثال

SQL
SELECT product_name, unit_price
FROM products
WHERE product_name !~ '(Refurbished|Used)'
  AND is_active = true;

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

7. المفهوم: تصفية الاستعلامات الفرعية بـ ANY / ALL / EXISTS

(1) ANY و ALL

المعامل المعنى الشكل المكافئ
col = ANY(array) يساوي أي قيمة في المصفوفة col IN (...)
col > ANY(array) أكبر من قيمة واحدة على الأقل
col > ALL(array) أكبر من جميع القيم

(18) ▶ مثال

SQL
SELECT product_name, unit_price
FROM products
WHERE category = ANY(ARRAY['Electronics', 'Books', 'Toys'])
  AND is_active = true;

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(19) ▶ مثال

SQL
SELECT product_name, unit_price, category
FROM products p
WHERE unit_price > ALL (
  SELECT unit_price FROM products
  WHERE category = 'Books' AND is_active = true
);

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) استعلام فرعي EXISTS

EXISTS تتحقق مما إذا كان الاستعلام الفرعي يعيد أي صفوف؛ لا تهتم بالقيم الفعلية، فقط بما إذا كانت هناك نتائج. إنها أكثر أماناً من IN (لا فخ NULL) وعادة أسرع للاستعلامات الفرعية المترابطة.

الاستخدام الوصف
EXISTS (subquery) TRUE إذا أعاد الاستعلام الفرعي صفوفاً
NOT EXISTS (subquery) TRUE إذا لم يعد الاستعلام الفرعي صفوفاً

(20) ▶ مثال

SQL
SELECT customer_id, customer_name
FROM customers c
WHERE EXISTS (
  SELECT 1 FROM orders o
  WHERE o.customer_id = c.customer_id
    AND o.order_date >= CURRENT_DATE - INTERVAL '30 days'
);

Output:

TEXT 📖 للعرض فقط
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(3) دليل اختيار IN مقابل EXISTS

السيناريو الموصى به السبب
مجموعة نتائج استعلام فرعي صغيرة IN المحسن ينفذ الاستعلام الفرعي أولاً، ببناء مجموعة صغيرة
جدول خارجي صغير، جدول استعلام فرعي كبير EXISTS المحسن ينفذ الاستعلام الفرعي لكل صف خارجي، يتوقف مبكراً
الاستعلام الفرعي قد يحتوي NULL EXISTS يتجنب فخ NOT IN NULL
استعلام فرعي مستقل (غير مترابط) IN نتيجة الاستعلام الفرعي يمكن تجسيدها

8. مخطط التدفق: اختيار مطابقة الأنماط

100%
flowchart TD
    A[بحاجة لمطابقة أنماط؟] --> B{حساس لحالة الأحرف؟}
    B -->|حساس| C{بحاجة لتعبير نمطي؟}
    B -->|غير حساس| D{بحاجة لتعبير نمطي؟}
    C -->|حرف بدل بسيط| E[LIKE]
    C -->|بحاجة لـ OR/فئة أحرف| F[SIMILAR TO]
    C -->|تعبير نمطي كامل| G["~ معامل"]
    D -->|حرف بدل بسيط| H[ILIKE]
    D -->|تعبير نمطي كامل| I["~* معامل"]
    E --> J[إعادة النتيجة]
    F --> J
    G --> J
    H --> J
    I --> J

    style A fill:#e1f5fe
    style J fill:#c8e6c9

9. مثال شامل

تنفذ Alice منطق الاستعلام الأساسي لميزة البحث في التجارة الإلكترونية.

SQL
-- الخطوة 1: بحث أساسي بالكلمة المفتاحية بـ ILIKE (غير حساس لحالة الأحرف)
SELECT product_id, product_name, unit_price, description
FROM products
WHERE is_active = true
  AND (product_name ILIKE '%keyboard%' OR description ILIKE '%keyboard%')
ORDER BY unit_price ASC;

-- الخطوة 2: بحث متقدم بتعبير نمطي للمستخدمين المتقدمين
SELECT product_id, product_name, unit_price, description
FROM products
WHERE is_active = true
  AND product_name ~* '(mechanical|wireless)\s*keyboard'
  AND unit_price BETWEEN 50 AND 300
ORDER BY unit_price DESC;

-- الخطوة 3: بحث المنتجات في فئات محددة مع معالجة NULL
SELECT product_id, product_name, unit_price, category
FROM products
WHERE category IN ('Electronics', 'Computer Accessories', 'Gaming')
  AND is_active = true
  AND discount_rate IS NOT NULL
  AND unit_price BETWEEN 20 AND 500
ORDER BY unit_price DESC NULLS LAST;

-- الخطوة 4: المنتجات التي لديها طلب مكتمل واحد على الأقل (EXISTS)
SELECT p.product_id, p.product_name, p.unit_price
FROM products p
WHERE p.is_active = true
  AND EXISTS (
    SELECT 1 FROM order_items oi
    JOIN orders o ON o.order_id = oi.order_id
    WHERE oi.product_id = p.product_id
      AND o.order_status = 'completed'
      AND o.order_date >= CURRENT_DATE - INTERVAL '90 days'
  )
  AND NOT EXISTS (
    SELECT 1 FROM product_flags pf
    WHERE pf.product_id = p.product_id
      AND pf.flag_type = 'recalled'
  )
ORDER BY p.unit_price DESC
FETCH FIRST 50 ROWS ONLY;

❓ أسئلة شائعة

س هل فجوة الأداء بين LIKE و ILIKE كبيرة؟
ج لأن ILIKE يجب أن تتجاهل حالة الأحرف، لا يمكنها استخدام فهرس B-tree القياسي. عند أحجام البيانات الكبيرة، استخدم امتداد pg_trgm في PostgreSQL لبناء فهرس GIN يسرع استعلامات ILIKE.
س ماذا لو أعاد استعلام فرعي NOT IN قيمة NULL؟
ج إذا كانت نتيجة الاستعلام الفرعي تحتوي NULL، قد يعيد NOT IN ككل نتيجة فارغة. الحلول: 1) استبدله بـ NOT EXISTS؛ 2) أضف WHERE col IS NOT NULL في الاستعلام الفرعي لتصفية NULLs.
س هل يمكن استخدام BETWEEN على TIMESTAMP؟
ج نعم، لكن لاحظ أن BETWEEN شاملة. لـ TIMESTAMP، BETWEEN '2025-01-01' AND '2025-01-31' يستبعد جزء الوقت من 31 يناير. فضّل >= AND < بدلاً من ذلك.
س أيهما أفضل، SIMILAR TO أم التعابير النمطية POSIX؟
ج SIMILAR TO هي مجموعة فرعية بمعيار SQL — قابلة للنقل لكن محدودة الميزات. التعابير النمطية POSIX (معامل ~) كاملة الميزات وهي الخيار الموصى به في مشاريع PostgreSQL.
س ما الفرق بين ANY و IN؟
ج col = ANY(مصفوفة) مكافئة وظيفياً لـ col IN (list)، لكن ANY تقبل وسيط مصفوفة ومصفوفات معادة من الاستعلامات الفرعية، وتدعم مقارنات غير عادية مثل > ANY و < ALL؛ IN تدعم المساواة فقط.
س SELECT 1 أم SELECT * في استعلام فرعي EXISTS؟
ج متطابقان وظيفياً — المحسن يتجاهل قائمة SELECT. SELECT 1 هو الشكل التقليدي وأكثر إيجازاً قليلاً؛ SELECT * جيد أيضاً، بدون فرق في الأداء.
س لماذا لا يمكن لـ LIKE '%keyword%' استخدام فهرس؟
ج حرف البدل البادئ '%keyword' يهزم فهرس B-tree لأن ترتيب الفهرس لا يمكن استغلاله. الحلول: فهرس pg_trgm GIN، البحث النصي الكامل (tsvector + tsquery)، أو محرك بحث متخصص.

📖 ملخص


📝 تمارين

  1. ⭐ اكتب استعلاماً يجد المنتجات في جدول products حيث category هي Electronics أو Books و is_active = true، مرتبة تنازلياً حسب unit_price.

  2. ⭐⭐ اكتب استعلاماً يستخدم ILIKE للبحث في product_name و description عن كلمة مفتاحية للمستخدم (مثلاً "wireless mouse")، مع تصفية unit_price BETWEEN 20 AND 200 واستبعاد المنتجات التي discount_rate IS NULL.

  3. ⭐⭐⭐ اكتب استعلاماً يستخدم EXISTS لإيجاد العملاء الذين قدموا طلباً في آخر 90 يوماً، ويستخدم NOT EXISTS لاستبعاد العملاء الموسومين بـ "suspended"، مرتباً حسب وقت تسجيل العميل تنازلياً، مع عرض أول 20 صفاً عبر تقسيم الصفحات.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%