> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> يتيح الكتابة السريعة لحالات الكائنات التي تتغير باستمرار، وحذف حالات الكائنات القديمة في الخلفية.

# محرك جدول VersionedCollapsingMergeTree

يتميز هذا المحرك بما يلي:

* يتيح الكتابة السريعة لحالات الكائنات التي تتغير باستمرار.
* يحذف حالات الكائنات القديمة في الخلفية. وهذا يقلل حجم التخزين بشكل كبير.

راجع قسم [طي](#table_engines_versionedcollapsingmergetree) لمزيد من التفاصيل.

يرث هذا المحرك من [MergeTree](/docs/ar/reference/engines/table-engines/mergetree-family/mergetree) ويضيف منطق طي الصفوف إلى خوارزمية دمج أجزاء البيانات. يؤدي `VersionedCollapsingMergeTree` الغرض نفسه الذي يؤديه [CollapsingMergeTree](/docs/ar/reference/engines/table-engines/mergetree-family/collapsingmergetree)، لكنه يستخدم خوارزمية طي مختلفة تتيح إدراج البيانات بأي ترتيب باستخدام عدة خيوط. وعلى وجه الخصوص، يساعد العمود `Version` على طي الصفوف بشكل صحيح حتى إذا أُدرجت بترتيب غير صحيح. في المقابل، لا يتيح `CollapsingMergeTree` إلا الإدراج المتتالي الصارم.

<div id="creating-a-table">
  ## إنشاء جدول
</div>

```sql theme={null}
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
    ...
) ENGINE = VersionedCollapsingMergeTree(sign, version)
[PARTITION BY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]
```

للاطلاع على وصف معلمات الاستعلام، راجع [وصف الاستعلام](/docs/ar/reference/statements/create/table).

<div id="engine-parameters">
  ### معلمات المحرك
</div>

```sql theme={null}
VersionedCollapsingMergeTree(sign, version)
```

| المعلمة   | الوصف                                                                 | النوع                                                                                                                                                                                                                                                                                    |
| --------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sign`    | اسم العمود الذي يحدد نوع الصف: `1` هو صف "حالة"، و`-1` هو صف "إلغاء". | [`Int8`](/docs/ar/reference/data-types/int-uint)                                                                                                                                                                                                                                              |
| `version` | اسم العمود الذي يحتوي على إصدار حالة الكائن.                          | [`Int*`](/docs/ar/reference/data-types/int-uint), [`UInt*`](/docs/ar/reference/data-types/int-uint), [`Date`](/docs/ar/reference/data-types/date), [`Date32`](/docs/ar/reference/data-types/date32), [`DateTime`](/docs/ar/reference/data-types/datetime), أو [`DateTime64`](/docs/ar/reference/data-types/datetime64) |

<div id="query-clauses">
  ### بنود الاستعلام
</div>

عند إنشاء جدول `VersionedCollapsingMergeTree`، تكون [البنود](/docs/ar/reference/engines/table-engines/mergetree-family/mergetree) نفسها مطلوبة كما عند إنشاء جدول `MergeTree`.

<details markdown="1">
  <summary>الطريقة المهجورة لإنشاء جدول</summary>

  <Note>
    لا تستخدم هذه الطريقة في المشاريع الجديدة. وإذا أمكن، انقل المشاريع القديمة إلى الطريقة الموضحة أعلاه.
  </Note>

  ```sql theme={null}
  CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
  (
      name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
      name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
      ...
  ) ENGINE [=] VersionedCollapsingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, sign, version)
  ```

  جميع المعلمات، باستثناء `sign` و`version`، لها المعنى نفسه كما في `MergeTree`.

  * `sign` — اسم العمود الذي يحدد نوع الصف: `1` هو صف "حالة"، و`-1` هو صف "إلغاء".

    نوع بيانات العمود — `Int8`.

  * `version` — اسم العمود الذي يحتوي على إصدار حالة الكائن.

    يجب أن يكون نوع بيانات العمود `UInt*`.
</details>

<div id="table_engines_versionedcollapsingmergetree">
  ## الاختزال
</div>

<div id="data">
  ### البيانات
</div>

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

استخدم العمود `Sign` عند كتابة الصف. إذا كانت قيمة `Sign = 1` فهذا يعني أن الصف يمثّل حالة لكائن ما (لنسمّه صف "الحالة"). وإذا كانت قيمة `Sign = -1` فهذا يشير إلى إلغاء حالة لكائن له السمات نفسها (لنسمّه صف "الإلغاء"). واستخدم أيضًا العمود `Version`، الذي ينبغي أن يميّز كل حالة من حالات الكائن برقم مستقل.

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

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         5 │      146 │    1 │       1 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
```

في مرحلة لاحقة، نسجّل التغيير في نشاط المستخدم وندوّنه في الصفّين التاليين.

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         5 │      146 │   -1 │       1 |
│ 4324182021466249494 │         6 │      185 │    1 │       2 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
```

يلغي الصف الأول الحالة السابقة للكائن (المستخدم). ويجب أن ينسخ جميع حقول الحالة المُلغاة باستثناء `Sign`.

يحتوي الصف الثاني على الحالة الحالية.

وبما أننا نحتاج فقط إلى أحدث حالة لنشاط المستخدم، فإن الصفوف

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         5 │      146 │    1 │       1 |
│ 4324182021466249494 │         5 │      146 │   -1 │       1 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
```

يمكن حذفها، مما يؤدي إلى طيّ الحالة غير الصالحة (القديمة) للكائن. ويقوم `VersionedCollapsingMergeTree` بذلك أثناء دمج أجزاء البيانات.

لمعرفة سبب حاجتنا إلى صفّين لكل تغيير، راجع [Algorithm](#table_engines-versionedcollapsingmergetree-algorithm).

**ملاحظات حول الاستخدام**

1. يجب أن يتذكر البرنامج الذي يكتب البيانات حالة الكائن حتى يتمكن من إلغائها. يجب أن يحتوي السطر "إلغاء" على نسخ من حقول المفتاح الأساسي، وإصدار السطر "حالة"، وقيمة `Sign` المعاكسة. يزيد ذلك الحجم الأولي للتخزين، لكنه يتيح كتابة البيانات بسرعة.
2. تؤدي المصفوفات الطويلة والمتزايدة في الأعمدة إلى تقليل كفاءة المحرك بسبب العبء الواقع على عمليات الكتابة. وكلما كانت البيانات أبسط، كانت الكفاءة أفضل.
3. تعتمد نتائج `SELECT` بدرجة كبيرة على اتساق سجل تغييرات الكائن. كن دقيقًا عند إعداد البيانات للإدراج. فقد تحصل على نتائج غير متوقعة مع البيانات غير المتسقة، مثل القيم السالبة لمقاييس غير سالبة مثل عمق الجلسة.

<div id="table_engines-versionedcollapsingmergetree-algorithm">
  ### الخوارزمية
</div>

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

عندما يُدرِج ClickHouse البيانات، فإنه يرتب الصفوف حسب المفتاح الأساسي. وإذا لم يكن العمود `Version` ضمن المفتاح الأساسي، يضيفه ClickHouse ضمنيًا إلى المفتاح الأساسي باعتباره الحقل الأخير ويستخدمه في الترتيب.

<div id="selecting-data">
  ## اختيار البيانات
</div>

لا يضمن ClickHouse أن تكون جميع الصفوف التي لها المفتاح الأساسي نفسه في جزء البيانات الناتج نفسه، أو حتى على الخادم الفعلي نفسه. وينطبق ذلك سواء عند كتابة البيانات أو عند الدمج اللاحق لأجزاء البيانات. بالإضافة إلى ذلك، يعالج ClickHouse استعلامات `SELECT` باستخدام عدة خيوط تنفيذ، ولا يمكنه التنبؤ بترتيب الصفوف في النتيجة. وهذا يعني أن التجميع مطلوب إذا كانت هناك حاجة إلى الحصول على بيانات "مطوية" بالكامل من جدول `VersionedCollapsingMergeTree`.

لإتمام الطي، اكتب استعلامًا يتضمن عبارة `GROUP BY` ودوال تجميع تراعي الإشارة. على سبيل المثال، لحساب الكمية، استخدم `sum(Sign)` بدلًا من `count()`. ولحساب مجموع قيمةٍ ما، استخدم `sum(Sign * x)` بدلًا من `sum(x)`، وأضف `HAVING sum(Sign) > 0`.

يمكن حساب `count` و`sum` و`avg` بهذه الطريقة. ويمكن حساب `uniq` إذا كان للكائن حالة واحدة غير مطوية على الأقل. أما `min` و`max` فلا يمكن حسابهما لأن `VersionedCollapsingMergeTree` لا يحتفظ بسجل قيم الحالات المطوية.

إذا كنت بحاجة إلى استخراج البيانات مع "الطي" ولكن من دون تجميع (على سبيل المثال، للتحقق مما إذا كانت هناك صفوف تتطابق أحدث قيمها مع شروط معينة)، فيمكنك استخدام المعدِّل `FINAL` مع عبارة `FROM`. هذا النهج غير فعّال ويجب عدم استخدامه مع الجداول الكبيرة.

<div id="example-of-use">
  ## مثال للاستخدام
</div>

بيانات المثال:

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         5 │      146 │    1 │       1 |
│ 4324182021466249494 │         5 │      146 │   -1 │       1 |
│ 4324182021466249494 │         6 │      185 │    1 │       2 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
```

إنشاء الجدول:

```sql theme={null}
CREATE TABLE UAct
(
    UserID UInt64,
    PageViews UInt8,
    Duration UInt8,
    Sign Int8,
    Version UInt8
)
ENGINE = VersionedCollapsingMergeTree(Sign, Version)
ORDER BY UserID
```

إدخال البيانات:

```sql theme={null}
INSERT INTO UAct VALUES (4324182021466249494, 5, 146, 1, 1)
```

```sql theme={null}
INSERT INTO UAct VALUES (4324182021466249494, 5, 146, -1, 1),(4324182021466249494, 6, 185, 1, 2)
```

نستخدم استعلامَي `INSERT` لإنشاء جزأَي بيانات مختلفين. وإذا أدرجنا البيانات باستعلام واحد، فسينشئ ClickHouse جزء بيانات واحدًا ولن ينفّذ أي عملية دمج.

جلب البيانات:

```sql theme={null}
SELECT * FROM UAct
```

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         5 │      146 │    1 │       1 │
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         5 │      146 │   -1 │       1 │
│ 4324182021466249494 │         6 │      185 │    1 │       2 │
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
```

ماذا نرى هنا، وأين الأجزاء التي تم طيّها؟
لقد أنشأنا جزأين من البيانات باستخدام استعلامَي `INSERT`. نُفِّذ استعلام `SELECT` على خيطَي تنفيذ، وكانت النتيجة ترتيبًا عشوائيًا للصفوف.
لم يحدث الطي لأن أجزاء البيانات لم تُدمج بعد. يدمج ClickHouse أجزاء البيانات في وقت غير معلوم لا يمكننا التنبؤ به.

ولهذا نحتاج إلى التجميع:

```sql theme={null}
SELECT
    UserID,
    sum(PageViews * Sign) AS PageViews,
    sum(Duration * Sign) AS Duration,
    Version
FROM UAct
GROUP BY UserID, Version
HAVING sum(Sign) > 0
```

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Version─┐
│ 4324182021466249494 │         6 │      185 │       2 │
└─────────────────────┴───────────┴──────────┴─────────┘
```

إذا لم نكن بحاجة إلى التجميع وأردنا فرض عملية الطيّ، يمكننا استخدام المُعدِّل `FINAL` مع عبارة `FROM`.

```sql theme={null}
SELECT * FROM UAct FINAL
```

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         6 │      185 │    1 │       2 │
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
```

هذه طريقة غير فعّالة للغاية لاستخراج البيانات. لا تستخدمها مع الجداول الكبيرة.
