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

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

# المحلّل

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

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

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

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

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

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

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

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

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

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

تحدث المشكلة نفسها في هذا الاستعلام. يُستخدَم العمود `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` بالكامل، بما يطابق السلوك القديم.

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

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

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

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

```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;
```

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

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

لا يمكن افتراضيًا استخدام اسم مستعار من قائمة `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`، فسيفشل الاستعلام مع ظهور خطأ.

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

في المُحلِّل، يؤدي استخدام `*` في استعلام `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` بدلًا من استخدام `*`.

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

في المحلّل، جرى توحيد قواعد تحديد النوع الأعلى المشترك للأعمدة المحددة في بند `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`.

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

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

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

   ┌─x─┬─plus(plus(1, 1), 1)─┐
1. │ 2 │                   3 │
   └───┴─────────────────────┘

SELECT
    1 + 1 AS x,
    x + 1
SETTINGS enable_analyzer = 1
FORMAT PrettyCompact

   ┌─x─┬─plus(x, 1)─┐
1. │ 2 │          3 │
   └───┴────────────┘
```

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

في المحلّل، يحدث استنتاج الأنواع أثناء تحليل الاستعلام الأوّلي.
ويعني هذا التغيير أن عمليات التحقق من الأنواع تُجرى قبل التقييم المختصر؛ لذلك، يجب أن تمتلك وسائط الدالة `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'))
```

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

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

<div id="mutations-are-interpreted-by-previous-analyzer">
  ### تُفسَّر عمليات التعديل بواسطة المحلّل السابق
</div>

لا تزال عمليات التعديل تستخدم المحلّل القديم.
وهذا يعني أنه لا يمكن استخدام بعض ميزات ClickHouse SQL الجديدة في عمليات التعديل، مثل عبارة `QUALIFY`.
يمكن الاطلاع على الحالة [هنا](https://github.com/ClickHouse/ClickHouse/issues/61563).

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

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

* فهرس Annoy.
* فهرس Hypothesis. العمل جارٍ عليه [هنا](https://github.com/ClickHouse/ClickHouse/pull/48381).
* ميزة Window View غير مدعومة. ولا توجد خطط لدعمها مستقبلًا.

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

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

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

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
    enable_analyzer=1,
    analyzer_compatibility_join_using_top_level_identifier=1
```

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

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

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

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

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

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

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

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

الخطأ: `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
```

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

الخطأ: `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;
```

<div id="duplicate-cte-names">
  ### أسماء تعبيرات الجدول الشائعة المكررة
</div>

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

السبب: كان المُحلِّل القديم يسمح بتعريف عدة تعبيرات جدول شائعة (WITH ...) بالاسم نفسه، بحيث يطغى التعريف اللاحق على السابق. ويمنع المُحلِّل هذا الالتباس.

الحل: أعد تسمية تعبيرات الجدول الشائعة المكررة لتكون فريدة.

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

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

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

الخطأ: `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...
```

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

الخطأ: `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 ...
```

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

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

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

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