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

> توثيق عبارة WITH

# عبارة WITH

يدعم ClickHouse التعبيرات الجدولية الشائعة ([CTE](https://en.wikipedia.org/wiki/Hierarchical_and_recursive_queries_in_SQL))، والتعبيرات القياسية الشائعة، والاستعلامات التكرارية.

<div id="common-table-expressions">
  ## التعبيرات الجدولية الشائعة
</div>

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

يُستبدل كل مرجع إلى تعبير جدولي شائع في استعلامات `SELECT` دائمًا بالاستعلام الفرعي الوارد في تعريفه إذا لم يكن CTE معرّفًا صراحةً على أنه materialized (راجع [التعبيرات الجدولية الشائعة المُجسَّدة](#materialized-common-table-expressions)).
ويُمنَع التكرار العودي بإخفاء CTE الحالي من عملية حلّ المعرّفات.

يرجى ملاحظة أن CTEs لا تضمن النتائج نفسها في جميع المواضع التي يُشار إليها فيها، لأن الاستعلام يُعاد تنفيذه عند كل استخدام.

<div id="common-table-expressions-syntax">
  ### الصيغة
</div>

```sql theme={null}
WITH <identifier> AS [MATERIALIZED] <subquery expression>
```

<div id="common-table-expressions-example">
  ### مثال
</div>

فيما يلي مثال على حالة يُعاد فيها تنفيذ استعلام فرعي:

```sql theme={null}
WITH cte_numbers AS
(
    SELECT
        num
    FROM generateRandom('num UInt64', NULL)
    LIMIT 1000000
)
SELECT
    count()
FROM cte_numbers
WHERE num IN (SELECT num FROM cte_numbers)
```

إذا كانت CTEs تمرّر النتائج نفسها تمامًا، لا مجرد جزء من الشيفرة، فسترى دائمًا `1000000`

لكن بما أننا نشير إلى `cte_numbers` مرتين، تُولَّد أرقام عشوائية في كل مرة، ولذلك نرى نتائج عشوائية مختلفة مثل `280501, 392454, 261636, 196227` وهكذا...

<div id="materialized-common-table-expressions">
  ## التعبيرات الجدولية الشائعة المُجسَّدة
</div>

بشكل افتراضي، يضمّن ClickHouse الاستعلام الفرعي الخاص بـ CTE في كل موضع يُشار إليه فيه، ويعيد تنفيذه في كل مرة.
تؤدي إضافة الكلمة المفتاحية `MATERIALIZED` إلى توجيه ClickHouse لتنفيذ الاستعلام الفرعي لـ CTE **مرة واحدة فقط**، وتخزين النتائج في جدول مؤقت، ثم خدمة جميع المراجع من ذلك الجدول.
ويكون هذا مفيدًا بشكل خاص عندما يُشار إلى CTE نفسه عدة مرات داخل الاستعلام (على سبيل المثال، في عمليات self-join أو في عدة استعلامات فرعية `IN`)، لأن العملية الحسابية الأساسية لا تحدث إلا مرة واحدة.

<Note>
  تُعد Materialized CTEs ميزة **تجريبية**.
  ويتطلب استخدامها تفعيل [المحلّل](/ar/guides/clickhouse/performance-and-monitoring/analyzer) والإعداد `enable_materialized_cte`.
</Note>

<div id="common-table-expressions-syntax">
  ### الصيغة
</div>

```sql theme={null}
WITH <identifier> AS MATERIALIZED (<subquery>)
SELECT ...
```

<div id="materialized-cte-when-to-use">
  ### متى تستخدم
</div>

تكون Materialized CTEs أكثر فائدة في الحالات التالية:

* عند الإشارة إلى CTE نفسه **أكثر من مرة** ضمن استعلام.
  فبدون `MATERIALIZED`، تُعيد كل إشارة تنفيذ الاستعلام الفرعي بشكل مستقل.
* عندما يحتوي CTE على دوال **غير حتمية** مثل `generateRandom`.
  يضمن materializing أن ترى جميع الإشارات البيانات نفسها.
* عندما يتضمن CTE **حسابات مكلفة** (مثل عمليات aggregation وJOIN وعمليات المسح الكبيرة) ولا ينبغي تكرارها.

<Tip>
  إذا تمت الإشارة إلى materialized CTE مرة واحدة فقط، فإن ClickHouse يضمّنه تلقائيًا داخل استعلام فرعي عادي لتجنّب overhead غير الضروري.
</Tip>

<div id="materialized-common-table-expressions-examples">
  ### أمثلة
</div>

**المثال 1:** ربط ذاتي على CTE مُجسَّدة

من دون `MATERIALIZED`، سينفّذ كل جانب من جانبي الربط الاستعلام الفرعي بشكل مستقل.
مع `MATERIALIZED`، لا يُمسَح الجدول إلا مرة واحدة، ويقرأ جانبا الربط من الجدول المؤقت نفسه.

```sql theme={null}
SET enable_materialized_cte = 1;

CREATE TABLE users (uid Int16, name String, age Int16) ENGINE = Memory;
INSERT INTO users VALUES (1231, 'John', 33), (6666, 'Ksenia', 48), (8888, 'Alice', 50);

WITH
    a AS MATERIALIZED (SELECT * FROM users WHERE name = 'Alice')
SELECT count() FROM a AS l JOIN a AS r ON l.uid = r.uid;
```

```response theme={null}
┌─count()─┐
│       1 │
└─────────┘
```

**مثال 2:** نتائج حتمية مع دوال غير حتمية

تنتج تعبيرات الجدول الشائعة العادية (`CTEs`) مع `generateRandom` نتائج مختلفة في كل موضع تتم فيه الإشارة إليها.
يضمن تجسيد الـ CTE الاتساق:

```sql theme={null}
SET enable_materialized_cte = 1;

WITH cte_numbers AS MATERIALIZED
(
    SELECT num
    FROM generateRandom('num UInt64', NULL)
    LIMIT 1000000
)
SELECT count()
FROM cte_numbers
WHERE num IN (SELECT num FROM cte_numbers);
```

لأن كلا المرجعين يقرآن من البيانات المجسّدة نفسها، تكون النتيجة دائمًا `1000000`.

**مثال 3:** ربط تعابير CTE المجسّدة

يمكن لتعابير CTE المجسّدة أن تُشير إلى تعابير CTE مجسّدة أخرى.
يحلّ ClickHouse التبعيات ويُجسّدها بالترتيب الصحيح:

```sql theme={null}
SET enable_materialized_cte = 1;

WITH
    a AS MATERIALIZED (SELECT uid, name FROM users),
    b AS MATERIALIZED (SELECT uid FROM a)
SELECT count() FROM b AS l LEFT SEMI JOIN b AS r ON l.uid = r.uid;
```

```response theme={null}
┌─count()─┐
│       3 │
└─────────┘
```

لا يهم ترتيب تعريفات CTE — ويُسمح بالإشارة إلى تعريفات تَرِد لاحقًا:

```sql theme={null}
SET enable_materialized_cte = 1;

WITH
    b AS MATERIALIZED (SELECT uid FROM a),
    a AS MATERIALIZED (SELECT uid FROM users)
SELECT count() FROM b AS l LEFT SEMI JOIN b AS r ON l.uid = r.uid;
```

```response theme={null}
┌─count()─┐
│       3 │
└─────────┘
```

<div id="materialized-cte-restrictions">
  ### القيود
</div>

* **يتطلب إعدادًا تجريبيًا**: يجب تمكين الإعداد `enable_materialized_cte`.
* **المحلِّل مطلوب**: لا تعمل عبارات CTE المُجسَّدة إلا عند تمكين [المحلِّل](/ar/guides/clickhouse/performance-and-monitoring/analyzer) (`enable_analyzer = 1`).
* **غير مدعوم مع `RECURSIVE`**: لا يُسمح بالجمع بين الكلمتين المفتاحيتين `MATERIALIZED` و`RECURSIVE`، وينتج عن ذلك استثناء `UNSUPPORTED_METHOD`.
* **عبارات CTE المرتبطة غير مسموح بها**: لا يمكن لعبارة CTE مُجسَّدة أن تُشير إلى أعمدة من نطاقات الاستعلام الخارجية.

<div id="common-scalar-expressions">
  ## التعبيرات القياسية الشائعة
</div>

يتيح لك ClickHouse تعريف أسماء مستعارة لأي تعبيرات قياسية في عبارة `WITH`.
ويمكن الرجوع إلى التعبيرات القياسية الشائعة في أي موضع ضمن الاستعلام.

<Note>
  إذا كان التعبير القياسي المشترك يشير إلى شيء غير قيمة حرفية ثابتة، فقد يؤدي ذلك إلى وجود [متغيرات حرة](https://en.wikipedia.org/wiki/Free_variables_and_bound_variables).
  يحلّ ClickHouse أي معرّف ضمن أقرب نطاق ممكن، ما يعني أن المتغيرات الحرة قد تشير إلى كيانات غير متوقعة في حال تعارض الأسماء، أو قد تؤدي إلى استعلام فرعي مترابط.
  يوصى بتعريف CSE على شكل [دالة لامبدا](/ar/reference/functions/regular-functions/overview#arrow-operator-and-lambda) (وذلك ممكن فقط عند تمكين [المحلّل](/ar/guides/clickhouse/performance-and-monitoring/analyzer))، مع ربط جميع المعرّفات المستخدمة، لتحقيق سلوك أكثر قابلية للتنبؤ عند حلّ معرّفات التعبير.
</Note>

<div id="common-table-expressions-syntax">
  ### الصيغة
</div>

```sql theme={null}
WITH <expression> AS <identifier>
```

<div id="materialized-common-table-expressions-examples">
  ### أمثلة
</div>

**مثال 1:** استخدام تعبير ثابت كـ"متغير"

```sql theme={null}
WITH '2019-08-01 15:23:00' AS ts_upper_bound
SELECT *
FROM hits
WHERE
    EventDate = toDate(ts_upper_bound) AND
    EventTime <= ts_upper_bound;
```

**مثال 2:** استخدام الدوال عالية الرتبة لتقييد المعرّفات

```sql theme={null}
WITH
    '.txt' as extension,
    (id, extension) -> concat(lower(id), extension) AS gen_name
SELECT gen_name('test', '.sql') as file_name;
```

```response theme={null}
   ┌─file_name─┐
1. │ test.sql  │
   └───────────┘
```

**مثال 3:** استخدام الدوال عالية الترتيب مع المتغيرات الحرة

تُظهر أمثلة الاستعلامات التالية أن المعرّفات غير المقيّدة تُفسَّر على أنها كيان ضمن أقرب نطاق.
هنا، لا يكون `extension` مقيّدًا داخل متن دالة لامبدا `gen_name`.
مع أن `extension` مُعرَّف على أنه `'.txt'` كتعبير قياسي شائع ضمن نطاق تعريف `generated_names` واستخدامه، فإنه يُفسَّر على أنه عمود في الجدول `extension_list`، لأنه متاح في الاستعلام الفرعي `generated_names`.

```sql theme={null}
CREATE TABLE extension_list
(
    extension String
)
ORDER BY extension
AS SELECT '.sql';

WITH
    '.txt' as extension,
    generated_names as (
        WITH
            (id) -> concat(lower(id), extension) AS gen_name
        SELECT gen_name('test') as file_name FROM extension_list
    )
SELECT file_name FROM generated_names;
```

```response theme={null}
   ┌─file_name─┐
1. │ test.sql  │
   └───────────┘
```

**مثال 4:** استبعاد نتيجة التعبير sum(bytes) من قائمة أعمدة جملة SELECT

```sql theme={null}
WITH sum(bytes) AS s
SELECT
    formatReadableSize(s),
    table
FROM system.parts
GROUP BY table
ORDER BY s;
```

**مثال 5:** استخدام نتائج استعلام فرعي يُرجِع قيمة واحدة

```sql theme={null}
/* this example would return TOP 10 of most huge tables */
WITH
    (
        SELECT sum(bytes)
        FROM system.parts
        WHERE active
    ) AS total_disk_usage
SELECT
    (sum(bytes) / total_disk_usage) * 100 AS table_disk_usage,
    table
FROM system.parts
GROUP BY table
ORDER BY table_disk_usage DESC
LIMIT 10;
```

**مثال 6:** إعادة استخدام تعبير داخل استعلام فرعي

```sql theme={null}
WITH test1 AS (SELECT i + 1, j + 1 FROM test1)
SELECT * FROM test1;
```

<div id="recursive-queries">
  ## الاستعلامات التكرارية
</div>

يسمح المُعدِّل الاختياري `RECURSIVE` لاستعلام WITH بالإشارة إلى ناتجه الخاص. مثال:

**مثال:** جمع الأعداد الصحيحة من 1 إلى 100

```sql theme={null}
WITH RECURSIVE test_table AS (
    SELECT 1 AS number
UNION ALL
    SELECT number + 1 FROM test_table WHERE number < 100
)
SELECT sum(number) FROM test_table;
```

```text theme={null}
┌─sum(number)─┐
│        5050 │
└─────────────┘
```

<Note>
  تعتمد تعبيرات CTE التكرارية على [محلل الاستعلام](/ar/guides/clickhouse/performance-and-monitoring/analyzer) الذي طُرح في الإصدار **`24.3`**. إذا كنت تستخدم الإصدار **`24.3+`** وواجهت الاستثناء **`(UNKNOWN_TABLE)`** أو **`(UNSUPPORTED_METHOD)`**، فهذا يشير إلى أن المحلل معطّل على المثيل أو الدور أو ملف التعريف لديك. لتفعيل المحلل، فعِّل الإعداد **`allow_experimental_analyzer`** أو حدِّث إعداد **`compatibility`** إلى إصدار أحدث.
  واعتبارًا من الإصدار `24.8`، أصبح المحلل معتمدًا بالكامل للاستخدام في بيئات production، وأُعيدت تسمية الإعداد `allow_experimental_analyzer` إلى `enable_analyzer`.
</Note>

تكون الصيغة العامة لاستعلام `WITH` التكراري دائمًا على النحو التالي: عبارة غير تكرارية، ثم `UNION ALL`، ثم عبارة تكرارية، ولا يجوز إلا للعبارة التكرارية أن تتضمن مرجعًا إلى مخرجات الاستعلام نفسه. ويُنفَّذ استعلام CTE التكراري كما يلي:

1. قيِّم العبارة غير التكرارية. وضع نتيجة استعلام العبارة غير التكرارية في جدول عمل مؤقت.
2. ما دام جدول العمل غير فارغ، كرِّر الخطوات التالية:
   1. قيِّم العبارة التكرارية، مع استبدال المرجع الذاتي التكراري بالمحتويات الحالية لجدول العمل. وضع نتيجة استعلام العبارة التكرارية في جدول وسيط مؤقت.
   2. استبدل محتويات جدول العمل بمحتويات الجدول الوسيط، ثم أفرغ الجدول الوسيط.

تُستخدم الاستعلامات التكرارية عادةً للتعامل مع البيانات الهرمية أو البيانات ذات البنية الشجرية. على سبيل المثال، يمكننا كتابة استعلام ينفِّذ اجتيازًا للشجرة:

**مثال:** اجتياز الشجرة

لننشئ أولًا جدول الشجرة:

```sql theme={null}
DROP TABLE IF EXISTS tree;
CREATE TABLE tree
(
    id UInt64,
    parent_id Nullable(UInt64),
    data String
) ENGINE = MergeTree ORDER BY id;

INSERT INTO tree VALUES (0, NULL, 'ROOT'), (1, 0, 'Child_1'), (2, 0, 'Child_2'), (3, 1, 'Child_1_1');
```

يمكننا اجتياز تلك الشجرة باستخدام الاستعلام التالي:

**مثال:** اجتياز الشجرة

```sql theme={null}
WITH RECURSIVE search_tree AS (
    SELECT id, parent_id, data
    FROM tree t
    WHERE t.id = 0
UNION ALL
    SELECT t.id, t.parent_id, t.data
    FROM tree t, search_tree st
    WHERE t.parent_id = st.id
)
SELECT * FROM search_tree;
```

```text theme={null}
┌─id─┬─parent_id─┬─data──────┐
│  0 │      ᴺᵁᴸᴸ │ ROOT      │
│  1 │         0 │ Child_1   │
│  2 │         0 │ Child_2   │
│  3 │         1 │ Child_1_1 │
└────┴───────────┴───────────┘
```

<div id="search-order">
  ### ترتيب البحث
</div>

لإنشاء ترتيب العمق أولًا، نحسب لكل صف في النتيجة مصفوفةً من الصفوف التي سبق أن زرناها:

**مثال:** اجتياز الشجرة بترتيب العمق أولًا

```sql theme={null}
WITH RECURSIVE search_tree AS (
    SELECT id, parent_id, data, [t.id] AS path
    FROM tree t
    WHERE t.id = 0
UNION ALL
    SELECT t.id, t.parent_id, t.data, arrayConcat(path, [t.id])
    FROM tree t, search_tree st
    WHERE t.parent_id = st.id
)
SELECT * FROM search_tree ORDER BY path;
```

```text theme={null}
┌─id─┬─parent_id─┬─data──────┬─path────┐
│  0 │      ᴺᵁᴸᴸ │ ROOT      │ [0]     │
│  1 │         0 │ Child_1   │ [0,1]   │
│  3 │         1 │ Child_1_1 │ [0,1,3] │
│  2 │         0 │ Child_2   │ [0,2]   │
└────┴───────────┴───────────┴─────────┘
```

لإنشاء ترتيب بالعرض أولًا، تتمثل الطريقة القياسية في إضافة عمود يتتبّع عمق البحث:

**مثال:** ترتيب اجتياز الشجرة بالعرض أولًا

```sql theme={null}
WITH RECURSIVE search_tree AS (
    SELECT id, parent_id, data, [t.id] AS path, toUInt64(0) AS depth
    FROM tree t
    WHERE t.id = 0
UNION ALL
    SELECT t.id, t.parent_id, t.data, arrayConcat(path, [t.id]), depth + 1
    FROM tree t, search_tree st
    WHERE t.parent_id = st.id
)
SELECT * FROM search_tree ORDER BY depth;
```

```text theme={null}
┌─id─┬─link─┬─data──────┬─path────┬─depth─┐
│  0 │ ᴺᵁᴸᴸ │ ROOT      │ [0]     │     0 │
│  1 │    0 │ Child_1   │ [0,1]   │     1 │
│  2 │    0 │ Child_2   │ [0,2]   │     1 │
│  3 │    1 │ Child_1_1 │ [0,1,3] │     2 │
└────┴──────┴───────────┴─────────┴───────┘
```

<div id="cycle-detection">
  ### اكتشاف الحلقات
</div>

لننشئ أولًا جدول graph:

```sql theme={null}
DROP TABLE IF EXISTS graph;
CREATE TABLE graph
(
    from UInt64,
    to UInt64,
    label String
) ENGINE = MergeTree ORDER BY (from, to);

INSERT INTO graph VALUES (1, 2, '1 -> 2'), (1, 3, '1 -> 3'), (2, 3, '2 -> 3'), (1, 4, '1 -> 4'), (4, 5, '4 -> 5');
```

يمكننا اجتياز هذا الرسم البياني باستخدام الاستعلام التالي:

**مثال:** اجتياز الرسم البياني دون اكتشاف الدورات

```sql theme={null}
WITH RECURSIVE search_graph AS (
    SELECT from, to, label FROM graph g
    UNION ALL
    SELECT g.from, g.to, g.label
    FROM graph g, search_graph sg
    WHERE g.from = sg.to
)
SELECT DISTINCT * FROM search_graph ORDER BY from;
```

```text theme={null}
┌─from─┬─to─┬─label──┐
│    1 │  4 │ 1 -> 4 │
│    1 │  2 │ 1 -> 2 │
│    1 │  3 │ 1 -> 3 │
│    2 │  3 │ 2 -> 3 │
│    4 │  5 │ 4 -> 5 │
└──────┴────┴────────┘
```

ولكن إذا أضفنا حلقة إلى ذلك المخطط، فسيفشل الاستعلام السابق مع ظهور الخطأ `Maximum recursive CTE evaluation depth`:

```sql theme={null}
INSERT INTO graph VALUES (5, 1, '5 -> 1');

WITH RECURSIVE search_graph AS (
    SELECT from, to, label FROM graph g
UNION ALL
    SELECT g.from, g.to, g.label
    FROM graph g, search_graph sg
    WHERE g.from = sg.to
)
SELECT DISTINCT * FROM search_graph ORDER BY from;
```

```text theme={null}
Code: 306. DB::Exception: Received from localhost:9000. DB::Exception: Maximum recursive CTE evaluation depth (1000) exceeded, during evaluation of search_graph AS (SELECT from, to, label FROM graph AS g UNION ALL SELECT g.from, g.to, g.label FROM graph AS g, search_graph AS sg WHERE g.from = sg.to). Consider raising max_recursive_cte_evaluation_depth setting.: While executing RecursiveCTESource. (TOO_DEEP_RECURSION)
```

الطريقة القياسية للتعامل مع الدورات هي حساب مصفوفة من العُقد التي تمت زيارتها بالفعل:

**مثال:** اجتياز الرسم البياني مع اكتشاف الدورات

```sql theme={null}
WITH RECURSIVE search_graph AS (
    SELECT from, to, label, false AS is_cycle, [tuple(g.from, g.to)] AS path FROM graph g
UNION ALL
    SELECT g.from, g.to, g.label, has(path, tuple(g.from, g.to)), arrayConcat(sg.path, [tuple(g.from, g.to)])
    FROM graph g, search_graph sg
    WHERE g.from = sg.to AND NOT is_cycle
)
SELECT * FROM search_graph WHERE is_cycle ORDER BY from;
```

```text theme={null}
┌─from─┬─to─┬─label──┬─is_cycle─┬─path──────────────────────┐
│    1 │  4 │ 1 -> 4 │ true     │ [(1,4),(4,5),(5,1),(1,4)] │
│    4 │  5 │ 4 -> 5 │ true     │ [(4,5),(5,1),(1,4),(4,5)] │
│    5 │  1 │ 5 -> 1 │ true     │ [(5,1),(1,4),(4,5),(5,1)] │
└──────┴────┴────────┴──────────┴───────────────────────────┘
```

<div id="infinite-queries">
  ### استعلامات لا نهائية
</div>

يمكن أيضًا استخدام استعلامات CTE التكرارية اللانهائية إذا استُخدم `LIMIT` في الاستعلام الخارجي:

**مثال:** استعلام CTE تكراري لا نهائي

```sql theme={null}
WITH RECURSIVE test_table AS (
    SELECT 1 AS number
UNION ALL
    SELECT number + 1 FROM test_table
)
SELECT sum(number) FROM (SELECT number FROM test_table LIMIT 100);
```

```text theme={null}
┌─sum(number)─┐
│        5050 │
└─────────────┘
```

<div id="trailing-comma">
  ## الفاصلة الختامية
</div>

يُسمح بوضع فاصلة بعد آخر عنصر في جملة `WITH`:

```sql theme={null}
WITH
    (SELECT sum(number) FROM numbers(10)) AS total,
    total * 2 AS doubled,
SELECT total, doubled;
```
