> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-8c05c8a2.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> توثيق نوع البيانات Map في ClickHouse

# Map(K, V)

يخزّن نوع البيانات `Map(K, V)` أزواج المفتاح-القيمة.

بخلاف قواعد البيانات الأخرى، لا تكون المفاتيح في الخرائط فريدة في ClickHouse، أي يمكن أن تحتوي الخريطة على عنصرين بالمفتاح نفسه.
(ويعود ذلك إلى أن الخرائط تُنفَّذ داخليًا على هيئة `Array(Tuple(K, V))`.)

يمكنك استخدام الصياغة `m[k]` للحصول على قيمة المفتاح `k` في الخريطة `m`.
كذلك، تقوم `m[k]` بمسح الخريطة، أي إن زمن تنفيذ العملية يزداد خطيًا مع حجم الخريطة.

**المعلمات**

* `K` — نوع مفاتيح Map. يمكن أن يكون أي نوع باستثناء [Nullable](/ar/reference/data-types/nullable) و[LowCardinality](/ar/reference/data-types/lowcardinality) المتداخل مع أنواع [Nullable](/ar/reference/data-types/nullable).
* `V` — نوع قيم Map. يمكن أن يكون أي نوع.

**أمثلة**

أنشئ جدولًا يحتوي على عمود من النوع map:

```sql title="Query" theme={null}
CREATE TABLE tab (m Map(String, UInt64)) ENGINE=Memory;
INSERT INTO tab VALUES ({'key1':1, 'key2':10}), ({'key1':2,'key2':20}), ({'key1':3,'key2':30});
```

لاختيار قيم `key2`:

```sql title="Query" theme={null}
SELECT m['key2'] FROM tab;
```

```text title="Response" theme={null}
┌─arrayElement(m, 'key2')─┐
│                      10 │
│                      20 │
│                      30 │
└─────────────────────────┘
```

إذا لم يكن المفتاح المطلوب `k` موجودًا في الـ map، فإن `m[k]` تُرجع القيمة الافتراضية لنوع القيمة، مثل `0` لأنواع الأعداد الصحيحة و `''` لأنواع السلاسل النصية.
للتحقق مما إذا كان مفتاح ما موجودًا في map، يمكنك استخدام الدالة [mapContains](/ar/reference/functions/regular-functions/tuple-map-functions#mapContainsKey).

```sql title="Query" theme={null}
CREATE TABLE tab (m Map(String, UInt64)) ENGINE=Memory;
INSERT INTO tab VALUES ({'key1':100}), ({});
SELECT m['key1'] FROM tab;
```

```text title="Response" theme={null}
┌─arrayElement(m, 'key1')─┐
│                     100 │
│                       0 │
└─────────────────────────┘
```

<div id="converting-tuple-to-map">
  ## تحويل Tuple إلى Map
</div>

يمكن تحويل القيم من النوع `Tuple()` إلى قيم من النوع `Map()` باستخدام الدالة [CAST](/ar/reference/functions/regular-functions/type-conversion-functions#CAST):

**مثال**

```sql title="Query" theme={null}
SELECT CAST(([1, 2, 3], ['Ready', 'Steady', 'Go']), 'Map(UInt8, String)') AS map;
```

```text title="Response" theme={null}
┌─map───────────────────────────┐
│ {1:'Ready',2:'Steady',3:'Go'} │
└───────────────────────────────┘
```

<div id="reading-subcolumns-of-map">
  ## قراءة الأعمدة الفرعية في Map
</div>

لتجنب قراءة الـMap بالكامل، يمكنك في بعض الحالات استخدام العمودين الفرعيين `keys` و`values`.

**مثال**

```sql title="Query" theme={null}
CREATE TABLE tab (m Map(String, UInt64)) ENGINE = Memory;
INSERT INTO tab VALUES (map('key1', 1, 'key2', 2, 'key3', 3));

SELECT m.keys FROM tab; --   same as mapKeys(m)
SELECT m.values FROM tab; -- same as mapValues(m)
```

```text title="Response" theme={null}
┌─m.keys─────────────────┐
│ ['key1','key2','key3'] │
└────────────────────────┘

┌─m.values─┐
│ [1,2,3]  │
└──────────┘
```

<div id="bucketed-map-serialization">
  ## التسلسل المُقسَّم إلى buckets لـ Map في MergeTree
</div>

بشكل افتراضي، يُخزَّن عمود `Map` في MergeTree كتدفّق واحد من `Array(Tuple(K, V))`.
تتطلّب قراءة مفتاح واحد باستخدام `m['key']` فحص العمود بأكمله — أي كل زوج مفتاح-قيمة في كل صف — حتى إذا كان المطلوب مفتاحًا واحدًا فقط.
وبالنسبة إلى maps التي تحتوي على عدد كبير من المفاتيح المميّزة، يصبح ذلك عنق زجاجة.

يُقسِّم التسلسل المُقسَّم إلى buckets (`with_buckets`) أزواج المفتاح-القيمة إلى عدة تدفّقات فرعية مستقلة (buckets) عبر تطبيق hash على المفتاح.
وعندما يصل الاستعلام إلى `m['key']`، لا يُقرأ من القرص إلا الـ bucket الذي يحتوي على ذلك المفتاح، مع تخطّي جميع الـ buckets الأخرى.

<div id="enabling-bucketed-serialization">
  ### تمكين التسلسل بالتقسيم إلى دلاء
</div>

```sql theme={null}
CREATE TABLE tab (id UInt64, m Map(String, UInt64))
ENGINE = MergeTree ORDER BY id
SETTINGS
    map_serialization_version = 'with_buckets',
    max_buckets_in_map = 32,
    map_buckets_strategy = 'sqrt';
```

لتجنّب إبطاء عمليات الإدخال، يمكنك الإبقاء على serialization من النوع `basic` للأجزاء من المستوى الصفري (التي أُنشئت أثناء `INSERT`) واستخدام `with_buckets` فقط للأجزاء الناتجة عن الدمج:

```sql theme={null}
CREATE TABLE tab (id UInt64, m Map(String, UInt64))
ENGINE = MergeTree ORDER BY id
SETTINGS
    map_serialization_version = 'with_buckets',
    map_serialization_version_for_zero_level_parts = 'basic',
    max_buckets_in_map = 32,
    map_buckets_strategy = 'sqrt';
```

<div id="how-it-works">
  ### كيف يعمل
</div>

عند كتابة جزء بيانات باستخدام تسلسل `with_buckets`:

1. يُحسَب متوسط عدد المفاتيح لكل صف من إحصاءات block.
2. يُحدَّد عدد buckets وفقًا للاستراتيجية المُعدّة (راجع [الإعدادات](#bucketed-map-settings)).
3. يُسنَد كل زوج مفتاح-قيمة إلى bucket عن طريق تطبيق hash على المفتاح: `bucket = hash(key) % num_buckets`.
4. يُخزَّن كل bucket باعتباره substream مستقلًا له مفاتيحه وقيمه وoffsets الخاصة به.
5. يسجّل stream البيانات الوصفية `buckets_info` عدد buckets والإحصاءات.

عندما يقرأ query مفتاحًا محددًا (`m['key']`)، يعيد المُحسِّن كتابة expression إلى subcolumn للمفتاح (`m.key_<serialized_key>`).
ثم تحسب طبقة التسلسل bucket الذي ينتمي إليه المفتاح المطلوب، ولا تقرأ من disk سوى ذلك bucket الواحد.

عند قراءة map بالكامل (مثل `SELECT m`)، تُقرأ جميع buckets ويُعاد تجميعها لتكوين map الأصلي. ويكون ذلك أبطأ من تسلسل `basic` بسبب overhead الناتج عن قراءة عدة substreams ودمجها.

<Note>
  قد يختلف ترتيب المفاتيح داخل قيمة map عن ترتيب insertion الأصلي عند استخدام تسلسل `with_buckets`. تُوزَّع المفاتيح على buckets باستخدام hash، ثم يُعاد تجميعها بحسب ترتيب buckets لا ترتيب insertion. أما مع تسلسل `basic`، فيُحفَظ ترتيب المفاتيح في maps المُدرجة.
</Note>

يمكن أن يختلف عدد buckets بين parts. وعند دمج parts ذات أعداد buckets مختلفة، يُعاد حساب عدد buckets في part الجديد استنادًا إلى الإحصاءات المدمجة. ويمكن أن يتعايش تسلسلا `basic` و`with_buckets` في table نفسها، وتُدمَج هذه الأجزاء بشفافية.

<div id="bucketed-map-settings">
  ### الإعدادات
</div>

| الإعداد                                          | الافتراضي | الوصف                                                                                                                                                                                                                                                        |
| ------------------------------------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `map_serialization_version`                      | `basic`   | تنسيق التسلسل لأعمدة `Map`. يخزّن `basic` البيانات كتدفق `Array` واحد. ويقسّم `with_buckets` المفاتيح إلى حاويات لتسريع قراءات المفتاح الواحد.                                                                                                               |
| `map_serialization_version_for_zero_level_parts` | `basic`   | تنسيق التسلسل للأجزاء ذات المستوى الصفري (التي يتم إنشاؤها بواسطة `INSERT`). يتيح الإبقاء على `basic` لعمليات الإدراج لتجنّب عبء الكتابة الإضافي، بينما تستخدم الأجزاء المدمجة `with_buckets`.                                                               |
| `max_buckets_in_map`                             | `32`      | الحد الأعلى لعدد الحاويات. يعتمد العدد الفعلي على `map_buckets_strategy`. والحد الأقصى المسموح به هو 256.                                                                                                                                                    |
| `map_buckets_strategy`                           | `sqrt`    | استراتيجية حساب عدد الحاويات من متوسط حجم الخريطة: `constant` — استخدم دائمًا `max_buckets_in_map`؛ `sqrt` — استخدم `round(coefficient * sqrt(avg_size))`؛ `linear` — استخدم `round(coefficient * avg_size)`. تُقيَّد النتيجة ضمن `[1, max_buckets_in_map]`. |
| `map_buckets_coefficient`                        | `1.0`     | المعامل المضاعِف لاستراتيجيتي `sqrt` و`linear`. ويُتجاهل عندما تكون الاستراتيجية `constant`.                                                                                                                                                                 |
| `map_buckets_min_avg_size`                       | `32`      | الحد الأدنى لمتوسط عدد المفاتيح لكل صف لتمكين التقسيم إلى حاويات. إذا كان المتوسط أقل من هذه العتبة، فستُستخدم حاوية واحدة بغض النظر عن الإعدادات الأخرى. اضبطه على `0` لتعطيل هذه العتبة.                                                                   |

<div id="performance-trade-offs">
  ### مقايضات الأداء
</div>

يلخّص الجدول التالي أثر `with_buckets` على الأداء مقارنةً بتنسيق التسلسل `basic` عند أحجام مختلفة لـ Map (من 10 إلى 10,000 مفتاح لكل صف). وقد حُدِّد عدد الحاويات باستخدام استراتيجية `sqrt` بحد أقصى 32. وتعتمد الأرقام الدقيقة على أنواع المفاتيح/القيم، وتوزيع البيانات، والأجهزة.

| العملية                                        | 10 مفاتيح            | 100 مفتاح            | 1,000 مفتاح          | 10,000 مفتاح         | ملاحظات                                                                                                                                                                                                                          |
| ---------------------------------------------- | -------------------- | -------------------- | -------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **بحث عن مفتاح واحد** (`m['key']`)             | أسرع بمقدار 1.6–3.2x | أسرع بمقدار 4.5–7.7x | أسرع بمقدار 16–39x   | أسرع بمقدار 21–49x   | يقرأ حاوية واحدة فقط بدلًا من العمود بالكامل.                                                                                                                                                                                    |
| **5 عمليات بحث عن مفاتيح**                     | \~1x                 | أسرع بمقدار 1.5–3.1x | أسرع بمقدار 2.9–8.3x | أسرع بمقدار 4.5–6.7x | يقرأ كل مفتاح حاويته الخاصة؛ وقد تتداخل بعض الحاويات.                                                                                                                                                                            |
| **PREWHERE** (`SELECT m WHERE m['key'] = ...`) | أسرع بمقدار 1.5–3.0x | أسرع بمقدار 2.9–7.3x | أسرع بمقدار 5.3–31x  | أسرع بمقدار 20–45x   | لا يقرأ عامل التصفية PREWHERE سوى حاوية واحدة؛ ولا تُقرأ الـ Map كاملة إلا للصفوف المطابقة. تعتمد الزيادة في السرعة على الانتقائية — فكلما قلّ عدد الحبيبات المطابقة، انخفضت عمليات الإدخال/الإخراج الخاصة بقراءة الـ Map كاملة. |
| **فحص الـ Map بالكامل** (`SELECT m`)           | أبطأ بحوالي 2x       | أبطأ بحوالي 2x       | أبطأ بحوالي 2x       | أبطأ بحوالي 2x       | يجب قراءة جميع الحاويات وإعادة تجميعها.                                                                                                                                                                                          |
| **INSERT**                                     | أبطأ بمقدار 1.5–2.5x | أبطأ بمقدار 1.5–2.5x | أبطأ بمقدار 1.5–2.5x | أبطأ بمقدار 1.5–2.5x | كلفة إضافية ناتجة عن تجزئة المفاتيح والكتابة إلى عدة تدفقات فرعية.                                                                                                                                                               |

<div id="recommendations">
  ### التوصيات
</div>

* **الخرائط الصغيرة (أقل من 32 مفتاحًا في المتوسط):** أبقِ على التسلسل `basic`. لا يبرَّر العبء الإضافي للتقسيم إلى buckets مع الخرائط الصغيرة. وتفرض القيمة الافتراضية `map_buckets_min_avg_size = 32` ذلك تلقائيًا.
* **الخرائط المتوسطة (32–100 مفتاح):** استخدم `with_buckets` مع استراتيجية `sqrt` إذا كانت الاستعلامات تصل كثيرًا إلى مفاتيح فردية. تتراوح زيادة السرعة بين 4x و8x لعمليات `lookup` على مفتاح واحد.
* **الخرائط الكبيرة (100+ مفتاح):** استخدم `with_buckets`. تكون عمليات `lookup` على مفتاح واحد أسرع بمقدار 16x إلى 49x. فكّر في استخدام `map_serialization_version_for_zero_level_parts = 'basic'` للحفاظ على سرعة `insert` قريبة من الخط الأساسي.
* **إذا كانت عمليات الفحص الكامل للخريطة هي المهيمنة على عبء العمل:** أبقِ على `basic`. يضيف bucketed serialization عبئًا إضافيًا يقارب 2x عند الفحص الكامل.
* **عبء عمل مختلط (بعض عمليات `lookup` للمفاتيح، وبعض عمليات الفحص الكامل):** استخدم `with_buckets` مع ضبط zero-level parts على `basic`. يقرأ تحسين `PREWHERE` الـ bucket ذي الصلة فقط من أجل `filter`، ثم يقرأ الخريطة كاملةً فقط للصفوف المطابقة، مما يحقق زيادة صافية كبيرة في السرعة.

<div id="map-alternatives">
  ### أساليب بديلة
</div>

إذا لم يكن تسلسل `Map` المُقسَّم إلى مجموعات مناسبًا لسيناريو استخدامك، فهناك أسلوبان بديلان لتحسين أداء الوصول على مستوى المفاتيح:

<div id="using-the-json-data-type">
  #### استخدام نوع بيانات JSON
</div>

يخزّن [نوع بيانات JSON](/ar/reference/data-types/newjson) كل مسار متكرر كعمود فرعي ديناميكي منفصل. أما المسارات التي تتجاوز الحد `max_dynamic_paths` فتُنقل إلى [بنية بيانات مشتركة](/ar/reference/data-types/newjson#shared-data-structure)، والتي يمكنها استخدام التسلسل `advanced` لتحسين قراءة المسار الواحد. راجع [منشور المدونة](https://clickhouse.com/blog/json-data-type-gets-even-better) للحصول على نظرة عامة مفصلة على التسلسل `advanced`.

| الجانب              | `Map` with buckets                                                                                         | `JSON`                                                                                                                                                                                |
| ------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| قراءة مفتاح واحد    | يقرأ bucket واحدًا (وقد يحتوي على مفاتيح أخرى). ويجري إلغاء تسلسل جميع أزواج المفتاح-القيمة في هذا bucket. | تُقرأ المسارات المتكررة مباشرةً من الأعمدة الفرعية الديناميكية. أما المسارات غير المتكررة فتذهب إلى البيانات المشتركة؛ ومع التسلسل `advanced` لا تُقرأ إلا بيانات المسار المطلوب فقط. |
| أنواع القيم         | تشترك جميع القيم في النوع نفسه `V`                                                                         | يمكن أن يكون لكل مسار نوعه الخاص. والمسارات التي لا تحتوي على تلميح نوع تستخدم `Dynamic`.                                                                                             |
| دعم skip index      | يعمل مع بعض أنواع الفهارس المُنشأة على `mapKeys`/`mapValues`                                               | لا يمكن إنشاء skip indexes إلا على الأعمدة الفرعية الخاصة بمسارات محددة، وليس على جميع المسارات/القيم دفعة واحدة.                                                                     |
| قراءة العمود الكامل | أبطأ بحوالي \~2x من `basic` بسبب إعادة تجميع buckets                                                       | يوجد عبء إضافي ناتج عن ترميز النوع `Dynamic` وإعادة بناء المسارات.                                                                                                                    |
| عبء التخزين         | حد أدنى من البيانات الوصفية الإضافية                                                                       | أعلى بسبب ترميز النوع `Dynamic`، وتخزين أسماء المسارات، وبيانات وصفية إضافية في التسلسل `advanced`.                                                                                   |
| مرونة المخطط        | تكون أنواع المفاتيح والقيم ثابتة عند إنشاء الجدول                                                          | ديناميكي بالكامل — يمكن أن تختلف المفاتيح وأنواع القيم من صف إلى آخر. ويمكن تعريف تلميحات أنواع لمسارات معروفة للوصول المباشر إلى العمود الفرعي.                                      |

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

<div id="manual-sharding-into-multiple-map-columns">
  #### التقسيم اليدوي إلى عدة أعمدة Map
</div>

يمكنك تقسيم `Map` واحدة يدويًا إلى عدة أعمدة بناءً على تجزئة المفتاح على مستوى التطبيق:

```sql theme={null}
CREATE TABLE tab (
    id UInt64,
    m0 Map(String, UInt64),
    m1 Map(String, UInt64),
    m2 Map(String, UInt64),
    m3 Map(String, UInt64)
) ENGINE = MergeTree ORDER BY id;
```

أثناء الإدراج، وجّه كل زوج مفتاح-قيمة إلى العمود `m{hash(key) % 4}`. وأثناء الاستعلامات، اقرأ من العمود المحدد: `m{hash('target_key') % 4}['target_key']`.

| الجانب          | `Map` مع buckets                                | التجزئة اليدوية                                                   |
| --------------- | ----------------------------------------------- | ----------------------------------------------------------------- |
| سهولة الاستخدام | شفافة — يتولاها محرك التخزين                    | تتطلب منطق توجيه على مستوى التطبيق لعمليات insert وselect         |
| الدمج العمودي   | غير مدعوم — جميع الـbuckets تنتمي إلى عمود واحد | مدعوم — كل عمود `Map` مستقل ويمكن دمجه عموديًا                    |
| تغييرات المخطط  | يتكيّف عدد الـbuckets تلقائيًا لكل جزء          | يتطلب تغيير عدد الأجزاء إعادة كتابة البيانات أو إضافة أعمدة جديدة |
| صياغة الاستعلام | `m['key']` تعمل مباشرة                          | يجب حساب العمود الصحيح: `m0['key']`, `m1['key']`، إلخ.            |
| مستوى التقسيم   | لكل جزء، ويتكيّف مع إحصاءات البيانات            | ثابت عند إنشاء table                                              |

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

**راجع أيضًا**

* الدالة [map()](/ar/reference/functions/regular-functions/tuple-map-functions#map)
* الدالة [CAST()](/ar/reference/functions/regular-functions/type-conversion-functions#CAST)
* [المُركِّب ‎-Map‎ لنوع البيانات Map](/ar/reference/functions/aggregate-functions/combinators#-map)

<div id="related-content">
  ## محتوى ذو صلة
</div>

* المدونة: [إنشاء حل Observability باستخدام ClickHouse - الجزء 2 - التتبعات](https://clickhouse.com/blog/storing-traces-and-spans-open-telemetry-in-clickhouse)
