اذهب إلى المحتوى

تنشر العديد من قواعد البيانات المعلومات على جداول مختلفة بحسب معناها وسياقها، وبالتالي قد نحتاج إلى الإشارة إلى أكثر من جدول واحد عند استرجاع معلومات حول البيانات الموجودة في قاعدة البيانات، لذا توفّر لغة الاستعلام البنيوية Structured Query Language -أو SQL اختصارًا- طرقًا متعددة لاسترجاع البيانات من جداول مختلفة مثل عمليات المجموعة Set Operations، وخاصةً معامل المجموعة UNION الذي تدعمه معظم أنظمة قواعد البيانات العلاقية Relational Database Systems والذي يأخذ نتائج استعلامين لأعمدة متطابقة ويدمجهما في استعلام واحد.

سنشرح في هذا المقال طريقة استخدام المعامل UNION لاسترجاع ودمج البيانات من أكثر من جدول، ونتعلم كيفية دمجه مع عملية الترشيح Filtering لترتيب النتائج.

مستلزمات العمل

يجب أن يكون لديك حاسوب يشغّل نظام إدارة قواعد البيانات العلاقية Relational Database Management System -أو RDBMS اختصارًا- المستند إلى لغة SQL. اختبرنا التعليمات والأمثلة الواردة في هذا المقال باستخدام البيئة التالية:

ملاحظة: تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية RDBMS لها تقديماتها الفريدة من لغة SQL، إذ ستعمل الأوامر المُقدّمة في هذا المقال بنجاح مع معظم هذه الأنظمة وهي جزء من صيغة لغة SQL المعيارية، ولكن قد تجد بعض الاختلافات في الصيغة أو الناتج عند اختبارها على أنظمة مختلفة عن MySQL.

سنحتاج أيضًا إلى قاعدة بيانات تحتوي على بعض الجداول المُحمَّلة ببيانات تجريبية نموذجية لنتمكّن من التدرب على استخدام عمليات UNION. وسنوضح في القسم التالي تفاصيل حول الاتصال بخادم MySQL وإنشاء قاعدة بيانات تجريبية، والتي سنستخدمها في أمثلة هذا المقال.

الاتصال بخادم MySQL وإعداد قاعدة بيانات تجريبية نموذجية

سنتصل في هذا القسم بخادم MySQL وسننشئ قاعدة بيانات تجريبية لنتمكّن من اتباع الأمثلة الواردة في هذا المقال.

إذا كانت قاعدة بيانات SQL الخاصة بنا تعمل على خادم بعيد، علينا الاتصال بالخادم مُستخدمين بروتوكول SSH من جهازنا المحلي كما يلي:

$ ssh user@your_server_ip

ثم نفتح واجهة سطر أوامر خادم MySQL مع وضع اسم حساب مستخدم MySQL الخاص بنا مكان user:

$ mysql -u user -p

ننشئ قاعدة بيانات بالاسم bookstore:

mysql> CREATE DATABASE bookstore;

إذا أُنشئِت قاعدة البيانات بنجاح، فسيظهر الخرج التالي:

الخرج
Query OK, 1 row affected (0.01 sec)

يمكن اختيار قاعدة البيانات bookstore من خلال تنفيذ تعليمة USE التالية:

mysql> USE bookstore;

وسيظهر الخرج التالي:

الخرج
Database changed

اخترنا قاعدة البيانات، وسننشئ عدة جداول تجريبية ضمنها، حيث سنستخدم في هذا المقال مكتبة افتراضية تقدّم خدمة شراء الكتب وتأجيرها، وتُدار كل خدمة من هاتين الخدمتين على حدة، وبالتالي ستُخزَّن البيانات المتعلقة بشراء الكتب وتأجيرها في جداول منفصلة.

ملاحظة: بسّطنا مخطط قاعدة البيانات لهذا المثال للتوضيح، إذ تكون هياكل الجداول أكثر تعقيدًا في الحياة الواقعية مع تضمين مفاتيح رئيسية Primary Keys ومفاتيح خارجية Foreign Keys ضمنها. ويمكن مطالعة مقال فهم قواعد البيانات العلاقية لمزيد من المعلومات حول كيفية تنظيم البيانات.

يحتوي الجدول الأول book_purchases على بيانات حول الكتب المُشتراة والعملاء الذين أجروا عمليات الشراء، إذ سيحتوي على أربعة أعمدة هي:

  • purchase_id: يحتوي هذا العمود على معرّف عملية الشراء ونمثّله بنوع البيانات int، وسيكون هذا العمود هو المفتاح الرئيسي للجدول، حيث تصبح كل قيمة معرّفًا فريدًا للصف الخاص بها
  • customer_name: يحتوي هذا العمود على اسم العميل، ونمثّله باستخدام نوع البيانات varchar بحد أقصى 30 محرفًا
  • book_title: يحتوي هذا العمود على عنوان الكتاب الذي جرى شراؤه، ونمثّله باستخدام نوع البيانات varchar بحد أقصى 200 محرف
  • date: يستخدم هذا العمود نوع البيانات date، ويحتوي على تاريخ كل عملية شراء

ننشئ هذا الجدول التجريبي باستخدام الأمر التالي:

mysql> CREATE TABLE book_purchases (
mysql>    purchase_id int,
mysql>    customer_name varchar(30),
mysql>    book_title varchar(40),
mysql>    date date,
mysql>    PRIMARY KEY (purchase_id)
mysql> );

إذا كان الخرج كما يلي، فهذا يعني إنشاء الجدول الأول بنجاح:

الخرج
Query OK, 0 rows affected (0.00 sec)

يحزّن الجدول الثاني book_leases معلومات حول الكتب المُستعارة، وتكون بنيته مشابهة للجدول السابق، ولكن نميّز عملية تأجير الكتاب بتاريخين مختلفين هما تاريخ التأجير ومدته، وبالتالي سيحتوي هذا الجدول على خمسة أعمدة هي:

  • lease_id: يحتوي هذا العمود على معرّف عملية التأجير، ونمثّله بنوع البيانات int، ويكون هذا العمود هو المفتاح الرئيسي للجدول، حيث تصبح كل قيمة معرّفًا فريدًا للصف الخاص بها
  • customer_name: يحتوي هذا العمود على اسم العميل، ونمثّله باستخدام نوع البيانات varchar بحد أقصى 30 محرفًا
  • book_title: يحتوي هذا العمود على عنوان الكتاب المُستعار، ونمثّله باستخدام نوع البيانات varchar بحد أقصى 200 محرف
  • date_from: يستخدم نوع البيانات date، ويحتوي هذا العمود على تاريخ بدء عملية التأجير
  • date_to: يستخدم نوع البيانات date، ويحتوي هذا العمود على تاريخ انتهاء عملية التأجير

لننشئ الجدول الثاني باستخدام الأمر التالي:

mysql> CREATE TABLE book_leases (
mysql>     lease_id int,
mysql>     customer_name varchar(30),
mysql>     book_title varchar(40),
mysql>     date_from date,
mysql>     date_to date,
mysql>     PRIMARY KEY (lease_id)
);

يؤكّد الخرج التالي إنشاء الجدول الثاني:

الخرج
Query OK, 0 rows affected (0.00 sec)

لنحمّل الآن الجدول book_purchases ببعض البيانات التجريبية من خلال تشغيل عملية INSERT INTO التالية:

mysql> INSERT INTO book_purchases
mysql> VALUES
mysql> (1, 'sammy', 'The Picture of Dorian Gray', '2023-10-01'),
mysql> (2, 'sammy', 'Pride and Prejudice', '2023-10-04'),
mysql> (3, 'sammy', 'The Time Machine', '2023-09-23'),
mysql> (4, 'bill', 'Frankenstein', '2023-07-23'),
mysql> (5, 'bill', 'The Adventures of Huckleberry Finn', '2023-10-01'),
mysql> (6, 'walt', 'The Picture of Dorian Gray', '2023-04-15'),
mysql> (7, 'walt', 'Frankenstein', '2023-10-13'),
mysql> (8, 'walt', 'Pride and Prejudice', '2023-10-19');

ستضيف عملية INSERT INTO السابقة 8 عمليات شراء مع قيمها المحدَّدة إلى جدول book_purchases، حيث يشير الخرج التالي إلى إضافة جميع الصفوف الثمانية:

الخرج
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

ثم ندخِل بعض البيانات التجريبية في الجدول book_leases كما يلي:

mysql> INSERT INTO book_leases
mysql> VALUES
mysql> (1, 'sammy', 'Frankenstein', '2023-09-14', '2023-11-14'),
mysql> (2, 'sammy', 'Pride and Prejudice', '2023-10-01', '2023-12-31'),
mysql> (3, 'sammy', 'The Adventures of Huckleberry Finn', '2023-10-01', '2023-12-01'),
mysql> (4, 'bill', 'The Picture of Dorian Gray', '2023-09-03', '2023-09-18'),
mysql> (5, 'bill', 'Crime and Punishment', '2023-09-27', '2023-12-05'),
mysql> (6, 'kim', 'The Picture of Dorian Gray', '2023-10-01', '2023-11-15'),
mysql> (7, 'kim', 'Pride and Prejudice', '2023-09-08', '2023-11-17'),
mysql> (8, 'kim', 'The Time Machine', '2023-09-04', '2023-10-23');

وسيظهر الخرج التالي الذي يؤكّد إضافة هذه البيانات:

الخرج
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

تتعلق عمليات التأجير والشراء بعملاء وعناوين كتب متماثلة، مما سيكون مفيدًا لتوضيح سلوك المعامل UNION، وبذلك سنكون جاهزين لمتابعة هذا المقال والبدء في استخدام عمليات UNION في لغة SQL.

فهم صيغة المعامل UNION

يطلب المعامل UNION في لغة SQL من قاعدة البيانات أخذ مجموعتي نتائج منفصلتين ومسترجعتين من استعلامات SELECT فردية ودمجهما في مجموعة نتائج واحدة تحتوي على صفوف مُعادة من كلا الاستعلامين.

ملاحظة: يمكن أن تكون استعلامات SELECT المستخدمة مع العملية UNION معقدة وتتطلب استخدام تعليمات JOIN أو دوال تجميعية Aggregations أو استعلامات الفرعية Subqueries. إذ يمكننا استخدام عمليات UNION لدمج نتائج الاستعلامات مهما كانت معقدة، ولكن للسهولة سنستخدم في أمثلتنا التالية استعلامات SELECT بسيطة للتركيز على كيفية تصرف المعامل UNION.

يوضّح المثال التالي الصيغة العامة لتعليمة SQL التي تتضمن المعامل UNION:

mysql> SELECT column1, column2 FROM table1
mysql> UNION
mysql> SELECT column1, column2 FROM table2;

تبدأ تعليمة SQL السابقة بتعليمة SELECT التي تعيد عمودين من الجدول table1، ونتبعها بالمعامل UNION وتعليمة SELECT ثانية، ويعيد استعلام SELECT الثاني أيضًا عمودين من الجدول table2. تخبر الكلمة المفتاحية UNION قاعدة البيانات بأخذ الاستعلامات السابقة واللاحقة، وتنفيذها تنفيذًا منفصلًا، ثم ضم مجموعات النتائج الخاصة بها ضمن مجموعة واحدة. يُعَد جزء الشيفرة البرمجية السابق بأكمله -بما في ذلك استعلامات SELECT والكلمة المفتاحية UNION بينهما- تعليمة SQL واحدة، لذلك لا ينتهي استعلام SELECT الأول بفاصلة منقوطة، والتي تظهر بعد اكتمال التعليمة فقط.

لنفترض مثلًا أننا نريد إدخال جميع العملاء الذين اشتروا كتابًا أو استأجروه، حيث يحتفظ الجدول book_purchases بسجلات عمليات الشراء، بينما يخزّن الجدول book_leases عمليات تأجير الكتب، لذا لنشغّل الاستعلام التالي:

mysql> SELECT customer_name FROM book_purchases
mysql> UNION
mysql> SELECT customer_name FROM book_leases;

وتكون مجموعة نتائج هذا الاستعلام كما يلي:

الخرج
+---------------+
| customer_name |
+---------------+
| sammy         |
| bill          |
| walt          |
| kim           |
+---------------+
4 rows in set (0.000 sec)

يشير هذا الخرج إلى أن Sammy و Bill و Walt و Kim اشتروا كتبًا أو استأجروها. دعونا نجرّب تنفيذ تعليمتي SELECT تنفيذًا منفصلًا، مرةً لعمليات الشراء ومرةً لعمليات التأجير لفهم كيفية إنشاء مجموعة هذه النتائج.

لنشغّل أولًا الاستعلام التالي لإعادة العملاء الذين اشتروا كتبًا فقط:

mysql> SELECT customer_name FROM book_purchases;

وسيكون الخرج كما يلي:

الخرج
+---------------+
| customer_name |
+---------------+
| sammy         |
| sammy         |
| sammy         |
| bill          |
| bill          |
| walt          |
| walt          |
| walt          |
+---------------+
8 rows in set (0.000 sec)

اشترى Sammy و Bill و Walt كتبًا، لكن Kim لم يفعل ذلك.

ثم لنشغّل الاستعلام التالي لإعادة العملاء الذين استأجروا كتبًا فقط:

mysql> SELECT customer_name FROM book_leases;

وسيكون الخرج كما يلي:

الخرج
+---------------+
| customer_name |
+---------------+
| sammy         |
| sammy         |
| sammy         |
| bill          |
| bill          |
| kim           |
| kim           |
| kim           |
+---------------+
8 rows in set (0.000 sec)

يشير الجدول book_leases إلى أن Sammy و Bill و Kim يستعيرون كتبًا، ولكن Walt لا يستعير كتبًا أبدًا. إذا جمعنا بين الإجابتين، فيمكننا الحصول على البيانات لكل من عمليات الشراء والتأجير. الفرق المهم بين استخدام عملية UNION وتنفيذ استعلامين تنفيذًا منفصلًا هو أن عملية UNION تزيل القيم المكررة، حيث تُدمَج النتائج دون تكرار أسماء العملاء في النتيجة.

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

تنفيذ عملية UNION مع عدد غير متطابق من الأعمدة

لنجرّب تنفيذ عملية UNION مع تعليمة SELECT تعيد عمودًا واحدًا وتعليمة SELECT أخرى تعيد عمودين:

mysql> SELECT purchase_id, customer_name FROM book_purchases
mysql> UNION
mysql> SELECT customer_name FROM book_leases;

سيستجيب خادم قاعدة البيانات بالخطأ التالي:

الخرج
The used SELECT statements have a different number of columns

لا يمكن إجراء عمليات UNION على مجموعات النتائج التي لها أعداد أعمدة مختلفة.

تنفيذ عملية UNION مع ترتيب غير متطابق للأعمدة

لنجرّب الآن تنفيذ عملية UNION مع تعلميتي SELECT تعيدان القيم نفسها ولكن بترتيب مختلف كما يلي:

mysql> SELECT customer_name, book_title FROM book_purchases
mysql> UNION
mysql> SELECT book_title, customer_name FROM book_leases;

لن يعيد خادم قاعدة البيانات خطأ، ولكن لن تكون مجموعة النتائج صحيحة كما يلي:

الخرج
+------------------------------------+------------------------------------+
| customer_name                      | book_title                         |
+------------------------------------+------------------------------------+
| sammy                              | The Picture of Dorian Gray         |
| sammy                              | Pride and Prejudice                |
| sammy                              | The Time Machine                   |
| bill                               | Frankenstein                       |
| bill                               | The Adventures of Huckleberry Finn |
| walt                               | The Picture of Dorian Gray         |
| walt                               | Frankenstein                       |
| walt                               | Pride and Prejudice                |
| Frankenstein                       | sammy                              |
| Pride and Prejudice                | sammy                              |
| The Adventures of Huckleberry Finn | sammy                              |
| The Picture of Dorian Gray         | bill                               |
| Crime and Punishment               | bill                               |
| The Picture of Dorian Gray         | kim                                |
| Pride and Prejudice                | kim                                |
| The Time Machine                   | kim                                |
+------------------------------------+------------------------------------+
16 rows in set (0.000 sec)

تدمج عملية UNION في هذا المثال العمود الأول من الاستعلام الأول مع العمود الأول من الاستعلام الثاني وتفعل الشيء نفسه بالنسبة للعمود الثاني، وبالتالي ستخلط أسماء العملاء وعناوين الكتب مع بعضها بعضًا.

استخدام الشروط وترتيب النتائج باستخدام UNION

دمجنا في المثال السابق مجموعات النتائج التي تمثل جميع الصفوف في جدولين متطابقين، ولكن ستحتاج في كثير من الأحيان إلى ترشيح الصفوف وفق شروط محددة قبل دمج النتائج، حيث يمكن لتعليمات SELECT المدموجة باستخدام المعامل UNION استخدام تعليمة WHERE لتطبيق ذلك.

لنفترض مثلًا أننا نريد معرفة الكتب التي يقرأها Sammy من خلال شرائها أو استئجارها، لذا نشغّل الاستعلام التالي:

mysql> SELECT book_title FROM book_purchases
mysql> WHERE customer_name = 'Sammy'
mysql> UNION
mysql> SELECT book_title FROM book_leases
mysql> WHERE customer_name = 'Sammy';

يتضمن كلا الاستعلامين تعليمة WHERE، مما يؤدي إلى ترشيح الصفوف من الجدولين المنفصلين لتضمين عمليات الشراء والتأجير التي أجراها Sammy فقط، وستكون نتائج هذا الاستعلام كما يلي:

الخرج
+------------------------------------+
| book_title                         |
+------------------------------------+
| The Picture of Dorian Gray         |
| Pride and Prejudice                |
| The Time Machine                   |
| Frankenstein                       |
| The Adventures of Huckleberry Finn |
+------------------------------------+
5 rows in set (0.000 sec)

تضمن عملية UNION عدم وجود تكرارات في قائمة النتائج. يمكننا استخدام تعليمات WHERE أيضًا لتحديد الصفوف المُعادة في استعلامي SELECT أو أحدهما فقط، ويمكن أن تشير تعليمة WHERE إلى أعمدة وشروط مختلفة في كل من الاستعلامين.

إضافةً لذلك، لا تتبع النتائج التي تعيدها عملية UNION ترتيبًا محدَّدًا، ولكن يمكننا تغيير ذلك من خلال استخدام تعليمة ORDER BY، حيث ينفَّذ الترتيب على النتائج النهائية المدموجة وليس على الاستعلامات الفردية. يمكننا فرز عناوين الكتب أبجديًا بعد استرجاع قائمة بجميع الكتب التي اشتراها أو استأجرها Sammy من خلال تنفيذ الاستعلام التالي:

mysql> SELECT book_title FROM book_purchases
mysql> WHERE customer_name = 'Sammy'
mysql> UNION
mysql> SELECT book_title FROM book_leases
mysql> WHERE customer_name = 'Sammy'
mysql> ORDER BY book_title;

وسيكون الخرج كما يلي:

الخرج
+------------------------------------+
| book_title                         |
+------------------------------------+
| Frankenstein                       |
| Pride and Prejudice                |
| The Adventures of Huckleberry Finn |
| The Picture of Dorian Gray         |
| The Time Machine                   |
+------------------------------------+
5 rows in set (0.001 sec)

نلاحظ أن إعادة النتائج بالترتيب يعتمد على العمود book_title الذي يحتوي على النتائج الناتجة عن دمج استعلامَي SELECT.

استخدام العملية UNION ALL للاحتفاظ بالنسخ المكررة

يزيل المعامل UNION تلقائيًا الصفوف المكررة من النتائج كما أظهرت الأمثلة السابقة، ولكن قد لا يكون هذا السلوك هو ما نريد تحقيقه باستخدام الاستعلام، فمثلًا لنفترض أننا نريد معرفة الكتب المُشتراة أو المستأجرة في 1 من شهر 11 من عام 2023، حيث يمكننا استرجاع هذه العناوين من خلال اتباع المثال التالي المشابه لما سبق:

mysql> SELECT book_title FROM book_purchases
mysql> WHERE date = '2022-10-01'
mysql> UNION
mysql> SELECT book_title FROM book_leases
mysql> WHERE date_from = '2022-10-01'
mysql> ORDER BY book_title;

وسنحصل على النتائج التالية:

الخرج
+------------------------------------+
| book_title                         |
+------------------------------------+
| Pride and Prejudice                |
| The Adventures of Huckleberry Finn |
| The Picture of Dorian Gray         |
+------------------------------------+
3 rows in set (0.001 sec)

عناوين الكتب المُعادة هنا صحيحة، ولكن لن تخبرنا النتائج فيما إذا كانت هذه الكتب مشتراة فقط أم مستأجرة فقط أم كليهما، حيث ستظهر عناوين الكتب في الجدولين book_purchases و book_leases في الحالات التي جرى فيها شراء بعض الكتب واستئجارها، ولكن ستُفقَد هذه المعلومات في النتيجة، لأن المعامل UNION يزيل الصفوف المكررة.

تمتلك لغة SQL طريقة لتغيير هذا السلوك والاحتفاظ بالصفوف المكررة، حيث يمكننا استخدام المعامل UNION ALL لدمج النتائج من استعلامين دون إزالة الصفوف المكررة، ويعمل المعامل UNION ALL بطريقة مشابهة للمعامل UNION، ولكن إذا وُجِدت تكرارات متعددة للقيم نفسها، فستكون جميعها موجودة في النتيجة.

لنشغّل الاستعلام السابق نفسه مع وضع UNION ALL مكان UNION كما يلي:

mysql> SELECT book_title FROM book_purchases
mysql> WHERE date = '2022-10-01'
mysql> UNION ALL
mysql> SELECT book_title FROM book_leases
mysql> WHERE date_from = '2022-10-01'
mysql> ORDER BY book_title;

ستكون القائمة الناتجة أطول هذه المرة كما يلي:

الخرج
+------------------------------------+
| book_title                         |
+------------------------------------+
| Pride and Prejudice                |
| The Adventures of Huckleberry Finn |
| The Adventures of Huckleberry Finn |
| The Picture of Dorian Gray         |
| The Picture of Dorian Gray         |
+------------------------------------+
5 rows in set (0.000 sec)

يظهَر الكتابان The Adventures of Huckleberry Finn و The Picture of Dorian Gray مرتين في مجموعة النتائج، وهذا يعني أنهما ظهرا في الجدولين book_purchases و book_leases، وبالتالي يمكننا افتراض أنه جرى تأجيرهما وشراؤهما في ذلك اليوم.

يمكن الاختيار بين المعاملين UNION و UNION ALL والتبديل بينهما اعتمادًا على ما إذا أردنا إزالة التكرارات أو الاحتفاظ بها.

ملاحظة: تنفيذ المعامل UNION ALL أسرع من تنفيذ المعامل UNION، إذ لا تحتاج قاعدة البيانات إلى فحص مجموعة النتائج للعثور على التكرارات. فإذا أردنا دمج نتائج استعلامات SELECT التي نعرف أنها لن تحتوي على أي صفوف مكررة، فيمكن أن يحقق استخدام المعامل UNION ALL أداءً أفضل مع مجموعات البيانات الأكبر حجمًا.

الخلاصة

تعرّفنا في هذا المقال على كيفية استرجاع البيانات من جداول متعددة باستخدام عمليات UNION و UNION ALL، واستخدمنا أيضًا تعليمات WHERE لترشيح النتائج وتعليمات ORDER BY لترتيبها، وتعلّمنا الأخطاء المحتمَلة والسلوكيات غير المتوقعة إذا أنتجت تعليمات SELECT تنسيقات بيانات مختلفة.

يجب أن تعمل الأوامر الواردة في هذا المقال على معظم قواعد البيانات العلاقية، ولكن يجب أن نتذكر أن كل قاعدة بيانات SQL تستخدم تقديمها الفريد للغة، لذا ننصح بالاطلاع على مقال مقارنة بين أنظمة إدارة قواعد البيانات العلاقية: SQLite مع MySQL مع PostgreSQL لمزيد من المعلومات، كما ننصح بالرجوع إلى توثيق RDBMS الرسمي للحصول على شرح مفصّل لكل أمر ومجموعة خياراته الكاملة.

ترجمة -وبتصرف- للمقال How To Use Unions in SQL لصاحبَيه Mateusz Papiernik و Rachel Lee.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...