الانتقال إلى المحتوى الرئيسي
تُنشئ عبارة JOIN جدولًا جديدًا من خلال دمج الأعمدة من جدول واحد أو عدة جداول باستخدام القيم المشتركة بينها. وهي عملية شائعة في قواعد البيانات التي تدعم SQL، وتقابل عملية join في الجبر العلاقي. وغالبًا ما يُشار إلى الحالة الخاصة المتمثلة في ربط جدول بنفسه باسم “self-join”. الصياغة
SELECT <expr_list>
FROM <left_table>
[GLOBAL] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI|ANY|ALL|ASOF] JOIN <right_table>
(ON <expr_list>)|(USING <column_list>) ...
تُسمّى التعبيرات الواردة في عبارة ON والأعمدة الواردة في عبارة USING “مفاتيح الربط”. ما لم يُذكر خلاف ذلك، فإن JOIN يُنتج حاصل ضرب ديكارتيًا من الصفوف ذات “مفاتيح الربط” المتطابقة، وقد يؤدي ذلك إلى نتائج تضم عددًا من الصفوف أكبر بكثير من الجداول المصدرية.

أنواع JOIN المدعومة

جميع أنواع SQL JOIN القياسية مدعومة:
TypeDescription
INNER JOINتُعاد الصفوف المتطابقة فقط.
LEFT OUTER JOINتُعاد الصفوف غير المتطابقة من الجدول الأيسر بالإضافة إلى الصفوف المتطابقة.
RIGHT OUTER JOINتُعاد الصفوف غير المتطابقة من الجدول الأيمن بالإضافة إلى الصفوف المتطابقة.
FULL OUTER JOINتُعاد الصفوف غير المتطابقة من كلا الجدولين بالإضافة إلى الصفوف المتطابقة.
CROSS JOINيُنتج حاصل الضرب الديكارتي للجدولين بالكامل، ولا تُحدَّد “مفاتيح الربط”.
NATURAL JOINيربط تلقائيًا جميع الأعمدة ذات الاسم نفسه في كلا الجدولين؛ ويظهر كل عمود مشترك مرة واحدة في النتيجة. ويدعم الصيغ INNER (الافتراضية) وLEFT وRIGHT وFULL. وهو مكافئ لـ JOIN ... USING (col1, col2, ...) حيث تُشتق قائمة الأعمدة تلقائيًا.
  • يشير JOIN من دون تحديد نوع إلى INNER.
  • يمكن حذف الكلمة المفتاحية OUTER بأمان.
  • من الصيغ البديلة لـ CROSS JOIN تحديد عدة جداول في FROM clause مفصولة بفواصل.
  • إذا لم تكن هناك أعمدة متطابقة لـ NATURAL JOIN، فإنه يعمل مثل CROSS JOIN.
تتوفر أيضًا في ClickHouse أنواع JOIN إضافية:
TypeDescription
LEFT SEMI JOIN, RIGHT SEMI JOINقائمة سماح على “مفاتيح الربط”، من دون إنتاج حاصل ضرب ديكارتي.
LEFT ANTI JOIN, RIGHT ANTI JOINقائمة حظر على “مفاتيح الربط”، من دون إنتاج حاصل ضرب ديكارتي.
LEFT ANY JOIN, RIGHT ANY JOIN, INNER ANY JOINيعطّل جزئيًا (للجانب المقابل من LEFT وRIGHT) أو كليًا (بالنسبة إلى INNER وFULL) حاصل الضرب الديكارتي لأنواع JOIN القياسية.
ASOF JOIN, LEFT ASOF JOINربط للتسلسلات مع مطابقة غير تامة. يُشرح استخدام ASOF JOIN أدناه.
PASTE JOINيُجري دمجًا أفقيًا لجدولين.
عند ضبط join_algorithm على partial_merge، لا يكون RIGHT JOIN وFULL JOIN مدعومَين إلا مع strictness من النوع ALL (SEMI وANTI وANY وASOF غير مدعومة).

الإعدادات

يمكن تجاوز نوع JOIN الافتراضي باستخدام الإعداد join_default_strictness. يعتمد سلوك ClickHouse server في عمليات ANY JOIN على الإعداد any_join_distinct_right_table_keys. انظر أيضًا استخدم الإعداد cross_to_inner_join_rewrite لتحديد السلوك عند فشل ClickHouse في إعادة كتابة CROSS JOIN إلى INNER JOIN. القيمة الافتراضية هي 1، ما يسمح بمتابعة عملية JOIN ولكنها ستكون أبطأ. اضبط cross_to_inner_join_rewrite على 0 إذا كنت تريد ظهور خطأ، واضبطه على 2 لعدم تشغيل عمليات cross joins، وبدلًا من ذلك فرض إعادة كتابة جميع عمليات comma/cross joins. وإذا فشلت إعادة الكتابة عندما تكون القيمة 2، فستتلقى رسالة خطأ تنص على: “Please, try to simplify WHERE section”.

شروط قسم ON

يمكن أن يحتوي قسم ON على عدة شروط مدمجة باستخدام المعاملين AND وOR. ويجب أن تستوفي الشروط التي تحدد مفاتيح الربط ما يلي:
  • أن تشير إلى كلٍّ من الجدول الأيسر والجدول الأيمن
  • أن تستخدم معامل المساواة
أما الشروط الأخرى، فيمكنها استخدام معاملات منطقية أخرى، لكن يجب أن تشير إما إلى الجدول الأيسر أو إلى الجدول الأيمن في الاستعلام. تُربط الصفوف إذا تحقق الشرط المركب بالكامل. وإذا لم تتحقق الشروط، فقد تظل الصفوف مُضمَّنة في النتيجة بحسب نوع JOIN. لاحظ أنه إذا وُضعت الشروط نفسها في قسم WHERE ولم تتحقق، فستُستبعد الصفوف دائمًا من النتيجة. يعمل المعامل OR داخل عبارة ON باستخدام خوارزمية الربط بالتجزئة — إذ يُنشأ جدول تجزئة منفصل لكل وسيطة OR تحتوي على مفاتيح ربط لـ JOIN، لذلك يزداد استهلاك الذاكرة ووقت تنفيذ الاستعلام خطيًا مع زيادة عدد تعبيرات OR في عبارة ON.
إذا كان الشرط يشير إلى أعمدة من جداول مختلفة، فلا يدعم النظام حتى الآن سوى معامل المساواة (=).
مثال لننظر إلى table_1 وtable_2:
┌─Id─┬─name─┐     ┌─Id─┬─text───────────┬─scores─┐
│  1 │ A    │     │  1 │ Text A         │     10 │
│  2 │ B    │     │  1 │ Another text A │     12 │
│  3 │ C    │     │  2 │ Text B         │     15 │
└────┴──────┘     └────┴────────────────┴────────┘
استعلام يتضمن شرطًا واحدًا لمفتاح الربط وشرطًا إضافيًا لـ table_2:
Query
SELECT name, text FROM table_1 LEFT OUTER JOIN table_2
    ON table_1.Id = table_2.Id AND startsWith(table_2.text, 'Text');
لاحظ أن النتيجة تتضمن الصف الذي يحمل الاسم C وعمود النص الفارغ. وقد أُدرجت في النتيجة لأن نوع OUTER من join مستخدم هنا.
Response
┌─name─┬─text───┐
│ A    │ Text A │
│ B    │ Text B │
│ C    │        │
└──────┴────────┘
استعلام باستخدام عملية ربط من النوع INNER وبشروط متعددة:
Query
SELECT name, text, scores FROM table_1 INNER JOIN table_2
    ON table_1.Id = table_2.Id AND table_2.scores > 10 AND startsWith(table_2.text, 'Text');
Response
┌─name─┬─text───┬─scores─┐
│ B    │ Text B │     15
└──────┴────────┴────────┘
استعلام مع join من النوع INNER وشرط يستخدم OR:
Query
CREATE TABLE t1 (`a` Int64, `b` Int64) ENGINE = MergeTree() ORDER BY a;

CREATE TABLE t2 (`key` Int32, `val` Int64) ENGINE = MergeTree() ORDER BY key;

INSERT INTO t1 SELECT number as a, -a as b from numbers(5);

INSERT INTO t2 SELECT if(number % 2 == 0, toInt64(number), -number) as key, number as val from numbers(5);

SELECT a, b, val FROM t1 INNER JOIN t2 ON t1.a = t2.key OR t1.b = t2.key;
Response
┌─a─┬──b─┬─val─┐
│ 0 │  0 │   0 │
│ 1 │ -1 │   1 │
│ 2 │ -2 │   2 │
│ 3 │ -3 │   3 │
│ 4 │ -4 │   4 │
└───┴────┴─────┘
استعلام بنوع الربط INNER وبشروط تتضمن OR وAND:
افتراضيًا، تكون شروط عدم المساواة مدعومة ما دامت تستخدم أعمدة من الجدول نفسه. على سبيل المثال، t1.a = t2.key AND t1.b > 0 AND t2.b > t2.c، لأن t1.b > 0 يستخدم أعمدة من t1 فقط، وt2.b > t2.c يستخدم أعمدة من t2 فقط. ومع ذلك، يمكنك تجربة الدعم التجريبي لشروط مثل t1.a = t2.key AND t1.b > t2.key؛ راجع القسم أدناه لمزيد من التفاصيل.
Query
SELECT a, b, val FROM t1 INNER JOIN t2 ON t1.a = t2.key OR t1.b = t2.key AND t2.val > 3;
Response
┌─a─┬──b─┬─val─┐
│ 0 │  0 │   0 │
│ 2 │ -2 │   2 │
│ 4 │ -4 │   4 │
└───┴────┴─────┘

JOIN مع شروط عدم المساواة للأعمدة من جداول مختلفة

يدعم ClickHouse حاليًا ALL/ANY/SEMI/ANTI INNER/LEFT/RIGHT/FULL JOIN مع شروط عدم المساواة بالإضافة إلى شروط المساواة. ولا تُدعَم شروط عدم المساواة إلا مع خوارزميّتَي JOIN ‏hash وgrace_hash. كما أن شروط عدم المساواة غير مدعومة مع join_use_nulls. مثال الجدول t1:
┌─key──┬─attr─┬─a─┬─b─┬─c─┐
│ key1 │ a    │ 1 │ 1 │ 2 │
│ key1 │ b    │ 2 │ 3 │ 2 │
│ key1 │ c    │ 3 │ 2 │ 1 │
│ key1 │ d    │ 4 │ 7 │ 2 │
│ key1 │ e    │ 5 │ 5 │ 5 │
│ key2 │ a2   │ 1 │ 1 │ 1 │
│ key4 │ f    │ 2 │ 3 │ 4 │
└──────┴──────┴───┴───┴───┘
الجدول t2
┌─key──┬─attr─┬─a─┬─b─┬─c─┐
│ key1 │ A    │ 1 │ 2 │ 1 │
│ key1 │ B    │ 2 │ 1 │ 2 │
│ key1 │ C    │ 3 │ 4 │ 5 │
│ key1 │ D    │ 4 │ 1 │ 6 │
│ key3 │ a3   │ 1 │ 1 │ 1 │
│ key4 │ F    │ 1 │ 1 │ 1 │
└──────┴──────┴───┴───┴───┘
SELECT t1.*, t2.* FROM t1 LEFT JOIN t2 ON t1.key = t2.key AND (t1.a < t2.a) ORDER BY (t1.key, t1.attr, t2.key, t2.attr);
key1    a    1    1    2    key1    B    2    1    2
key1    a    1    1    2    key1    C    3    4    5
key1    a    1    1    2    key1    D    4    1    6
key1    b    2    3    2    key1    C    3    4    5
key1    b    2    3    2    key1    D    4    1    6
key1    c    3    2    1    key1    D    4    1    6
key1    d    4    7    2            0    0    \N
key1    e    5    5    5            0    0    \N
key2    a2    1    1    1            0    0    \N
key4    f    2    3    4            0    0    \N

قيم NULL في مفاتيح JOIN

لا تساوي NULL أي قيمة، بما في ذلك نفسها. وهذا يعني أنه إذا كانت قيمة مفتاح JOIN هي NULL في أحد الجدولين، فلن تطابق قيمة NULL في الجدول الآخر. مثال الجدول A:
┌───id─┬─name────┐
│    1 │ Alice   │
│    2 │ Bob     │
│ ᴺᵁᴸᴸ │ Charlie │
└──────┴─────────┘
الجدول B:
┌───id─┬─score─┐
│    1 │    90 │
│    3 │    85 │
│ ᴺᵁᴸᴸ │    88 │
└──────┴───────┘
SELECT A.name, B.score FROM A LEFT JOIN B ON A.id = B.id
┌─name────┬─score─┐
│ Alice   │    90 │
│ Bob     │     0 │
│ Charlie │     0 │
└─────────┴───────┘
لاحظ أن الصف الذي يحتوي على Charlie من الجدول A، والصف الذي درجته 88 من الجدول B، لا يظهران في النتيجة بسبب القيمة NULL في مفتاح JOIN. إذا أردت مطابقة قيم NULL، فاستخدم الدالة isNotDistinctFrom لمقارنة مفاتيح JOIN.
SELECT A.name, B.score FROM A LEFT JOIN B ON isNotDistinctFrom(A.id, B.id)
┌─name────┬─score─┐
│ Alice   │    90 │
│ Bob     │     0 │
│ Charlie │    88 │
└─────────┴───────┘

استخدام ASOF JOIN

تُعد ASOF JOIN مفيدة عندما تحتاج إلى ربط سجلات لا يوجد بينها تطابق تام. تتطلب خوارزمية JOIN هذه عمودًا خاصًا في الجداول. ويجب أن يكون هذا العمود:
  • أن يحتوي على تسلسل مرتب.
  • من أحد الأنواع التالية: Int, UInt، Float، Date، DateTime، Decimal.
  • بالنسبة إلى خوارزمية hash join، لا يمكن أن يكون العمود الوحيد في عبارة JOIN.
البنية ASOF JOIN ... ON:
SELECT expressions_list
FROM table_1
ASOF LEFT JOIN table_2
ON equi_cond AND closest_match_cond
يمكنك استخدام أي عدد من شروط المساواة، وشرطًا واحدًا فقط لأقرب مطابقة. على سبيل المثال، SELECT count() FROM table_1 ASOF LEFT JOIN table_2 ON table_1.a == table_2.b AND table_2.t <= table_1.t. الشروط المدعومة لأقرب مطابقة: >, >=, <, <=. الصياغة ASOF JOIN ... USING:
SELECT expressions_list
FROM table_1
ASOF JOIN table_2
USING (equi_column1, ... equi_columnN, asof_column)
يستخدم ASOF JOINequi_columnX للربط وفقًا للمساواة، وasof_column للربط بأقرب قيمة مطابقة مع الشرط table_1.asof_column >= table_2.asof_column. ويكون العمود asof_column دائمًا هو الأخير في بند USING. على سبيل المثال، تأمل الجداول التالية:
         table_1                           table_2
      event   | ev_time | user_id       event   | ev_time | user_id
    ----------|---------|----------   ----------|---------|----------
                  ...                               ...
    event_1_1 |  12:00  |  42         event_2_1 |  11:59  |   42
                  ...                 event_2_2 |  12:30  |   42
    event_1_2 |  13:00  |  42         event_2_3 |  13:00  |   42
                  ...                               ...
يمكن لـ ASOF JOIN أخذ الطابع الزمني لحدث مستخدم من table_1 والعثور على حدث في table_2 يكون طابعه الزمني الأقرب إلى الطابع الزمني لحدث table_1 وفقًا لشرط أقرب تطابق. وتُعد قيم الطابع الزمني المتساوية هي الأقرب إذا كانت متاحة. هنا، يمكن استخدام العمود user_id للربط على أساس المساواة، ويمكن استخدام العمود ev_time للربط على أساس أقرب تطابق. في مثالنا، يمكن ربط event_1_1 مع event_2_1، ويمكن ربط event_1_2 مع event_2_3، لكن لا يمكن ربط event_2_2.
لا يدعم ASOF JOIN إلا خوارزميتي JOIN ‏hash وfull_sorting_merge. وهو غير مدعوم في محرك الجدول Join.

استخدام PASTE JOIN

نتيجة PASTE JOIN هي جدول يحتوي على جميع الأعمدة من الاستعلام الفرعي الأيسر، ثم جميع الأعمدة من الاستعلام الفرعي الأيمن. تُطابَق الصفوف بناءً على مواضعها في الجداول الأصلية (يجب أن يكون ترتيب الصفوف محددًا). إذا أعادت الاستعلامات الفرعية أعدادًا مختلفة من الصفوف، فستُقتطع الصفوف الزائدة. مثال:
SELECT *
FROM
(
    SELECT number AS a
    FROM numbers(2)
) AS t1
PASTE JOIN
(
    SELECT number AS a
    FROM numbers(2)
    ORDER BY a DESC
) AS t2

┌─a─┬─t2.a─┐
01
10
└───┴──────┘
ملاحظة: في هذه الحالة، قد تكون النتيجة غير حتمية إذا جرت القراءة بالتوازي. على سبيل المثال:
SELECT *
FROM
(
    SELECT number AS a
    FROM numbers_mt(5)
) AS t1
PASTE JOIN
(
    SELECT number AS a
    FROM numbers(10)
    ORDER BY a DESC
) AS t2
SETTINGS max_block_size = 2;

┌─a─┬─t2.a─┐
29
38
└───┴──────┘
┌─a─┬─t2.a─┐
07
16
└───┴──────┘
┌─a─┬─t2.a─┐
45
└───┴──────┘

JOIN الموزّع

توجد طريقتان لتنفيذ JOIN يتضمن جداول موزعة:
  • عند استخدام JOIN عادي، يُرسَل الاستعلام إلى الخوادم البعيدة. وتُشغَّل الاستعلامات الفرعية على كل خادم منها لتكوين الجدول الأيمن، ثم تُنفَّذ عملية الربط باستخدام هذا الجدول. وبعبارة أخرى، يُنشأ الجدول الأيمن على كل خادم على حدة.
  • عند استخدام GLOBAL ... JOIN، يشغّل الخادم الذي أرسل الطلب أولًا استعلامًا فرعيًا لحساب أحد طرفَي الربط ويجمع النتيجة في جدول مؤقت. ثم يُمرَّر هذا الجدول المؤقت إلى كل خادم بعيد، وتُشغَّل الاستعلامات عليها باستخدام البيانات المؤقتة المنقولة. في عمليتَي الربط LEFT وINNER، يُحسَب الجدول الأيمن باعتباره الاستعلام الفرعي. أما في عملية الربط RIGHT، فيُحسَب الجدول الأيسر بدلًا من ذلك، لأن الجدول الأيمن هو الجدول الذي يجري الاحتفاظ به ويجب قراءته من الشظايا.
توخَّ الحذر عند استخدام GLOBAL. لمزيد من المعلومات، راجع قسم الاستعلامات الفرعية الموزعة.

تحويل النوع الضمني

تدعم استعلامات INNER JOIN وLEFT JOIN وRIGHT JOIN وFULL JOIN تحويل النوع الضمني لـ “join keys”. ومع ذلك، لا يمكن تنفيذ الاستعلام إذا تعذر تحويل مفاتيح الربط في الجدولين الأيسر والأيمن إلى نوع واحد (على سبيل المثال، لا يوجد نوع بيانات يمكنه استيعاب جميع القيم من كلٍّ من UInt64 وInt64، أو String وInt32). مثال لنأخذ الجدول t_1:
┌─a─┬─b─┬─toTypeName(a)─┬─toTypeName(b)─┐
│ 1 │ 1 │ UInt16        │ UInt8         │
│ 2 │ 2 │ UInt16        │ UInt8         │
└───┴───┴───────────────┴───────────────┘
والجدول t_2:
┌──a─┬────b─┬─toTypeName(a)─┬─toTypeName(b)───┐
│ -1 │    1 │ Int16         │ Nullable(Int64) │
│  1 │   -1 │ Int16         │ Nullable(Int64) │
│  1 │    1 │ Int16         │ Nullable(Int64) │
└────┴──────┴───────────────┴─────────────────┘
الاستعلام
SELECT a, b, toTypeName(a), toTypeName(b) FROM t_1 FULL JOIN t_2 USING (a, b);
يُعيد المجموعة:
┌──a─┬────b─┬─toTypeName(a)─┬─toTypeName(b)───┐
│  1 │    1 │ Int32         │ Nullable(Int64) │
│  2 │    2 │ Int32         │ Nullable(Int64) │
│ -1 │    1 │ Int32         │ Nullable(Int64) │
│  1 │   -1 │ Int32         │ Nullable(Int64) │
└────┴──────┴───────────────┴─────────────────┘

توصيات الاستخدام

معالجة الخلايا الفارغة أو NULL

أثناء ربط الجداول، قد تظهر خلايا فارغة. يحدّد الإعداد join_use_nulls كيفية ملء ClickHouse لهذه الخلايا. إذا كانت مفاتيح JOIN حقولًا من النوع Nullable، فلن تُربط الصفوف التي تكون قيمة واحد على الأقل من هذه المفاتيح فيها NULL.

البنية

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

قيود الصياغة

بالنسبة إلى عبارات JOIN المتعددة ضمن استعلام SELECT واحد:
  • لا يتاح استخدام جميع الأعمدة عبر * إلا إذا كان الربط بين جداول، لا استعلامات فرعية.
  • عبارة PREWHERE غير متاحة.
  • عبارة USING غير متاحة.
بالنسبة إلى عبارات ON وWHERE وGROUP BY:
  • لا يمكن استخدام تعبيرات عشوائية في عبارات ON وWHERE وGROUP BY، ولكن يمكنك تعريف تعبير في عبارة SELECT ثم استخدامه في هذه العبارات عبر اسم مستعار.

الأداء

عند تنفيذ JOIN، لا يوجد تحسين لترتيب التنفيذ بالنسبة إلى المراحل الأخرى من الاستعلام. ويُنفَّذ الـ join (أي البحث في الجدول الأيمن) قبل التصفية في WHERE وقبل التجميع. في كل مرة يُنفَّذ فيها استعلام يستخدم JOIN نفسه، يُعاد تنفيذ الاستعلام الفرعي لأن النتيجة غير مخزنة مؤقتًا. ولتجنّب ذلك، استخدم محرك الجدول الخاص Join، وهو مصفوفة مُعدّة مسبقًا لعمليات الربط وتبقى دائمًا في ذاكرة RAM. في بعض الحالات، يكون استخدام IN أكثر كفاءة من JOIN. إذا كنت بحاجة إلى JOIN للربط مع جداول الأبعاد (وهي جداول صغيرة نسبيًا تحتوي على خصائص الأبعاد، مثل أسماء الحملات الإعلانية)، فقد لا يكون JOIN مناسبًا جدًا لأن الجدول الأيمن يُعاد الوصول إليه مع كل استعلام. في مثل هذه الحالات، توجد ميزة “Dictionaries” ينبغي استخدامها بدلًا من JOIN. لمزيد من المعلومات، راجع قسم Dictionaries.

قيود الذاكرة

افتراضيًا، يستخدم ClickHouse خوارزمية hash join. يأخذ ClickHouse الجدول الأيمن right_table وينشئ له جدول تجزئة في RAM. إذا كان join_algorithm = 'auto' مفعّلًا، فبعد تجاوز عتبة معيّنة من استهلاك الذاكرة، يعود ClickHouse إلى خوارزمية merge join. للاطلاع على وصف خوارزميات JOIN، راجع الإعداد join_algorithm. إذا كنت بحاجة إلى تقييد استهلاك الذاكرة لعملية JOIN، فاستخدم الإعدادات التالية: عند بلوغ أيٍّ من هذه الحدود، يتصرّف ClickHouse وفقًا لما يحدّده الإعداد join_overflow_mode.

أمثلة

مثال:
SELECT
    CounterID,
    hits,
    visits
FROM
(
    SELECT
        CounterID,
        count() AS hits
    FROM test.hits
    GROUP BY CounterID
) ANY LEFT JOIN
(
    SELECT
        CounterID,
        sum(Sign) AS visits
    FROM test.visits
    GROUP BY CounterID
) USING CounterID
ORDER BY hits DESC
LIMIT 10
┌─CounterID─┬───hits─┬─visits─┐
│   1143050 │ 523264 │  13665 │
│    731962 │ 475698 │ 102716 │
│    722545 │ 337212 │ 108187 │
│    722889 │ 252197 │  10547 │
│   2237260 │ 196036 │   9522 │
│  23057320 │ 147211 │   7689 │
│    722818 │  90109 │  17847 │
│     48221 │  85379 │   4652 │
│  19762435 │  77807 │   7026 │
│    722884 │  77492 │  11056 │
└───────────┴────────┴────────┘
آخر تعديل في ٢٥ يونيو ٢٠٢٦