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

# أنشئ أول عرض مادي

> تعرّف على كيفية استخدام العروض المادية في ClickHouse لحساب نتائج الاستعلامات مسبقًا وتخزينها بترتيب فرز مختلف، مما يتيح عمليات بحث سريعة في الأعمدة التي لا يغطيها المفتاح الأساسي.

<a href="/ar/get-started/quickstarts/home"><Badge size="lg" color="gray" icon="arrow-left">جميع أدلة البدء السريع</Badge></a>

<div className="mt-2 flex flex-wrap gap-2">
  <Badge size="lg" color="blue">التحليلات اللحظية</Badge>
  <Badge size="lg" color="blue">مستودعات البيانات</Badge>
  <Badge size="lg" color="blue">قابلية الرصد</Badge>
  <Badge size="lg" color="blue">الذكاء الاصطناعي/تعلّم الآلة</Badge>
  <Badge size="lg" color="orange">Cloud</Badge>
  <Badge size="lg" color="orange">Oss</Badge>
</div>

<div id="prerequisites">
  ## المتطلبات المسبقة
</div>

To successfully follow this guide, you'll need the following:

* A running ClickHouse Cloud service. If you don't have one yet, complete the [Create your first Cloud service](/get-started/quickstarts/create-your-first-service-on-cloud) quickstart first.

يجب أيضًا أن تكون قد أكملت الدليل التمهيدي السريع [إنشاء أول جدول MergeTree](/ar/get-started/quickstarts/create-your-first-mergetree-table)، لأن هذا الدليل يعتمد مباشرةً على جدول `uk_price_paid` الذي تم إنشاؤه هناك.

<div id="what-youll-build">
  ## ما الذي ستبنيه
</div>

في الدليل السريع لـ MergeTree، رأيت أن الاستعلام عن `uk_price_paid` حسب `town` أو `county` يتطلب فحصًا كاملًا للجدول، لأن الجدول مُرتَّب وفق `(postcode, addr1, addr2)`.
في هذا الدليل السريع، ستحل هذه المشكلة بإنشاء **عرض مادي** يخزّن البيانات نفسها مرتبةً وفق `(town, date)`، مما يتيح عمليات بحث سريعة حسب البلدة من دون تغيير جدولك الأصلي.
وبنهاية هذا الدليل، ستفهم كيف تعمل العروض المادية كمشغّلات إدراج، وكيفية تنفيذ backfill للبيانات الموجودة، والمفاضلة في مساحة القرص الناتجة عن تخزين البيانات مرتين.

<Steps titleSize="h3">
  <Step>
    ### افهم سبب حاجتك إلى materialized view

    الجدول `uk_price_paid` لديك مرتّب حسب `(postcode, addr1, addr2)`. وهذا يعني أن ClickHouse يمكنه تخطي كتل كبيرة من البيانات عند التصفية حسب `postcode` أو `addr1` أو `addr2`، لكن الاستعلامات التي تُصفّي حسب `town` يجب أن تفحص كل صف — أي الصفوف الثلاثين مليونًا كلها.

    يمكنك إنشاء جدول ثانٍ باستخدام `ORDER BY` مختلف، لكنك ستحتاج عندها إلى تذكّر الإدراج في كلا الجدولين كلما وصلت بيانات جديدة. تعمل **materialized view** على أتمتة ذلك: فهي تراقب عمليات الإدراج إلى جدول مصدر، وتحوّل الصفوف، ثم تكتبها تلقائيًا إلى جدول الوجهة.

    فكّر في materialized view على أنها **insert trigger** — ففي كل مرة تُدرَج فيها صفوف في الجدول المصدر، يُشغَّل استعلام `SELECT` الخاص بـ MV على الكتلة الجديدة من الصفوف، وتُدرَج النتيجة في جدول الوجهة.
  </Step>

  <Step>
    ### أنشئ جدول الوجهة

    يحتاج العرض المُجسَّد إلى مكان لتخزين مخرجاته. هذا مجرد جدول MergeTree عادي — ولديك تحكّم كامل في مخططه، و`ORDER BY`، و`PARTITION BY`.

    أنشئ جدولًا مُرتَّبًا حسب `(town, date)` ويحتوي فقط على الأعمدة التي تحتاجها للاستعلامات المستندة إلى المدينة:

    ```sql theme={null}
    CREATE TABLE uk_price_paid_by_town
    (
        town       LowCardinality(String),
        date       Date,
        price      UInt32,
        type       Enum8('terraced' = 1, 'semi-detached' = 2, 'detached' = 3, 'flat' = 4, 'other' = 0)
    )
    ENGINE = MergeTree
    PARTITION BY toYYYYMM(date)
    ORDER BY (town, date);
    ```

    لا يوجد ما يميّز هذا الجدول - فهو جدول MergeTree عادي. أما العرض المُجسَّد الذي ستنشئه بعد ذلك، فسيقوم ببساطة بتوجيه البيانات إليه.

    تحقّق من إنشاء الجدول:

    ```sql theme={null}
    SHOW CREATE TABLE uk_price_paid_by_town;
    ```
  </Step>

  <Step>
    ### إنشاء العرض المادي

    أنشئ الآن العرض المادي الذي يربط بين جدول المصدر (`uk_price_paid`) وجدول الوجهة (`uk_price_paid_by_town`):

    ```sql theme={null}
    CREATE MATERIALIZED VIEW uk_price_paid_by_town_mv
    TO uk_price_paid_by_town
    AS SELECT
        town,
        date,
        price,
        type
    FROM uk_price_paid;
    ```

    يوضّح بند `TO uk_price_paid_by_town` لـ ClickHouse أن يكتب ناتج `SELECT` في جدول الوجهة. ومن الآن فصاعدًا، في كل مرة تُدرَج فيها rows في `uk_price_paid`، يُفعَّل هذا MV ويُدرِج الـ rows المحوَّلة في `uk_price_paid_by_town`.

    هناك تحذير مهم: لا تُفعَّل materialized views إلا عند **عمليات الإدراج**. إذا حذفت rows أو حدّثتها في source table، فلن يكون لدى destination table أي علم بذلك - لأن MVs لا تبقى متزامنة مع عمليات الحذف أو التحديث. وإذا كنت بحاجة إلى هذا النوع من المزامنة، ففكّر في استخدام [الإسقاطات](/ar/reference/statements/alter/projection) بدلًا من ذلك.
  </Step>

  <Step>
    ### استكمال البيانات الحالية

    يعالج العرض المادي فقط عمليات الإدراج *اللاحقة*. وقد أُدرجت بالفعل 30 مليون صف في `uk_price_paid` قبل إنشاء MV، لذا فإن الجدول الهدف فارغ حاليًا.

    قم باستكمالها يدويًا:

    ```sql theme={null}
    INSERT INTO uk_price_paid_by_town
    SELECT
        town,
        date,
        price,
        type
    FROM uk_price_paid;
    ```

    يُدرِج هذا البيانات مباشرةً في الجدول الوجهة - ولا يكون MV ضمن هذه الخطوة. بعد اكتمال ذلك، تحقّق من تطابق أعداد الصفوف:

    ```sql theme={null}
    SELECT
        'uk_price_paid' AS table,
        count() AS rows
    FROM uk_price_paid
    UNION ALL
    SELECT
        'uk_price_paid_by_town' AS table,
        count() AS rows
    FROM uk_price_paid_by_town;
    ```

    يجب أن يحتوي الجدولان على العدد نفسه من الصفوف.
  </Step>

  <Step>
    ### الاستعلام عن جدول وجهة العرض المُجسَّد

    نفّذ الآن استعلامًا يرشّح حسب `town` على جدول الوجهة، ثم قارنه بالاستعلام على الجدول المصدر مباشرةً.

    أولًا، استعلم عن الجدول المصدر:

    ```sql theme={null}
    SELECT
        toYear(date) AS year,
        round(avg(price)) AS avg_price,
        count() AS sales
    FROM uk_price_paid
    WHERE town = 'LONDON'
    GROUP BY year
    ORDER BY year DESC;
    ```

    تحقّق من إحصاءات الاستعلام — تُقرأ جميع الصفوف الثلاثين مليونًا لأن `town` ليس ضمن `ORDER BY` للجدول المصدر.

    الآن شغّل الاستعلام نفسه على الجدول الوجهة للعرض المادي:

    ```sql theme={null}
    SELECT
        toYear(date) AS year,
        round(avg(price)) AS avg_price,
        count() AS sales
    FROM uk_price_paid_by_town
    WHERE town = 'LONDON'
    GROUP BY year
    ORDER BY year DESC;
    ```

    تحقّق من إحصاءات الاستعلام مرة أخرى - سيُقرأ عدد أقل بكثير من الصفوف لأن جدول الوجهة مرتّب حسب `(town, date)`، ويمكن لـ ClickHouse تجاوز كل البيانات التي لا تطابق `LONDON`.

    شغّل `SHOW TABLES` لمعرفة ما تم إنشاؤه:

    ```sql theme={null}
    SHOW TABLES;
    ```

    سترى كلاً من `uk_price_paid_by_town` (جدول الوجهة) و`uk_price_paid_by_town_mv` (العرض). وبما أنك استخدمت `CREATE MATERIALIZED VIEW ... TO`، فأنت تتحكم في اسم جدول الوجهة. وإذا حذفت عبارة `TO`، فسينشئ ClickHouse جدول وجهة باسم ضمني (`.inner.xxx`) يصعب التعامل معه مباشرةً.
    لذلك، يُنصح بإنشاء العروض المادية باستخدام عبارة `TO`.
  </Step>

  <Step>
    ### لاحظ أن البيانات تُخزَّن مرتين

    تمنحك العروض المادية سرعة قراءة أعلى مقابل استهلاك مساحة إضافية على القرص. نفِّذ استعلامًا على `system.parts` لمعرفة مقدار المساحة التي يستخدمها كل جدول:

    ```sql theme={null}
    SELECT
        table,
        count() AS parts,
        sum(rows) AS total_rows,
        formatReadableSize(sum(bytes_on_disk)) AS compressed_size
    FROM system.parts
    WHERE table IN ('uk_price_paid', 'uk_price_paid_by_town')
      AND active = true
    GROUP BY table;
    ```

    تُخزَّن البيانات فعليًا مرتين: مرة في `uk_price_paid` مرتبة حسب `(postcode, addr1, addr2)`، ومرة في `uk_price_paid_by_town` مرتبة حسب `(town, date)`. وهذه هي المفاضلة الأساسية: تستخدم مساحة أكبر على القرص مقابل عمليات قراءة أسرع لأنماط وصول مختلفة.

    قد يكون الجدول الوجهة أصغر حجمًا على القرص لأنه يحتوي على أعمدة أقل، وقد يُضغط ترتيب الفرز `(town, date)` بطريقة مختلفة عن الترتيب الأصلي.
  </Step>
</Steps>

## الخطوات التالية

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

اطّلع بعد ذلك على أدلة البدء السريع التالية:

* [أنشئ أول إسقاط لك](/ar/get-started/quickstarts/create-your-first-projection)

أو تعمّق أكثر من خلال الوثائق المرجعية:

* [مرجع العرض المادي](/ar/reference/statements/create/view#materialized-view)
* [العروض المادية التزايدية](/ar/concepts/features/materialized-views/incremental-materialized-view)
* [الإسقاطات](/ar/reference/statements/alter/projection)

<Frame caption="Check out the ClickHouse academy for on-demand and live training">
  <a href="https://learn.clickhouse.com/" target="_blank">
    <img src="https://mintcdn.com/private-7c7dfe99-mintlify-a804b3ad/OpSXl2-4UDSGTfjT/images/academy.png?fit=max&auto=format&n=OpSXl2-4UDSGTfjT&q=85&s=dff7e9cc7b331bad3d0adf2af08b8688" alt="ClickHouse Academy — Master ClickHouse with expert-designed training for every skill level" width="560" noZoom data-path="images/academy.png" />
  </a>
</Frame>

<div className="mt-8">
  <a href="/ar/get-started/quickstarts/home"><Badge size="lg" color="gray" icon="arrow-left">جميع أدلة البدء السريع</Badge></a>
</div>
