> ## 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.

> توثيق لبند GROUP BY

# بند GROUP BY

يحوّل بند `GROUP BY` استعلام `SELECT` إلى وضع التجميع، ويعمل ذلك على النحو التالي:

* يحتوي بند `GROUP BY` على قائمة من التعبيرات (أو تعبيرًا واحدًا يُعدّ قائمةً بطول عنصر واحد). وتعمل هذه القائمة بوصفها "مفتاح التجميع"، بينما يُشار إلى كل تعبير فيها على حدة باسم "تعبير مفتاح".
* **يجب** أن تُحتسب جميع التعبيرات في البنود [SELECT](/ar/reference/statements/select/index) و[HAVING](/ar/reference/statements/select/having) و[ORDER BY](/ar/reference/statements/select/order-by) استنادًا إلى تعبيرات المفاتيح **أو** إلى [الدوال التجميعية](/ar/reference/functions/aggregate-functions/index) المطبّقة على تعبيرات ليست مفاتيح (بما في ذلك الأعمدة العادية). وبعبارة أخرى، يجب استخدام كل عمود مُختار من الجدول إما في تعبير مفتاح أو داخل دالة تجميعية، ولكن ليس في كليهما.
* ستحتوي نتيجة تجميع استعلام `SELECT` على عدد من الصفوف يساوي عدد القيم الفريدة لـ"مفتاح التجميع" في الجدول المصدر. وعادةً ما يقلّل هذا عدد الصفوف بشكل كبير، وغالبًا بعدة مراتب، لكن ليس بالضرورة: إذ يظل عدد الصفوف كما هو إذا كانت جميع قيم "مفتاح التجميع" متميزة.

إذا أردت تجميع البيانات في الجدول حسب أرقام الأعمدة بدلًا من أسماء الأعمدة، ففعّل الإعداد [enable\_positional\_arguments](/ar/reference/settings/session-settings#enable_positional_arguments).

<Note>
  توجد طريقة إضافية لإجراء التجميع على جدول. إذا كان الاستعلام يحتوي على أعمدة الجدول داخل الدوال التجميعية فقط، فيمكن حذف `بند GROUP BY`، وعندئذٍ يُفترض التجميع على مجموعة مفاتيح فارغة. وتُرجع مثل هذه الاستعلامات دائمًا صفًا واحدًا فقط.
</Note>

<div id="null-processing">
  ## معالجة NULL
</div>

في عمليات التجميع، يتعامل ClickHouse مع [NULL](/ar/reference/syntax#null) على أنها قيمة، ويُعدّ `NULL==NULL`. وهذا يختلف عن معالجة `NULL` في معظم السياقات الأخرى.

إليك مثالًا يوضح المقصود بذلك.

افترض أن لديك هذا الجدول:

```text theme={null}
┌─x─┬────y─┐
│ 1 │    2 │
│ 2 │ ᴺᵁᴸᴸ │
│ 3 │    2 │
│ 3 │    3 │
│ 3 │ ᴺᵁᴸᴸ │
└───┴──────┘
```

ينتج الاستعلام `SELECT sum(x), y FROM t_null_big GROUP BY y` ما يلي:

```text theme={null}
┌─sum(x)─┬────y─┐
│      4 │    2 │
│      3 │    3 │
│      5 │ ᴺᵁᴸᴸ │
└────────┴──────┘
```

يمكنك ملاحظة أن `GROUP BY` عند `y = NULL` جمع قيم `x` كما لو كانت `NULL` قيمةً فعلية.

إذا مرّرت عدة مفاتيح إلى `GROUP BY`، فستعطيك النتيجة جميع تركيبات التحديد، كما لو كانت `NULL` قيمةً محددة.

<div id="rollup-modifier">
  ## مُعدِّل `ROLLUP`
</div>

يُستخدم المُعدِّل `ROLLUP` لحساب المجاميع الفرعية لتعبيرات المفاتيح، استنادًا إلى ترتيبها في قائمة `GROUP BY`. وتُضاف صفوف المجاميع الفرعية بعد جدول النتائج.

تُحسَب المجاميع الفرعية بترتيب عكسي: في البداية تُحسَب المجاميع الفرعية لآخر تعبير مفتاح في القائمة، ثم للتعبير الذي يسبقه، وهكذا حتى أول تعبير مفتاح.

في صفوف المجاميع الفرعية، تُضبط قيم تعبيرات المفاتيح التي سبق "تجميعها" على `0` أو سلسلة فارغة.

<Note>
  انتبه إلى أن عبارة [HAVING](/ar/reference/statements/select/having) قد تؤثر في نتائج المجاميع الفرعية.
</Note>

**مثال**

لننظر إلى الجدول t:

```text theme={null}
┌─year─┬─month─┬─day─┐
│ 2019 │     1 │   5 │
│ 2019 │     1 │  15 │
│ 2020 │     1 │   5 │
│ 2020 │     1 │  15 │
│ 2020 │    10 │   5 │
│ 2020 │    10 │  15 │
└──────┴───────┴─────┘
```

```sql title="Query" theme={null}
SELECT year, month, day, count(*) FROM t GROUP BY ROLLUP(year, month, day);
```

نظرًا إلى أن قسم `GROUP BY` يحتوي على ثلاثة من تعبيرات المفاتيح، فإن النتيجة تتضمن أربعة جداول مع مجاميع فرعية "مُجمَّعة تصاعديًا" من اليمين إلى اليسار:

* `GROUP BY year, month, day`;
* `GROUP BY year, month` (ويُملأ العمود `day` بالأصفار);
* `GROUP BY year` (ويُملأ الآن كلٌّ من العمودين `month` و`day` بالأصفار);
* والإجماليات (وتكون أعمدة تعبيرات المفاتيح الثلاثة جميعها أصفارًا).

```text title="Response" theme={null}
┌─year─┬─month─┬─day─┬─count()─┐
│ 2020 │    10 │  15 │       1 │
│ 2020 │     1 │   5 │       1 │
│ 2019 │     1 │   5 │       1 │
│ 2020 │     1 │  15 │       1 │
│ 2019 │     1 │  15 │       1 │
│ 2020 │    10 │   5 │       1 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│ 2019 │     1 │   0 │       2 │
│ 2020 │     1 │   0 │       2 │
│ 2020 │    10 │   0 │       2 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│ 2019 │     0 │   0 │       2 │
│ 2020 │     0 │   0 │       4 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│    0 │     0 │   0 │       6 │
└──────┴───────┴─────┴─────────┘
```

يمكن أيضًا كتابة الاستعلام نفسه باستخدام الكلمة المفتاحية `WITH`.

```sql title="Query" theme={null}
SELECT year, month, day, count(*) FROM t GROUP BY year, month, day WITH ROLLUP;
```

**انظر أيضًا**

* إعداد [group\_by\_use\_nulls](/ar/reference/settings/session-settings#group_by_use_nulls) للتوافق مع معيار SQL.

<div id="cube-modifier">
  ## المُعدِّل `CUBE`
</div>

يُستخدم المُعدِّل `CUBE` لحساب المجاميع الفرعية لكل توليفة من تعبيرات المفاتيح في قائمة `GROUP BY`. وتُضاف صفوف المجاميع الفرعية بعد جدول النتائج.

في صفوف المجاميع الفرعية، تُضبط قيم جميع تعبيرات المفاتيح "المُجمَّعة" على `0` أو سلسلة فارغة.

<Note>
  انتبه إلى أن عبارة [HAVING](/ar/reference/statements/select/having) قد يؤثر في نتائج المجاميع الفرعية.
</Note>

**مثال**

لنفترض الجدول t:

```text theme={null}
┌─year─┬─month─┬─day─┐
│ 2019 │     1 │   5 │
│ 2019 │     1 │  15 │
│ 2020 │     1 │   5 │
│ 2020 │     1 │  15 │
│ 2020 │    10 │   5 │
│ 2020 │    10 │  15 │
└──────┴───────┴─────┘
```

```sql title="Query" theme={null}
SELECT year, month, day, count(*) FROM t GROUP BY CUBE(year, month, day);
```

نظرًا إلى أن قسم `GROUP BY` يحتوي على ثلاثة من تعبيرات المفاتيح، فإن النتيجة تتضمن ثمانية جداول فيها مجاميع فرعية لكل تركيبات تعبيرات المفاتيح:

* `GROUP BY year, month, day`
* `GROUP BY year, month`
* `GROUP BY year, day`
* `GROUP BY year`
* `GROUP BY month, day`
* `GROUP BY month`
* `GROUP BY day`
* والإجماليات.

تُملأ الأعمدة المستبعَدة من `GROUP BY` بالأصفار.

```text title="Response" theme={null}
┌─year─┬─month─┬─day─┬─count()─┐
│ 2020 │    10 │  15 │       1 │
│ 2020 │     1 │   5 │       1 │
│ 2019 │     1 │   5 │       1 │
│ 2020 │     1 │  15 │       1 │
│ 2019 │     1 │  15 │       1 │
│ 2020 │    10 │   5 │       1 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│ 2019 │     1 │   0 │       2 │
│ 2020 │     1 │   0 │       2 │
│ 2020 │    10 │   0 │       2 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│ 2020 │     0 │   5 │       2 │
│ 2019 │     0 │   5 │       1 │
│ 2020 │     0 │  15 │       2 │
│ 2019 │     0 │  15 │       1 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│ 2019 │     0 │   0 │       2 │
│ 2020 │     0 │   0 │       4 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│    0 │     1 │   5 │       2 │
│    0 │    10 │  15 │       1 │
│    0 │    10 │   5 │       1 │
│    0 │     1 │  15 │       2 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│    0 │     1 │   0 │       4 │
│    0 │    10 │   0 │       2 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│    0 │     0 │   5 │       3 │
│    0 │     0 │  15 │       3 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│    0 │     0 │   0 │       6 │
└──────┴───────┴─────┴─────────┘
```

يمكن أيضًا كتابة الاستعلام ذاته باستخدام الكلمة المفتاحية `WITH`.

```sql title="Query" theme={null}
SELECT year, month, day, count(*) FROM t GROUP BY year, month, day WITH CUBE;
```

**انظر أيضًا**

* إعداد [group\_by\_use\_nulls](/ar/reference/settings/session-settings#group_by_use_nulls) لضمان التوافق مع معيار SQL.

<div id="with-totals-modifier">
  ## مُعدِّل `WITH TOTALS`
</div>

إذا تم تحديد المُعدِّل `WITH TOTALS`، فسيُحسَب صف إضافي. سيحتوي هذا الصف على الأعمدة المفتاحية بقيم افتراضية (أصفار أو قيم فارغة)، وعلى أعمدة الدوال التجميعية بالقيم المحسوبة عبر جميع الصفوف (أي قيم "الإجمالي").

لا يُنتَج هذا الصف الإضافي إلا في التنسيقات `JSON*` و`TabSeparated*` و`Pretty*`، ويكون منفصلًا عن الصفوف الأخرى:

* في التنسيقين `XML` و`JSON*`، يُخرَج هذا الصف كحقل `totals` منفصل.
* في التنسيقات `TabSeparated*` و`CSV*` و`Vertical`، يأتي الصف بعد النتيجة الرئيسية، وتسبقه سطر فارغ (بعد البيانات الأخرى).
* في تنسيقات `Pretty*`، يُخرَج الصف كجدول منفصل بعد النتيجة الرئيسية.
* في تنسيق `Template`، يُخرَج الصف وفقًا للقالب المحدد.
* في التنسيقات الأخرى، لا يكون متاحًا.

<Note>
  يُخرَج `totals` في نتائج استعلامات `SELECT`، ولا يُخرَج في `INSERT INTO ... SELECT`.
</Note>

يمكن تنفيذ `WITH TOTALS` بطرق مختلفة عند وجود [HAVING](/ar/reference/statements/select/having). ويعتمد هذا السلوك على الإعداد `totals_mode`.

<div id="configuring-totals-processing">
  ### ضبط معالجة الإجماليات
</div>

افتراضيًا، تكون قيمة `totals_mode = 'before_having'`. في هذه الحالة، يُحتسَب 'totals' على جميع الصفوف، بما في ذلك الصفوف التي لا تجتاز HAVING و`max_rows_to_group_by`.

أما البدائل الأخرى، فتشمل في 'totals' فقط الصفوف التي تجتاز HAVING، ويختلف سلوكها مع الإعداد `max_rows_to_group_by` و`group_by_overflow_mode = 'any'`.

`after_having_exclusive` – لا تُدرِج الصفوف التي لم تجتز `max_rows_to_group_by`. بعبارة أخرى، سيكون عدد الصفوف في 'totals' أقل من أو مساويًا لما سيكون عليه إذا أُزيل `max_rows_to_group_by`.

`after_having_inclusive` – أدرِج جميع الصفوف التي لم تجتز `max_rows_to_group_by` في 'totals'. بعبارة أخرى، سيكون عدد الصفوف في 'totals' أكبر من أو مساويًا لما سيكون عليه إذا أُزيل `max_rows_to_group_by`.

`after_having_auto` – احسب عدد الصفوف التي اجتازت HAVING. إذا كان أكبر من حد معيّن (50% افتراضيًا)، فأدرِج جميع الصفوف التي لم تجتز `max_rows_to_group_by` في 'totals'. وإلا، فلا تُدرِجها.

`totals_auto_threshold` – القيمة الافتراضية هي 0.5. وهو المعامل الخاص بـ `after_having_auto`.

إذا لم يُستخدَم `max_rows_to_group_by` و`group_by_overflow_mode = 'any'`، فستكون جميع أشكال `after_having` متطابقة، ويمكنك استخدام أيٍّ منها (على سبيل المثال، `after_having_auto`).

يمكنك استخدام `WITH TOTALS` في الاستعلامات الفرعية، بما في ذلك الاستعلامات الفرعية في عبارة [JOIN](/ar/reference/statements/select/join) (وفي هذه الحالة، تُدمَج قيم الإجماليات المقابلة).

<div id="group-by-all">
  ## GROUP BY ALL
</div>

يُكافئ `GROUP BY ALL` إدراج جميع التعبيرات المحددة في عبارة `SELECT` التي ليست دوالًا تجميعية.

على سبيل المثال:

```sql theme={null}
SELECT
    a * 2,
    b,
    count(c),
FROM t
GROUP BY ALL
```

مطابق لـ

```sql theme={null}
SELECT
    a * 2,
    b,
    count(c),
FROM t
GROUP BY a * 2, b
```

في حالة خاصة، إذا كانت هناك دالة تتضمن وسائطها كلاً من الدوال التجميعية وحقولاً أخرى، فستتضمن مفاتيح `GROUP BY` أكبر عدد ممكن من الحقول غير التجميعية التي يمكن استخراجها منها.

على سبيل المثال:

```sql theme={null}
SELECT
    substring(a, 4, 2),
    substring(substring(a, 1, 2), 1, count(b))
FROM t
GROUP BY ALL
```

هو نفسه

```sql theme={null}
SELECT
    substring(a, 4, 2),
    substring(substring(a, 1, 2), 1, count(b))
FROM t
GROUP BY substring(a, 4, 2), substring(a, 1, 2)
```

<div id="examples">
  ## أمثلة
</div>

مثال:

```sql theme={null}
SELECT
    count(),
    median(FetchTiming > 60 ? 60 : FetchTiming),
    count() - sum(Refresh)
FROM hits
```

بخلاف MySQL (ووفقًا لمعيار SQL)، لا يمكنك جلب قيمة من عمود غير موجود ضمن مفتاح أو دالة تجميعية (باستثناء expression الثابتة). ولتجاوز ذلك، يمكنك استخدام دالة تجميعية ‏'any'‏ (للحصول على أول قيمة يتم العثور عليها) أو ‏'min/max'‏.

مثال:

```sql theme={null}
SELECT
    domainWithoutWWW(URL) AS domain,
    count(),
    any(Title) AS title -- getting the first occurred page header for each domain.
FROM hits
GROUP BY domain
```

لكل قيمة مفتاح مختلفة تتم مصادفتها، يحسب `GROUP BY` مجموعة من قيم الدوال التجميعية.

<div id="grouping-sets-modifier">
  ## مُعدِّل GROUPING SETS
</div>

هذا هو المُعدِّل الأكثر عمومية.
يتيح هذا المُعدِّل تحديد عدة مجموعات من مفاتيح التجميع يدويًا (grouping sets).
يُجرى التجميع بشكل منفصل لكل مجموعة تجميع، ثم تُدمج جميع النتائج.
إذا لم يكن العمود موجودًا في مجموعة تجميع، فستُملأ قيمته بقيمة افتراضية.

بعبارة أخرى، يمكن تمثيل المُعدِّلات الموضحة أعلاه باستخدام `GROUPING SETS`.
وعلى الرغم من أن الاستعلامات التي تستخدم المُعدِّلات `ROLLUP` و`CUBE` و`GROUPING SETS` متكافئة نحويًا، فقد يختلف أداؤها.
فبينما يحاول `GROUPING SETS` تنفيذ كل شيء بالتوازي، ينفّذ `ROLLUP` و`CUBE` الدمج النهائي للتجميعات في خيط تنفيذ واحد.

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

**مثال**

الاستعلامان التاليان متكافئان.

```sql theme={null}
-- Query 1
SELECT year, month, day, count(*) FROM t GROUP BY year, month, day WITH ROLLUP;

-- Query 2
SELECT year, month, day, count(*) FROM t GROUP BY
GROUPING SETS
(
    (year, month, day),
    (year, month),
    (year),
    ()
);
```

**انظر أيضًا**

* إعداد [group\_by\_use\_nulls](/ar/reference/settings/session-settings#group_by_use_nulls) للتوافق مع معيار SQL.

<div id="implementation-details">
  ## تفاصيل التنفيذ
</div>

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

<div id="group-by-optimization-depending-on-table-sorting-key">
  ### تحسين `GROUP BY` اعتمادًا على مفتاح فرز الجدول
</div>

يمكن إجراء التجميع بكفاءة أكبر إذا كان الجدول مفروزًا وفقًا لمفتاح معيّن، وكان تعبير `GROUP BY` يحتوي على بادئة مفتاح الفرز على الأقل أو دوال حقنية. في هذه الحالة، عند قراءة مفتاح جديد من الجدول، يمكن إنهاء النتيجة المرحلية للتجميع وإرسالها إلى العميل. يُفعَّل هذا السلوك بواسطة الإعداد [optimize\_aggregation\_in\_order](/ar/reference/settings/session-settings#optimize_aggregation_in_order). يقلّل هذا التحسين من استخدام الذاكرة أثناء التجميع، لكنه قد يبطئ تنفيذ الاستعلام في بعض الحالات.

<div id="group-by-in-external-memory">
  ### GROUP BY في الذاكرة الخارجية
</div>

يمكنك تمكين كتابة البيانات المؤقتة إلى القرص لتقييد استهلاك الذاكرة أثناء `GROUP BY`.
يحدّد الإعداد [max\_bytes\_before\_external\_group\_by](/ar/reference/settings/session-settings#max_bytes_before_external_group_by) حد استهلاك RAM الذي عنده تُكتب البيانات المؤقتة الخاصة بـ `GROUP BY` إلى نظام الملفات. وإذا ضُبط على 0 (القيمة الافتراضية)، فسيكون معطّلًا.
وبديلًا من ذلك، يمكنك ضبط [max\_bytes\_ratio\_before\_external\_group\_by](/ar/reference/settings/session-settings#max_bytes_ratio_before_external_group_by)، الذي يتيح استخدام `GROUP BY` في الذاكرة الخارجية فقط عندما يصل الاستعلام إلى حد معيّن من الذاكرة المستخدمة.

عند استخدام `max_bytes_before_external_group_by`، نوصي بضبط `max_memory_usage` على قيمة تقارب الضعف (أو `max_bytes_ratio_before_external_group_by=0.5`). وهذا ضروري لأن للتجميع مرحلتين: قراءة البيانات وتكوين البيانات الوسيطة (1)، ثم دمج البيانات الوسيطة (2). ولا يمكن كتابة البيانات إلى نظام الملفات إلا خلال المرحلة 1. وإذا لم تُكتب البيانات المؤقتة، فقد تتطلب المرحلة 2 مقدارًا من الذاكرة يصل إلى المقدار نفسه المطلوب في المرحلة 1.

على سبيل المثال، إذا كان [max\_memory\_usage](/ar/reference/settings/session-settings#max_memory_usage) مضبوطًا على 10000000000 وكنت تريد استخدام التجميع الخارجي، فمن المنطقي ضبط `max_bytes_before_external_group_by` على 10000000000، و`max_memory_usage` على 20000000000. وعند بدء التجميع الخارجي (إذا حدثت كتابة واحدة على الأقل للبيانات المؤقتة)، يكون الحد الأقصى لاستهلاك RAM أعلى بقليل فقط من `max_bytes_before_external_group_by`.

مع معالجة الاستعلامات الموزعة، يُنفَّذ التجميع الخارجي على الخوادم البعيدة. ولكي يستخدم الخادم الذي يرسل الطلب مقدارًا صغيرًا فقط من RAM، اضبط `distributed_aggregation_memory_efficient` على 1.

عند دمج البيانات التي كُتبت إلى القرص، وكذلك عند دمج النتائج من الخوادم البعيدة عندما يكون الإعداد `distributed_aggregation_memory_efficient` مفعّلًا، قد يصل استهلاك الذاكرة إلى `1/256 * the_number_of_threads` من إجمالي مقدار RAM.

عند تمكين التجميع الخارجي، إذا كان حجم البيانات أقل من `max_bytes_before_external_group_by` (أي إن البيانات لم تُكتب إلى القرص)، فسيعمل الاستعلام بالسرعة نفسها كما لو لم يكن التجميع الخارجي مفعّلًا. وإذا كُتبت أي بيانات مؤقتة إلى القرص، فسيكون زمن التشغيل أطول عدة مرات (حوالي ثلاث مرات).

إذا كان لديك [ORDER BY](/ar/reference/statements/select/order-by) مع [LIMIT](/ar/reference/statements/select/limit) بعد `GROUP BY`، فإن مقدار RAM المستخدم يعتمد على مقدار البيانات في `LIMIT`، وليس في الجدول بأكمله. ولكن إذا كان `ORDER BY` لا يحتوي على `LIMIT`، فلا تنسَ تمكين الفرز الخارجي (`max_bytes_before_external_sort`).
