PostgreSQL: معاملات وتعابير PostgreSQL

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

1. ما ستتعلمه


2. القصة

Charlie هو محلل مالي في منصة SaaS، مسؤول عن:

  1. حساب المبلغ بعد الضريبة لكل طلب (total * 1.08)
  2. التحقق مما إذا كان الطلب يقع ضمن نطاق تاريخ معين
  3. تحويل التواريخ المنسقة كنصوص إلى نوع DATE للمقارنة
  4. التحقق مما إذا كانت نطاقات الأسعار تتداخل

هذه المهام تتطلب منه إتقان معاملات وتعابير PostgreSQL المختلفة.


3. المفهوم: المعاملات الحسابية

(1) المعاملات الحسابية الأساسية

المعامل المعنى مثال النتيجة
+ جمع 100 + 8 108
- طرح 500 - 50 450
* ضرب 29.99 * 3 89.97
/ قسمة صحيحة (اقتطاع) 7 / 2 3
/ قسمة عشرية (بعلامة عشرية) 7.0 / 2 3.5
% باقي القسمة 10 % 3 1
^ أس 2 ^ 10 1024
` /` جذر تربيعي `
` /` جذر تكعيبي
! عاملي 5 ! 120
@ قيمة مطلقة @ -15 15

(1) ▶ مثال

SQL
SELECT
  order_id,
  total_amount,
  total_amount * 0.08 AS tax,
  total_amount * 1.08 AS total_with_tax
FROM orders
WHERE order_status = 'completed';

Output:

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

(2) ▶ مثال

SQL
SELECT
  7 / 2 AS int_div,
  7.0 / 2 AS numeric_div,
  7 / 2.0 AS numeric_div2,
  CAST(7 AS NUMERIC) / 2 AS cast_div;

Output:

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

(2) أولوية المعاملات

الأولوية المعامل الوصف
1 (أعلى) . وصول العضو
2 :: تحويل النوع
3 [ فهرس المصفوفة
4 - (أحادي)، @، ! عمليات أحادية
5 ^ أس
6 *، /، % ضرب / قسمة / باقي
7 +، - جمع / طرح
8 `
9 معاملات المقارنة = <> < > <= >=
10 IS، IN، BETWEEN مسندات
11 NOT نفي منطقي
12 AND و منطقي
13 (أدنى) OR أو منطقي

(3) ▶ مثال

SQL
SELECT
  2 + 3 * 4 AS no_parens,
  (2 + 3) * 4 AS with_parens;

Output:

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

(3) التجاوز والدقة

نطاق INTEGER في PostgreSQL من حوالي -2.1 مليار إلى 2.1 مليار؛ تجاوزه يثير خطأ. لحسابات الأموال، استخدم NUMERIC(dقة, مقياس) أو DECIMAL.

النوع النطاق الدقة الأنسب لـ
SMALLINT -32768 ~ 32767 صحيح تعدادات صغيرة النطاق
INTEGER ±2.1 مليار صحيح أعداد صحيحة عامة
BIGINT ±9.2 كوينتيليون صحيح معرفات كبيرة النطاق
NUMERIC(p,s) غير محدود دقة اعتباطية حسابات الأموال

(4) ▶ مثال

SQL
SELECT
  order_id,
  total_amount::NUMERIC(12,2) AS amount,
  (total_amount::NUMERIC(12,2) * 0.08)::NUMERIC(12,2) AS tax,
  (total_amount::NUMERIC(12,2) * 1.08)::NUMERIC(12,2) AS total_with_tax
FROM orders;

Output:

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

4. المفهوم: معاملات المقارنة

(1) المقارنة الأساسية

المعامل المعنى 1 <> 2 NULL = NULL
= يساوي FALSE NULL
<> / != لا يساوي TRUE NULL
< أصغر من TRUE NULL
> أكبر من FALSE NULL
<= أصغر من أو يساوي TRUE NULL
>= أكبر من أو يساوي FALSE NULL

أي مقارنة مع NULL تعطي NULL، وليس TRUE أو FALSE.

(5) ▶ مثال

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

Output:

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

(2) BETWEEN والمقارنات المركبة

التعبير المكافئ
x BETWEEN a AND b x >= a AND x <= b
x NOT BETWEEN a AND b x < a OR x > b

(6) ▶ مثال

SQL
SELECT order_id, total_amount, order_date
FROM orders
WHERE total_amount BETWEEN 100 AND 1000
  AND order_date BETWEEN '2025-01-01' AND '2025-12-31';

Output:

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

(3) IS DISTINCT FROM

IS DISTINCT FROM يعامل NULL كقيمة قابلة للمقارنة، متجنبًا مأزق NULL في المقارنات القياسية.

التعبير NULL = NULL NULL IS DISTINCT FROM NULL
النتيجة NULL FALSE
1 IS DISTINCT FROM NULL TRUE

(7) ▶ مثال

SQL
SELECT order_id, old_status, new_status
FROM order_status_log
WHERE old_status IS DISTINCT FROM new_status;

Output:

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

5. المفهوم: المعاملات المنطقية

(1) جدول حقيقة المنطق ثلاثي القيم

تستخدم PostgreSQL منطقًا ثلاثي القيم (TRUE / FALSE / NULL)؛ عندما يشارك NULL في عملية منطقية، يمكن أن تكون النتيجة غير بديهية.

| a | b | a AND b | a OR b | |---|---|---|---|---------|--------| | T | T | T | T | | T | F | F | T | | T | N | N | T | | F | T | F | T | | F | F | F | F | | F | N | F | N | | N | T | N | T | | N | F | F | N | | N | N | N | N |

(8) ▶ مثال

SQL
SELECT
  TRUE AND NULL AS and_result,
  TRUE OR NULL AS or_result,
  FALSE AND NULL AS and_false,
  FALSE OR NULL AS or_null,
  NOT NULL AS not_result;

Output:

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

(9) ▶ مثال

SQL
-- هذا يستبعد الصفوف حيث discount_rate هو NULL
SELECT product_name, discount_rate
FROM products
WHERE discount_rate > 0 OR discount_rate = 0;
-- صفوف NULL غير مضمنة لأن NULL > 0 هو NULL، NULL = 0 هو NULL

-- إصلاح: معالجة NULL صراحة
SELECT product_name, discount_rate
FROM products
WHERE discount_rate > 0 OR discount_rate = 0 OR discount_rate IS NULL;

Output:

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

6. المفهوم: معاملات السلاسل

(1) دمج السلاسل ||

|| هو معامل دمج السلاسل في PostgreSQL، على عكس + في SQL Server.

المعامل المعنى مثال
` `

(10) ▶ مثال

SQL
SELECT
  customer_id,
  first_name || ' ' || last_name AS full_name,
  'Order #' || order_id || ': $' || total_amount::TEXT AS order_summary
FROM orders o
JOIN customers c ON c.customer_id = o.customer_id;

Output:

TEXT 📖 للعرض فقط
  result  
----------
   42.50
(1 row)

(2) دمج السلاسل مع NULL

دمج أي سلسلة مع NULL يعطي NULL؛ استخدم COALESCE للتعامل معه.

التعبير النتيجة
`'A'
`'A'

(11) ▶ مثال

SQL
SELECT
  product_name || COALESCE(' - ' || description, '') AS product_info
FROM products;

Output:

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

7. المفهوم: تحويلات الأنواع

(1) :: مقابل CAST()

الصيغة مثال SQL قياسي الوصف
::type '2025-01-01'::DATE لا (خاص بـ PG) موجز، موصى به في PG
CAST(expr AS type) CAST('2025-01-01' AS DATE) نعم قابل للنقل، مطول

(12) ▶ مثال

SQL
SELECT
  order_id,
  order_date::DATE AS date_only,
  '2025-06-15'::DATE AS literal_date,
  CAST('2025-06-15 14:30:00' AS TIMESTAMP) AS full_timestamp
FROM orders
WHERE order_date::DATE >= '2025-01-01'::DATE;

Output:

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

(13) ▶ مثال

SQL
SELECT
  total_amount::NUMERIC(12,2) AS rounded,
  total_amount::INTEGER AS truncated,
  total_amount::TEXT AS as_text
FROM orders;

Output:

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

(2) سيناريوهات التحويل الشائعة

المصدر → الهدف الاستخدام
TEXT → DATE '2025-01-01'::DATE
TEXT → TIMESTAMP CAST(ts AS TIMESTAMP)
NUMERIC → TEXT 123.45::TEXT
INTEGER → NUMERIC id::NUMERIC
TEXT → INTEGER '42'::INTEGER

(14) ▶ مثال

SQL
SELECT order_id, total_amount, order_date
FROM orders
WHERE order_date >= '2025-01-01'::DATE
  AND order_date < '2025-07-01'::DATE
  AND total_amount::NUMERIC(12,2) > 500;

Output:

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

8. المفهوم: منشئو الصفوف وتعابير الاستعلام الفرعي

(1) منشئ ROW

ROW(val1, val2, ...) يبني قيمة صف مجهولة، مفيدة لمقارنات مستوى الصف.

(15) ▶ مثال

SQL
SELECT order_id, order_status, total_amount
FROM orders
WHERE ROW(order_status, total_amount) = ROW('completed', 500);

Output:

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

(2) مقارنة الصف و IN

يمكن إقران منشئ الصف مع IN للمطابقة متعددة الأعمدة.

(16) ▶ مثال

SQL
SELECT order_id, customer_id, order_status
FROM orders
WHERE (order_status, customer_id) IN (
  ('completed', 1001),
  ('completed', 1002),
  ('pending', 1003)
);

Output:

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

(3) مقارنة تعبير الاستعلام الفرعي

التعبير الدلالة نوع الإرجاع
col IN (subquery) يساوي أي قيمة في نتيجة الاستعلام الفرعي BOOLEAN
col = ANY(subquery) نفس IN BOOLEAN
col > ALL(subquery) أكبر من جميع القيم في الاستعلام الفرعي BOOLEAN
EXISTS (subquery) ما إذا كان الاستعلام الفرعي أعاد أي صفوف BOOLEAN

(17) ▶ مثال

SQL
SELECT product_name, unit_price, category
FROM products
WHERE unit_price > ALL (
  SELECT AVG(unit_price) FROM products GROUP BY category
);

Output:

TEXT 📖 للعرض فقط
  result  
----------
   42.50
(1 row)

9. المفهوم: معاملات النطاق

(1) أنواع النطاق

لدى PostgreSQL أنواع نطاق مدمجة مثل int4range و int8range و numrange و daterange و tstzrange.

(18) ▶ مثال

SQL
SELECT daterange('2025-01-01'::DATE, '2025-06-30'::DATE) AS h1_range;

Output:

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

(2) مرجع معاملات النطاق

المعامل المعنى مثال
@> يحتوي عنصر '[1,5]'::int4range @> 3 → TRUE
<@ محتوى من 3 <@ '[1,5]'::int4range → TRUE
&& يتداخل '[1,5]'::int4range && '[3,8]'::int4range → TRUE
&< لا يمتد يمين '[1,3]'::int4range &< '[2,5]'::int4range → TRUE
&> لا يمتد يسار '[3,5]'::int4range &> '[1,3]'::int4range → TRUE
`- -` مجاور لـ
+ اتحاد '[1,3]'::int4range + '[3,6]'::int4range[1,6)
* تقاطع '[1,5]'::int4range * '[3,8]'::int4range[3,5)
- فرق '[1,5]'::int4range - '[3,8]'::int4range[1,3)

(19) ▶ مثال

SQL
SELECT o1.order_id, o2.order_id
FROM orders o1, orders o2
WHERE o1.customer_id = o2.customer_id
  AND o1.order_id < o2.order_id
  AND daterange(o1.order_date, o1.delivery_date) &&
      daterange(o2.order_date, o2.delivery_date);

Output:

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

(20) ▶ مثال

SQL
SELECT product_name, unit_price
FROM products
WHERE numrange(100, 500) @> unit_price::NUMERIC;

Output:

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

10. مخطط تدفق: قرار تحويل النوع

100%
flowchart TD
    A[تحتاج تحويل نوع؟] --> B{النوع الهدف؟}
    B -->|تاريخ/وقت| C["::DATE / ::TIMESTAMP"]
    B -->|رقمي| D["::NUMERIC / ::INTEGER"]
    B -->|سلسلة| E["::TEXT"]
    B -->|تحتاج قابلية نقل| F["CAST(expr AS type)"]
    C --> G{هل المصدر له تنسيق؟}
    G -->|تنسيق قياسي| H[تحويل :: مباشر]
    G -->|تنسيق مخصص| I["TO_DATE() / TO_TIMESTAMP()"]
    D --> J[انتبه لفقدان الدقة]
    E --> K[انتبه لدمج NULL]
    H --> L[تم]
    I --> L
    J --> L
    K --> L
    F --> L

    style A fill:#e1f5fe
    style L fill:#c8e6c9

11. مثال شامل

أكمل Charlie جميع الحسابات التي تطلبها تحليله المالي.

SQL
-- الخطوة 1: حساب الضريبة مع التحكم في الدقة
SELECT
  order_id,
  total_amount::NUMERIC(12,2) AS amount,
  (total_amount::NUMERIC(12,2) * 0.08)::NUMERIC(12,2) AS tax,
  (total_amount::NUMERIC(12,2) * 1.08)::NUMERIC(12,2) AS total_with_tax
FROM orders
WHERE order_status = 'completed'
  AND order_date >= '2025-01-01'::DATE;

-- الخطوة 2: فحص نطاق التاريخ بـ daterange
SELECT order_id, customer_id, order_date, total_amount
FROM orders
WHERE daterange('2025-01-01'::DATE, '2025-07-01'::DATE) @> order_date::DATE
  AND order_status <> 'cancelled';

-- الخطوة 3: تحويل سلسلة إلى تاريخ وتنسيق
SELECT
  order_id,
  'Order #' || order_id || ' - ' ||
  COALESCE(customer_name, 'Unknown') ||
  ' ($' || total_amount::TEXT || ')' AS order_label
FROM orders o
LEFT JOIN customers c ON c.customer_id = o.customer_id
WHERE o.order_date::DATE >= '2025-01-01'::DATE
ORDER BY o.total_amount DESC;

-- الخطوة 4: منشئ صف وتداخل نطاق
SELECT
  p1.product_name AS product_a,
  p2.product_name AS product_b,
  numrange(p1.unit_price, p1.unit_price * 1.2) AS price_range_a,
  numrange(p2.unit_price, p2.unit_price * 1.2) AS price_range_b
FROM products p1
JOIN products p2 ON p1.product_id < p2.product_id
WHERE p1.category = p2.category
  AND numrange(p1.unit_price, p1.unit_price * 1.2) &&
      numrange(p2.unit_price, p2.unit_price * 1.2)
  AND p1.is_active = true AND p2.is_active = true;

❓ أسئلة شائعة

س هل القسمة الصحيحة 7/2 تعيد 3 أم 3.5؟
ج تعيد 3. في PostgreSQL، قسمة عددين صحيحين تقتطع إلى عدد صحيح. للحصول على 3.5، يجب أن يكون أحد المعاملين على الأقل نوعًا عشريًا: 7.0/2 أو 7::NUMERIC/2.
س أيهما أفضل، :: أم CAST()؟
ج :: أكثر إيجازًا وهو صيغة خاصة بـ PostgreSQL؛ CAST() هو الصيغة القياسية في SQL. لمشاريع PostgreSQL الخالصة، يوصى بـ ::؛ استخدم CAST() عندما تحتاج توافقًا عبر قواعد البيانات.
س ما هي مآزق NULL في العمليات المنطقية؟
ج TRUE AND NULL = NULL (ليس TRUE)، FALSE OR NULL = NULL (ليس FALSE)، NOT NULL = NULL. في WHERE، يعامل NULL على أنه ليس TRUE، لذا يتم استبعاد الصفوف ذات الصلة.
س لماذا دمج سلسلة مع NULL يعطي NULL؟
ج معيار SQL يقول إن أي دمج سلسلة مع NULL ينتج NULL. استبدل NULL بـ COALESCE(col, '') قبل الدمج.
س متى تكون مقارنة منشئ الصف مفيدة؟
ج عندما تحتاج مقارنة تركيبة من عدة أعمدة دفعة واحدة — مثلاً WHERE (status, amount) = ROW('completed', 500) — إنها أنظف من مقارنة عمودين منفصلين، وتعمل أيضًا مع IN متعدد الأعمدة.
س هل تعمل معاملات النطاق فقط على أنواع Range المدمجة؟
ج نعم، المعاملات مثل @> <@ && تعمل على أنواع Range المدمجة في PostgreSQL (int4range, numrange, daterange، إلخ). يجب أولاً تحويل الأعمدة العادية إلى قيمة Range قبل المقارنة.
س هل معامل الأس ^ له أولوية أعلى من الضرب/القسمة؟
ج نعم، ^ يرتبط أقوى من * / %. 2^3^2 = 2^(3^2) = 512 (ترابطي يمين)، ليس (2^3)^2 = 64.
س ما الفرق بين IS DISTINCT FROM و <>؟
ج مع <>، NULL <> NULL يعيد NULL (ليس TRUE ولا FALSE). IS DISTINCT FROM يعامل NULL كقيمة قابلة للمقارنة: NULL IS DISTINCT FROM NULL يعيد FALSE، و 1 IS DISTINCT FROM NULL يعيد TRUE.

📖 ملخص


📝 تمارين

  1. ⭐ اكتب استعلامًا يحسب unit_price * quantity لكل صف في order_items، ويحول النتيجة إلى NUMERIC(10,2)، ويرتب تنازليًا حسب مبلغ السطر.

  2. ⭐⭐ اكتب استعلامًا يستخدم daterange والمعامل @> لإيجاد الطلبات التي يقع order_date فيها في الربع الأول 2025 (1 يناير – 31 مارس)، واستخدم || لبناء سلسلة ملخص طلب (تنسيق: "Order #id - customer_name - $amount")، مع التعامل مع أسماء العملاء NULL.

  3. ⭐⭐⭐ اكتب استعلامًا يجد أزواج المنتجات في نفس الفئة التي تتداخل نطاقات أسعارها (ابنِ النطاق بـ numrange(unit_price, unit_price * 1.2)، وتحقق من التداخل بـ &&)، وأظهر ما إذا كانت مقارنة منشئ الصف ROW(a.price, a.category) = ROW(b.price, b.category) متساوية لكل زوج.

Web-Tutorial.com

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

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

100%