PostgreSQL: التصفية المتقدمة ومطابقة الأنماط في PostgreSQL
آخر تحديث: 2026-08-26
1. ما ستتعلمه
- دمج شروط تصفية متعددة بـ
AND/OR/NOT - تصفية النطاقات و NULL بـ
IN/BETWEEN/IS NULL - مطابقة الأنماط بـ
LIKE/ILIKE(ميزة PostgreSQL غير الحساسة لحالة الأحرف) SIMILAR TOومعاملات التعابير النمطية~/~*/!~/!~*- تصفية الاستعلامات الفرعية بـ
ANY/ALL/EXISTS
2. القصة
Alice هي مطورة ميزة البحث في منصة تجارة إلكترونية. عندما يكتب المستخدم كلمة مفتاحية في صندوق البحث، يحتاج النظام إلى:
- مطابقة تقريبية ضد أسماء المنتجات والأوصاف
- دعم بحث غير حساس لحالة الأحرف
- السماح للمستخدمين المتقدمين باستخدام التعابير النمطية للبحث الدقيق
- تصفية المنتجات المسحوبة
- ترتيب النتائج حسب الصلة
تحتاج Alice إلى إتقان أدوات التصفية ومطابقة الأنماط المتنوعة التي تقدمها PostgreSQL لبناء نظام البحث هذا.
3. المفهوم: تركيبات AND / OR / NOT
(1) أولوية المعاملات المنطقية
| المعامل | الأولوية | الوصف |
|---|---|---|
NOT |
الأعلى | النفي |
AND |
المتوسط | كلا الشرطين صحيح |
OR |
الأدنى | أي من الشرطين صحيح |
(1) ▶ مثال
SELECT product_name, unit_price, category
FROM products
WHERE category = 'Electronics'
AND unit_price > 500
AND is_active = true;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) ▶ مثال
SELECT product_name, unit_price, category
FROM products
WHERE category = 'Electronics'
OR category = 'Books'
OR category = 'Toys';
Output:
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) ▶ مثال
SELECT product_name, unit_price, category
FROM products
WHERE is_active = true
AND (category = 'Electronics' OR category = 'Books');
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) NOT النفي
NOT تنفي أي تعبير منطقي وغالباً ما تقترن مع IN، BETWEEN، LIKE، إلخ.
(4) ▶ مثال
SELECT product_name, unit_price
FROM products
WHERE NOT category = 'Electronics'
AND is_active = true;
Output:
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) ▶ مثال
SELECT product_name, unit_price, category
FROM products
WHERE category IN ('Electronics', 'Books', 'Toys')
AND is_active = true;
Output:
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) ▶ مثال
SELECT product_name, unit_price
FROM products
WHERE unit_price BETWEEN 100 AND 500
ORDER BY unit_price;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(7) ▶ مثال
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:
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) ▶ مثال
SELECT product_name, unit_price, discount_rate
FROM products
WHERE discount_rate IS NULL
AND is_active = true;
Output:
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) ▶ مثال
-- خطر: 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:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
5. المفهوم: مطابقة الأنماط LIKE / ILIKE
(1) أحرف البدل في LIKE
| حرف البدل | المعنى | مثال |
|---|---|---|
% |
يطابق أي سلسلة بأي طول (بما في ذلك الفارغة) | 'Phone%' يطابق Phone، Phone Case |
_ |
يطابق حرفاً واحداً | 'A_c' يطابق Arc، ABC |
(10) ▶ مثال
SELECT product_name, unit_price
FROM products
WHERE product_name LIKE 'Wireless%'
AND is_active = true;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(11) ▶ مثال
SELECT product_name, unit_price
FROM products
WHERE product_name LIKE '%Battery%'
AND is_active = true;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) ILIKE: غير حساسة لحالة الأحرف (ميزة PostgreSQL)
ILIKE هي امتداد PostgreSQL يطابق بدون حساسية لحالة الأحرف، مكافئة لـ LIKE + LOWER().
| المعامل | حساس لحالة الأحرف | SQL قياسي |
|---|---|---|
LIKE |
حساس | نعم |
ILIKE |
غير حساس | لا (PG فقط) |
(12) ▶ مثال
SELECT product_name, unit_price, description
FROM products
WHERE (product_name ILIKE '%iphone%'
OR description ILIKE '%iphone%')
AND is_active = true;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) تهريب أحرف البدل
عندما تحتاج للبحث عن % أو _ حرفياً، استخدم ESCAPE لتحديد حرف تهريب.
(13) ▶ مثال
SELECT product_name, description
FROM products
WHERE description LIKE '%100\%%' ESCAPE '\';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
6. المفهوم: مطابقة التعابير النمطية
(1) نظرة عامة على معاملات التعابير النمطية
توفر PostgreSQL أربعة معاملات مطابقة للتعابير النمطية، بالإضافة إلى صيغة SIMILAR TO.
| المعامل | حساس لحالة الأحرف | الوصف |
|---|---|---|
~ |
حساس | يطابق التعبي�� النمطي |
~* |
غير حساس | يطابق التعبي�� النمطي (متجاهل الحالة) |
!~ |
حساس | لا يطابق التعبي�� النمطي |
!~* |
غير حساس | لا يطابق التعبي�� النمطي (متجاهل الحالة) |
SIMILAR TO |
حساس | مجموعة فرعية من التعابير النمطية بمعيار SQL (تدعم % _ ` |
(14) ▶ مثال
SELECT product_name, unit_price
FROM products
WHERE product_name ~ '^(Wireless|Bluetooth)'
AND is_active = true;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(15) ▶ مثال
SELECT product_name, unit_price
FROM products
WHERE product_name ~* 'iphone\s*(1[0-9])?'
AND is_active = true;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) SIMILAR TO
SIMILAR TO تقع بين LIKE والتعابير النمطية POSIX: تدعم |، []، *، +، إلخ، لكنها تستخدم أحرف البدل بنمط SQL % و _.
| الميزة | LIKE | SIMILAR TO | POSIX ~ |
|---|---|---|---|
حرف البدل % |
نعم | نعم | لا (استخدم .*) |
حرف البدل _ |
نعم | نعم | لا (استخدم .) |
| التبديل ` | ` | لا | نعم |
فئة الأحرف [] |
لا | نعم | نعم |
المحدد الكمي +*{m,n} |
لا | جزئي | كامل |
(16) ▶ مثال
SELECT product_name, unit_price
FROM products
WHERE product_name SIMILAR TO '(Wireless|Bluetooth)%'
AND is_active = true;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(17) ▶ مثال
SELECT product_name, unit_price
FROM products
WHERE product_name !~ '(Refurbished|Used)'
AND is_active = true;
Output:
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) ▶ مثال
SELECT product_name, unit_price
FROM products
WHERE category = ANY(ARRAY['Electronics', 'Books', 'Toys'])
AND is_active = true;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(19) ▶ مثال
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:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) استعلام فرعي EXISTS
EXISTS تتحقق مما إذا كان الاستعلام الفرعي يعيد أي صفوف؛ لا تهتم بالقيم الفعلية، فقط بما إذا كانت هناك نتائج. إنها أكثر أماناً من IN (لا فخ NULL) وعادة أسرع للاستعلامات الفرعية المترابطة.
| الاستخدام | الوصف |
|---|---|
EXISTS (subquery) |
TRUE إذا أعاد الاستعلام الفرعي صفوفاً |
NOT EXISTS (subquery) |
TRUE إذا لم يعد الاستعلام الفرعي صفوفاً |
(20) ▶ مثال
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:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) دليل اختيار IN مقابل EXISTS
| السيناريو | الموصى به | السبب |
|---|---|---|
| مجموعة نتائج استعلام فرعي صغيرة | IN |
المحسن ينفذ الاستعلام الفرعي أولاً، ببناء مجموعة صغيرة |
| جدول خارجي صغير، جدول استعلام فرعي كبير | EXISTS |
المحسن ينفذ الاستعلام الفرعي لكل صف خارجي، يتوقف مبكراً |
| الاستعلام الفرعي قد يحتوي NULL | EXISTS |
يتجنب فخ NOT IN NULL |
| استعلام فرعي مستقل (غير مترابط) | IN |
نتيجة الاستعلام الفرعي يمكن تجسيدها |
8. مخطط التدفق: اختيار مطابقة الأنماط
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 منطق الاستعلام الأساسي لميزة البحث في التجارة الإلكترونية.
-- الخطوة 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;
❓ أسئلة شائعة
📖 ملخص
- انتبه لأولوية المعاملات مع
AND/OR/NOT؛ استخدم الأقواس للتحكم الصريح في المنطق INتناسب مساواة القيم المتعددة؛BETWEENتناسب تصفية النطاقات؛IS NULLتعالج القيم المفقودةLIKEتطابق أحرف البدل البسيطة؛ILIKEغير حساسة لحالة الأحرف (PostgreSQL فقط)- التعابير النمطية POSIX
~/~*توفر قوة التعابير النمطية الكاملة؛SIMILAR TOتقع بين LIKE والتعابير الن��طية EXISTSأكثر أماناً منIN(لا فخ NULL) وعادة أسرع للاستعلامات الفرعية المترابطةANY/ALLتوفر مقارنات كمية عبر المصفوفات / الاستعلامات الفرعية
📝 تمارين
-
⭐ اكتب استعلاماً يجد المنتجات في جدول
productsحيثcategoryهي Electronics أو Books وis_active = true، مرتبة تنازلياً حسبunit_price. -
⭐⭐ اكتب استعلاماً يستخدم
ILIKEللبحث فيproduct_nameوdescriptionعن كلمة مفتاحية للمستخدم (مثلاً "wireless mouse")، مع تصفيةunit_price BETWEEN 20 AND 200واستبعاد المنتجات التيdiscount_rate IS NULL. -
⭐⭐⭐ اكتب استعلاماً يستخدم
EXISTSلإيجاد العملاء الذين قدموا طلباً في آخر 90 يوماً، ويستخدمNOT EXISTSلاستبعاد العملاء الموسومين بـ "suspended"، مرتباً حسب وقت تسجيل العميل تنازلياً، مع عرض أول 20 صفاً عبر تقسيم الصفحات.