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

> صفحة تشرح المحلّل في ClickHouse

# المحلّل

في الإصدار `24.3` من ClickHouse، كان المحلّل مفعّلًا افتراضيًا.
يمكنك الاطّلاع على مزيد من التفاصيل حول كيفية عمله [هنا](/ar/guides/clickhouse/performance-and-monitoring/understanding-query-execution-with-the-analyzer#analyzer).

اعتبارًا من الإصدار `26.9` أصبح المحلّل إلزاميًا: فالإعداد `enable_analyzer` أصبح مهملًا، وأي محاولة لضبطه على `0` تُرفض، ولم يعد تحليل الاستعلامات الذي كان ClickHouse يستخدمه قبل `24.3` مدعومًا. توضّح حالات عدم التوافق المذكورة أدناه أوجه اختلاف ذلك التحليل الأقدم، بما يتيح تحديث أي استعلام كُتب من أجله؛ ولمعاينة سلوكه، شغّل الاستعلام على إصدار من ClickHouse أقدم من `26.9`.

<h2 id="known-incompatibilities">
  حالات عدم التوافق المعروفة
</h2>

على الرغم من إصلاح عدد كبير من الأخطاء وإضافة تحسينات جديدة، فإنه يقدّم أيضًا بعض التغييرات غير المتوافقة مع الإصدارات السابقة في سلوك ClickHouse. يُرجى قراءة التغييرات التالية لمعرفة كيفية إعادة كتابة استعلاماتك لتناسب المحلّل.

<h3 id="invalid-queries-are-no-longer-optimized">
  لم تعد الاستعلامات غير الصالحة تُحسَّن
</h3>

كانت البنية التحتية السابقة لتخطيط الاستعلامات تطبّق تحسينات على مستوى AST قبل خطوة التحقّق من صحة الاستعلام.
وكانت هذه التحسينات قادرة على إعادة كتابة الاستعلام الأوّلي بحيث يصبح صالحًا وقابلًا للتنفيذ.

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

<h4 id="example-1">
  المثال 1
</h4>

يستخدم الاستعلام التالي العمود `number` في قائمة الإسقاط، مع أن المتاح بعد التجميع هو `toString(number)` فقط.
في المحلّل القديم، كان `GROUP BY toString(number)` يُحسَّن إلى `GROUP BY number,`، مما يجعل الاستعلام صالحًا.

```sql theme={null}
SELECT number
FROM numbers(1)
GROUP BY toString(number)
```

<h4 id="example-2">
  المثال 2
</h4>

تحدث المشكلة نفسها في هذا الاستعلام. يُستخدَم العمود `number` بعد التجميع مع مفتاح آخر.
كان محلّل الاستعلام السابق يُصحّح هذا الاستعلام بنقل عامل التصفية `number > 5` من عبارة `HAVING` إلى عبارة `WHERE`.

```sql theme={null}
SELECT
    number % 2 AS n,
    sum(number)
FROM numbers(10)
GROUP BY n
HAVING number > 5
```

لإصلاح الاستعلام، ينبغي نقل جميع الشروط التي تخص الأعمدة غير المجمّعة إلى قسم `WHERE` للامتثال لصياغة SQL القياسية:

```sql theme={null}
SELECT
    number % 2 AS n,
    sum(number)
FROM numbers(10)
WHERE number > 5
GROUP BY n
```

لتسهيل الترحيل، يمكن لمحلّل الاستعلام تكرار إعادة الكتابة القديمة من `HAVING` إلى `WHERE` للمقترنات غير التجميعية المتصلة بـ AND. فعِّل `analyzer_compatibility_allow_non_aggregate_in_having = 1` لتمكين هذا السلوك. هذا الإعداد متاح منذ ClickHouse `26.7`. يُتجاهل هذا الإعداد مع `WITH CUBE` و`WITH ROLLUP` و`WITH TOTALS` و`GROUPING SETS`. تبقى المقترنات التي تحتوي على دوال تجميعية أو `grouping` أو دوال غير حتمية في `HAVING`؛ وإذا احتوى أي مقترن على دالة نافذة أو دالة ذات حالة (مثل `rowNumberInBlock`)، فستُعطَّل إعادة الكتابة لعبارة `HAVING` بالكامل، بما يطابق السلوك القديم.

<h3 id="create-view-with-invalid-query">
  `CREATE VIEW` مع استعلام غير صالح
</h3>

يجري المحلِّل دائمًا التحقّق من الأنواع.
في السابق، كان من الممكن إنشاء `VIEW` باستعلام `SELECT` غير صالح.
ثم كان يفشل عند تنفيذ أول `SELECT` أو `INSERT` (في حالة `MATERIALIZED VIEW`).

لم يعد إنشاء `VIEW` بهذه الطريقة ممكنًا.

<h4 id="example-view">
  مثال
</h4>

```sql theme={null}
CREATE TABLE source (data String)
ENGINE=MergeTree
ORDER BY tuple();

CREATE VIEW some_view
AS SELECT JSONExtract(data, 'test', 'DateTime64(3)')
FROM source;
```

<h3 id="known-incompatibilities-of-the-join-clause">
  أوجه عدم التوافق المعروفة لعبارة `JOIN`
</h3>

<h4 id="join-using-column-from-projection">
  `JOIN` باستخدام عمود من إسقاط
</h4>

لا يمكن افتراضيًا استخدام اسم مستعار من قائمة `SELECT` كمفتاح `JOIN USING`.

عند تمكين الإعداد الجديد `analyzer_compatibility_join_using_top_level_identifier`، فإنه يغيّر سلوك `JOIN USING` بحيث يفضّل تفسير المعرّفات استنادًا إلى التعبيرات الواردة في قائمة الإسقاط ضمن استعلام `SELECT`، بدلًا من استخدام أعمدة الجدول الأيسر مباشرةً.

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

```sql theme={null}
SELECT a + 1 AS b, t2.s
FROM VALUES('a UInt64, b UInt64', (1, 1)) AS t1
JOIN VALUES('b UInt64, s String', (1, 'one'), (2, 'two')) t2
USING (b);
```

عند ضبط `analyzer_compatibility_join_using_top_level_identifier` على `true`، يُفسَّر شرط الـ join على أنه `t1.a + 1 = t2.b`، بما يتوافق مع سلوك الإصدارات السابقة.
ستكون النتيجة `2, 'two'`.
وعندما تكون قيمة الإعداد `false`، يكون شرط الـ join افتراضيًا `t1.b = t2.b`، وسيُرجع الاستعلام `2, 'one'`.
إذا لم يكن `b` موجودًا في `t1`، فسيفشل الاستعلام مع ظهور خطأ.

<h4 id="changes-in-behavior-with-join-using-and-aliasmaterialized-columns">
  تغيّرات في السلوك مع `JOIN USING` والأعمدة `ALIAS`/`MATERIALIZED`
</h4>

في المُحلِّل، يؤدي استخدام `*` في استعلام `JOIN USING` يتضمن أعمدة `ALIAS` أو `MATERIALIZED` إلى إدراج هذه الأعمدة في مجموعة النتائج افتراضيًا.

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

```sql theme={null}
CREATE TABLE t1 (id UInt64, payload ALIAS sipHash64(id)) ENGINE = MergeTree ORDER BY id;
INSERT INTO t1 VALUES (1), (2);

CREATE TABLE t2 (id UInt64, payload ALIAS sipHash64(id)) ENGINE = MergeTree ORDER BY id;
INSERT INTO t2 VALUES (2), (3);

SELECT * FROM t1
FULL JOIN t2 USING (payload);
```

في المحلل، ستتضمن نتيجة هذا الاستعلام العمود `payload` إلى جانب `id` من كلا الجدولين.
في المقابل، كان المحلل السابق لا يتضمن أعمدة `ALIAS` هذه إلا إذا كانت إعدادات محددة (`asterisk_include_alias_columns` أو `asterisk_include_materialized_columns`) مفعّلة،
كما قد تظهر الأعمدة بترتيب مختلف.

ولضمان الحصول على نتائج متسقة ومتوقعة، خاصةً عند ترحيل الاستعلامات القديمة إلى المحلل، يُنصح بتحديد الأعمدة صراحةً في عبارة `SELECT` بدلًا من استخدام `*`.

<h4 id="handling-of-type-modifiers-for-columns-in-using-clause">
  التعامل مع معدِّلات النوع للأعمدة في بند `USING`
</h4>

في المحلّل، جرى توحيد قواعد تحديد النوع الأعلى المشترك للأعمدة المحددة في بند `USING` لإعطاء نتائج أكثر قابلية للتنبؤ،
خصوصًا عند التعامل مع معدِّلات النوع مثل `LowCardinality` و`Nullable`.

* `LowCardinality(T)` و`T`: عند ضمّ عمود من النوع `LowCardinality(T)` إلى عمود من النوع `T`، سيكون النوع الأعلى المشترك الناتج هو `T`، ما يعني عمليًا تجاهل معدِّل `LowCardinality`.
* `Nullable(T)` و`T`: عند ضمّ عمود من النوع `Nullable(T)` إلى عمود من النوع `T`، سيكون النوع الأعلى المشترك الناتج هو `Nullable(T)`، بما يضمن الحفاظ على خاصية قبول القيم `NULL`.

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

```sql theme={null}
SELECT id, toTypeName(id)
FROM VALUES('id LowCardinality(String)', ('a')) AS t1
FULL OUTER JOIN VALUES('id String', ('b')) AS t2
USING (id);
```

في هذا الاستعلام، يُحدَّد النوع الأعلى المشترك لـ `id` على أنه `String`، مع إسقاط المُعدِّل `LowCardinality` من `t1`.

<h3 id="projection-column-names-changes">
  تغييرات على أسماء أعمدة الإسقاط
</h3>

أثناء حساب أسماء الإسقاطات، لا يجري استبدال الأسماء المستعارة.

```sql theme={null}
SELECT
    1 + 1 AS x,
    x + 1
FORMAT PrettyCompact
```

قبل الإصدار `24.3` كان العمود الثاني يُسمّى باسم الاسم المستعار المُستبدل:

```text theme={null}
   ┌─x─┬─plus(plus(1, 1), 1)─┐
1. │ 2 │                   3 │
   └───┴─────────────────────┘
```

يُبقي الـ محلّل الاسم المستعار في الاسم:

```text theme={null}
   ┌─x─┬─plus(x, 1)─┐
1. │ 2 │          3 │
   └───┴────────────┘
```

<h3 id="incompatible-function-arguments-types">
  أنواع وسائط الدالة غير المتوافقة
</h3>

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

على سبيل المثال، يفشل الاستعلام التالي مع `There is no supertype for types Array(UInt8), String because some of them are Array and some of them are not`:

```sql theme={null}
SELECT toTypeName(if(0, [2, 3, 4], 'String'))
```

<h3 id="heterogeneous-clusters">
  العناقيد غير المتجانسة
</h3>

يُحدِث المُحلِّل تغييرًا كبيرًا في بروتوكول الاتصال بين الخوادم داخل العنقود. لذلك، يتعذّر تشغيل الاستعلامات الموزعة على خوادم لا تتفق على ما إذا كان المُحلِّل مُستخدمًا — وهذا يعني، في عنقود من خوادم أقدم من `26.9`، خوادم تستخدم قيماً مختلفة للإعداد `enable_analyzer`.

لم يعد لدى الخادم من الإصدار `26.10` أو أحدث أي آلية أخرى لتحليل الاستعلام، لذا فهو يتجاهل القيمة التي يرسلها المُنشئ الأقدم ويحلّل الاستعلام بالمُحلِّل في كل الأحوال. ولا يُسمّي التحليلان أعمدة النتيجة بالطريقة نفسها، كما أن المُنشئ يُطابق الكتلة التي يُعيدها الـ shard بحسب اسم الـ column، ولذلك قد يفشل مثل هذا الاستعلام لدى المُنشئ بالخطأ `NOT_FOUND_COLUMN_IN_BLOCK` — مثلاً عندما يحدّد دالة مكتوبة بحالة أحرف غير قياسية (`hostname()`)، فيحوّلها المُحلِّل إلى اسمها القياسي (`hostName()`). لذلك يجب على أي عنقود ما يزال يعمل بتحليل الاستعلام الأقدم ضبط `enable_analyzer = 1` على كل خادم قبل ترقية أي منها إلى `26.10`.

<h3 id="unsupported-features">
  الميزات غير المدعومة
</h3>

فيما يلي قائمة بالميزات التي لا يدعمها المحلّل حاليًا:

* فهرس Annoy.
* فهرس Hypothesis. العمل جارٍ عليه [هنا](https://github.com/ClickHouse/ClickHouse/pull/48381).

<h2 id="cloud-migration">
  الترحيل إلى Cloud
</h2>

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

<h3 id="migration-workflow">
  سير عمل الترحيل
</h3>

1. حدِّد الاستعلام بتصفية `system.query_log` باستخدام `normalized_query_hash`:

```sql theme={null}
SELECT query 
FROM clusterAllReplicas(default, system.query_log)
WHERE normalized_query_hash='{hash}' 
LIMIT 1 
SETTINGS skip_unavailable_shards=1
```

2. نفّذ الاستعلام باستخدام المحلّل، مع إضافة إعداد التوافق الذي يستعيد آلية تحليل المعرّفات في التحليل الأقدم في الحالات التي يعتمد فيها الاستعلام عليها.

```sql theme={null}
SETTINGS
    analyzer_compatibility_join_using_top_level_identifier=1
```

3. أعد صياغة الاستعلام وتحقّق من نتائج الاستعلام للتأكد من أنها تطابق المخرجات التي أنتجها الاستعلام قبل الترحيل.

يُرجى الرجوع إلى أكثر حالات عدم التوافق شيوعًا التي ظهرت أثناء الاختبارات الداخلية.

<h3 id="unknown-expression-identifier">
  معرّف تعبير غير معروف
</h3>

الخطأ: `Unknown expression identifier ... in scope ... (UNKNOWN_IDENTIFIER)`. رمز الاستثناء: 47

السبب: تُعرَّف الآن الاستعلامات التي تعتمد على سلوكيات قديمة متساهلة وغير قياسية — مثل الإشارة إلى الأسماء المستعارة المحسوبة داخل عوامل التصفية، أو عناصر الإسقاط الملتبسة في الاستعلامات الفرعية، أو نطاق تعبير الجدول الشائع "dynamic" — تعريفًا صحيحًا على أنها غير صالحة، وتُرفض فورًا.

الحل: حدّث أنماط SQL لديك كما يلي:

* منطق التصفية: انقل المنطق من WHERE إلى HAVING إذا كنت تطبّق التصفية على النتائج، أو كرّر التعبير في WHERE إذا كنت تطبّق التصفية على البيانات المصدر.
* نطاق الاستعلام الفرعي: حدّد صراحةً جميع الأعمدة التي يحتاجها الاستعلام الخارجي.
* مفاتيح JOIN: استخدم ON مع تعبيرات كاملة بدلًا من USING إذا كان المفتاح اسمًا مستعارًا.
* في الاستعلامات الخارجية، ارجع إلى الاسم المستعار الخاص بالاستعلام الفرعي/تعبير الجدول الشائع نفسه، لا إلى الجداول الموجودة داخله.

<h3 id="non-aggregated-columns-in-group-by">
  الأعمدة غير المجمّعة في GROUP BY
</h3>

الخطأ: `Column ... is not under aggregate function and not in GROUP BY keys (NOT_AN_AGGREGATE)`. رمز الاستثناء: 215

السبب: كان المحلّل القديم يسمح بتحديد أعمدة غير موجودة في بند GROUP BY (وغالبًا ما كان يختار قيمة عشوائية). يلتزم المحلّل بمعيار SQL القياسي: يجب أن يكون كل عمود محدد إما تجميعيًا أو مفتاح تجميع.

الحل: غلّف العمود داخل `any()` أو `argMax()`، أو أضِفه إلى GROUP BY.

```sql theme={null}
/* ORIGINAL QUERY */
-- device_id is ambiguous
SELECT user_id, device_id FROM table GROUP BY user_id

/* FIXED QUERY */
SELECT user_id, any(device_id) FROM table GROUP BY user_id
-- OR
SELECT user_id, device_id FROM table GROUP BY user_id, device_id
```

<h3 id="non-aggregated-columns-in-having">
  الأعمدة غير المجمّعة في `HAVING`
</h3>

الخطأ: `Column ... is not under aggregate function and not in GROUP BY keys (NOT_AN_AGGREGATE)`. رمز الاستثناء: 215

السبب: كان المحلّل القديم ينقل بصمت الشروط غير التجميعية من مقترنات `AND` من `HAVING` إلى `WHERE`، ويعاملها كعوامل تصفية قبل التجميع. يلتزم المحلّل بـ SQL القياسية: لا يجوز أن يشير `HAVING` إلا إلى مفاتيح التجميع والدوال التجميعية.

الحل: انقل الشرط من `HAVING` إلى `WHERE` يدويًا، أو فعّل `analyzer_compatibility_allow_non_aggregate_in_having = 1` (متاح منذ ClickHouse `26.7`) لاستعادة إعادة الكتابة القديمة كمساعدة في الترحيل. يتم تجاهل إعداد `compatibility` مع `WITH CUBE` و`WITH ROLLUP` و`WITH TOTALS` و`GROUPING SETS`. وتبقى الشروط التي تحتوي على دوال تجميعية أو `grouping` أو دوال غير حتمية داخل `HAVING`؛ وإذا احتوى أي شرط على دالة نافذة أو دالة ذات حالة (مثل `rowNumberInBlock`)، فسيتم تعطيل إعادة الكتابة لكل `HAVING`، بما يتوافق مع السلوك القديم.

```sql theme={null}
/* ORIGINAL QUERY */
SELECT category, sum(value) FROM t GROUP BY category HAVING service = 'svc1';

/* FIXED QUERY */
SELECT category, sum(value) FROM t WHERE service = 'svc1' GROUP BY category;
```

<h3 id="duplicate-cte-names">
  تكرار أسماء CTE
</h3>

الخطأ: `CTE with name ... already exists (MULTIPLE_EXPRESSIONS_FOR_ALIAS)`. رمز الاستثناء: 179

السبب: كان المحلّل القديم يسمح بتعريف عدة تعابير جداول شائعة (WITH ...) بالاسم نفسه، بحيث يحجب التعريف اللاحق التعريف السابق. أما المحلّل الجديد فيرفض هذا الالتباس افتراضيًا.

الحل: أعد تسمية تعابير الجدول الشائعة المكررة بحيث يصبح لكلٍّ منها اسم مميز. ولتسهيل الترحيل، يمكنك تفعيل `analyzer_compatibility_allow_cte_redefinition = 1` (متاح منذ ClickHouse `26.10`) لاستعادة السلوك القديم، إذ يرتبط كل مرجع بأحدث تعريف للاسم لا يجري تحليله في تلك اللحظة. وبذلك يمكن لإعادة التعريف أن تقرأ التعريف السابق، بينما يقرأ متن الاستعلام التعريف الأخير.

القيود: لا يمكن إعادة تعريف تعبير الجدول الشائع المُعلَن بوصفه `MATERIALIZED`، ولا تعبير الجدول الشائع الوارد في عبارة `WITH RECURSIVE`، حتى مع تفعيل هذا الإعداد. وثمة حالة واحدة يختلف فيها السلوك عن المحلّل القديم: فتعبير الجدول الشائع المُعلَن بين تعريفين للاسم نفسه يرتبط هو أيضًا بالتعريف الأخير، في حين كان المحلّل القديم يربطه بالتعريف المرئي عند موضع إعلانه.

```sql theme={null}
/* ORIGINAL QUERY */
WITH
  data AS (SELECT 1 AS id),
  data AS (SELECT id + 1 AS id FROM data) -- Redefined, reads the previous definition
SELECT * FROM data;

/* FIXED QUERY */
WITH
  raw_data AS (SELECT 1 AS id),
  processed_data AS (SELECT id + 1 AS id FROM raw_data)
SELECT * FROM processed_data;

/* LEGACY BEHAVIOR AS A MIGRATION AID */
WITH
  data AS (SELECT 1 AS id),
  data AS (SELECT id + 1 AS id FROM data)
SELECT * FROM data
SETTINGS analyzer_compatibility_allow_cte_redefinition = 1;
```

<h3 id="ambiguous-column-identifiers">
  معرّفات الأعمدة الملتبسة
</h3>

الخطأ: `JOIN [JOIN TYPE] ambiguous identifier ... (AMBIGUOUS_IDENTIFIER)` رمز الاستثناء: 207

السبب: يشير الاستعلام إلى اسم عمود موجود في عدة جداول ضمن JOIN من دون تحديد الجدول المصدر. وكان المحلّل القديم غالبًا ما يستنتج العمود استنادًا إلى المنطق الداخلي، بينما يتطلب المحلّل تحديد الاسم صراحةً.

الحل: حدّد العمود بالكامل باستخدام table\_alias.column\_name.

```sql theme={null}
/* ORIGINAL QUERY */
SELECT table1.ID AS ID FROM table1, table2 WHERE ID...

/* FIXED QUERY */
SELECT table1.ID AS ID_RENAMED FROM table1, table2 WHERE ID_RENAMED...
```

<h3 id="invalid-usage-of-final">
  استخدام غير صحيح لـ FINAL
</h3>

الخطأ: `Table expression modifiers FINAL are not supported for subquery...` أو `Storage ... doesn't support FINAL` (`UNSUPPORTED_METHOD`). رموز الاستثناء: 1، 181

السبب: FINAL هو معدِّل لتخزين الجدول (وتحديدًا \[Shared]ReplacingMergeTree). يرفض المحلِّل FINAL عند تطبيقه على:

* الاستعلامات الفرعية أو الجداول المشتقة (مثل FROM (SELECT ...) FINAL).
* محركات الجداول التي لا تدعمه (مثل SharedMergeTree).

الحل: طبِّق FINAL فقط على الجدول المصدر داخل الاستعلام الفرعي، أو أزِله إذا كان المحرك لا يدعمه.

```sql theme={null}
/* ORIGINAL QUERY */
SELECT * FROM (SELECT * FROM my_table) AS subquery FINAL ...

/* FIXED QUERY */
SELECT * FROM (SELECT * FROM my_table FINAL) AS subquery ...
```

<h3 id="countdistinct-case-insensitivity">
  عدم التمييز بين حالات الأحرف في الدالة `countDistinct()`
</h3>

الخطأ: `Function with name countdistinct does not exist (UNKNOWN_FUNCTION)`. رمز الاستثناء: 46

السبب: أسماء الدوال تميّز بين حالات الأحرف، أو تُطابَق بشكل صارم ضمن المحلّل. لم يعد `countdistinct` (بحروف صغيرة بالكامل) يُعرَف تلقائيًا.

الحل: استخدم الصيغة القياسية `countDistinct` (camelCase) أو الدالة `uniq` الخاصة بـ ClickHouse.
