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

# لماذا لا يُستخدَم المفتاح الأساسي لديّ؟ وكيف يمكنني التحقّق من ذلك؟

> يتناول سببًا شائعًا لعدم استخدام المفتاح الأساسي في الترتيب وكيفية التأكد من ذلك

<div id="checking-your-primary-key">
  ## التحقق من المفتاح الأساسي
</div>

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

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

لنأخذ الجدول البسيط التالي:

```sql theme={null}
CREATE TABLE logs
(
    `code` LowCardinality(String),
    `timestamp` DateTime64(3)
)
ENGINE = MergeTree
ORDER BY (code, toUnixTimestamp(timestamp))
```

لاحظ كيف يتضمّن مفتاح الترتيب لدينا `toUnixTimestamp(timestamp)` بوصفه العنصر الثاني.

<div id="populate-data">
  ## تعبئة البيانات
</div>

عبّئ هذا الجدول بـ 100 مليون صف:

```sql theme={null}
INSERT INTO logs SELECT
 ['200', '404', '502', '403'][toInt32(randBinomial(4, 0.1)) + 1] AS code,
    now() + toIntervalMinute(number) AS timestamp
FROM numbers(100000000)

0 rows in set. Elapsed: 15.845 sec. Processed 100.00 million rows, 800.00 MB (6.31 million rows/s., 50.49 MB/s.)

SELECT count()
FROM logs

┌───count()─┐
│ 100000000 │ -- 100.00 million
└───────────┘

1 row in set. Elapsed: 0.002 sec.
```

<div id="basic-filtering">
  ## التصفية الأساسية
</div>

إذا قمنا بالتصفية حسب `code`، فسنرى في الناتج عدد الصفوف التي جرى فحصها: `49.15 thousand`. لاحظ أن هذا يمثّل مجموعة فرعية من إجمالي 100m صف.

```sql theme={null}
SELECT count() AS c
FROM logs
WHERE code = '200'

┌────────c─┐
│ 65607542 │ -- 65.61 million
└──────────┘

1 row in set. Elapsed: 0.021 sec. Processed 49.15 thousand rows, 49.17 KB (2.34 million rows/s., 2.34 MB/s.)
Peak memory usage: 92.70 KiB.
```

علاوة على ذلك، يمكننا أيضًا التأكد من استخدام الفهرس عبر البند `EXPLAIN indexes=1`:

```sql theme={null}
EXPLAIN indexes = 1
SELECT count() AS c
FROM logs
WHERE code = '200'

┌─explain────────────────────────────────────────────────────────────┐
│ Expression ((Project names + Projection))                          │
│   AggregatingProjection                                            │
│     Expression (Before GROUP BY)                                   │
│       Filter ((WHERE + Change column names to column identifiers)) │
│         ReadFromMergeTree (default.logs)                           │
│         Indexes:                                                   │
│           PrimaryKey                                               │
│             Keys:                                                  │
│               code                                                 │
│             Condition: (code in ['200', '200'])                    │
│             Parts: 3/3 │
│             Granules: 8012/12209 │
│     ReadFromPreparedSource (_minmax_count_projection)              │
└────────────────────────────────────────────────────────────────────┘
```

لاحظ كيف أن عدد الـ حبيبات التي جرى فحصها، `8012`، لا يمثّل سوى جزء من الإجمالي `12209`. ويؤكد القسم المميز أدناه استخدام شيفرة المفتاح الأساسي.

```bash theme={null}
PrimaryKey
  Keys: 
   code 
```

تُعدّ الحبيبات وحدة معالجة البيانات في ClickHouse، وعادةً ما تحتوي كل واحدة منها على 8192 صفًا. ولمزيد من التفاصيل حول الحبيبات وكيفية ترشيحها، نوصي بقراءة [هذا الدليل](/docs/ar/guides/clickhouse/data-modelling/sparse-primary-indexes#mark-files-are-used-for-locating-granules).

<Note>
  لن تكون التصفية على المفاتيح المتأخرة ضمن مفتاح الترتيب بالكفاءة نفسها التي تكون عليها التصفية على المفاتيح الأسبق في الـ tuple. ولمعرفة السبب، راجع [هنا](/docs/ar/guides/clickhouse/data-modelling/sparse-primary-indexes#secondary-key-columns-can-not-be-inefficient)
</Note>

<div id="multi-key-filtering">
  ## التصفية باستخدام مفاتيح متعددة
</div>

لنفترض أننا نُجري التصفية وفقًا لـ`code` و`timestamp`:

```sql theme={null}
SELECT count()
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')

┌─count()─┐
│  689742 │
└─────────┘

1 row in set. Elapsed: 0.008 sec. Processed 712.70 thousand rows, 6.41 MB (88.92 million rows/s., 799.27 MB/s.)

EXPLAIN indexes = 1
SELECT count()
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')

┌─explain───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Expression ((Project names + Projection))                                                                                                                         │
│   Aggregating                                                                                                                                                     │
│     Expression (Before GROUP BY)                                                                                                                                  │
│       Expression                                                                                                                                                  │
│         ReadFromMergeTree (default.logs)                                                                                                                          │
│         Indexes:                                                                                                                                                  │
│           PrimaryKey                                                                                                                                              │
│             Keys:                                                                                                                                                 │
│               code                                                                                                                                                │
│               toUnixTimestamp(timestamp)                                                                                                                          │
│             Condition: and((toUnixTimestamp(timestamp) in (-Inf, 1767225600]), and((toUnixTimestamp(timestamp) in [1735689600, +Inf)), (code in ['200', '200']))) │
│             Parts: 3/3 │
│             Granules: 87/12209 │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

13 rows in set. Elapsed: 0.002 sec.

```

في هذه الحالة، يُستخدم مفتاحا الترتيب كليهما لتصفية الصفوف، ما يعني الحاجة إلى قراءة `87` حبيبة فقط.

<div id="using-keys-in-sorting">
  ## استخدام المفاتيح في الفرز
</div>

يمكن لـ ClickHouse أيضًا الاستفادة من مفاتيح الترتيب لتنفيذ الفرز بكفاءة. وتحديدًا،

عندما يكون الإعداد [optimize\_read\_in\_order](/docs/ar/reference/statements/select/order-by#optimization-of-data-reading) مُمكّنًا (وهو كذلك افتراضيًا)، يستخدم خادم ClickHouse فهرس الجدول ويقرأ البيانات وفقًا لترتيب مفتاح ORDER BY. يتيح ذلك تجنّب قراءة جميع البيانات عند تحديد LIMIT. لذلك، تُعالَج الاستعلامات على مجموعات البيانات الكبيرة ذات الحدود الصغيرة بسرعة أكبر. راجع [هنا](/docs/ar/reference/statements/select/order-by#optimization-of-data-reading) و[هنا](/docs/ar/resources/support-center/knowledge-base/performance-optimization/async-vs-optimize-read-in-order#what-about-optimize_read_in_order) لمزيد من التفاصيل.

ومع ذلك، يتطلب هذا تطابق المفاتيح المستخدمة.

على سبيل المثال، انظر إلى هذا الاستعلام:

```sql theme={null}
SELECT *
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
ORDER BY timestamp ASC
LIMIT 10

┌─code─┬───────────────timestamp─┐
│ 200 │ 2025-01-01 00:00:01.000 │
│ 200 │ 2025-01-01 00:00:45.000 │
│ 200 │ 2025-01-01 00:01:01.000 │
│ 200 │ 2025-01-01 00:01:45.000 │
│ 200 │ 2025-01-01 00:02:01.000 │
│ 200 │ 2025-01-01 00:03:01.000 │
│ 200 │ 2025-01-01 00:03:45.000 │
│ 200 │ 2025-01-01 00:04:01.000 │
│ 200 │ 2025-01-01 00:05:45.000 │
│ 200 │ 2025-01-01 00:06:01.000 │
└──────┴─────────────────────────

10 rows in set. Elapsed: 0.009 sec. Processed 712.70 thousand rows, 6.41 MB (80.13 million rows/s., 720.27 MB/s.)
Peak memory usage: 125.50 KiB.
```

يمكننا التأكد من عدم الاستفادة من هذا التحسين هنا باستخدام `EXPLAIN pipeline`:

```sql theme={null}
EXPLAIN PIPELINE
SELECT *
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
ORDER BY timestamp ASC
LIMIT 10

┌─explain───────────────────────────────────────────────────────────────────────┐
│ (Expression)                                                                  │
│ ExpressionTransform                                                           │
│   (Limit)                                                                     │
│   Limit │
│     (Sorting)                                                                 │
│     MergingSortedTransform 12 → 1 │
│       MergeSortingTransform × 12 │
│         LimitsCheckingTransform × 12 │
│           PartialSortingTransform × 12 │
│             (Expression)                                                      │
│             ExpressionTransform × 12 │
│               (Expression)                                                    │
│               ExpressionTransform × 12 │
│                 (ReadFromMergeTree)                                           │
│                 MergeTreeSelect(pool: ReadPool, algorithm: Thread) × 12 0 → 1 │
└───────────────────────────────────────────────────────────────────────────────┘

15 rows in set. Elapsed: 0.004 sec.
```

السطر `MergeTreeSelect(pool: ReadPool, algorithm: Thread)` هنا لا يدل على استخدام هذا التحسين، بل على عملية قراءة عادية. ويرجع ذلك إلى أن مفتاح ترتيب الجدول لدينا يستخدم `toUnixTimestamp(Timestamp)` **وليس** `timestamp`. ويؤدي تصحيح هذا الاختلاف إلى حل المشكلة:

```sql theme={null}
EXPLAIN PIPELINE
SELECT *
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
ORDER BY toUnixTimestamp(timestamp) ASC
LIMIT 10

┌─explain──────────────────────────────────────────────────────────────────────────┐
│ (Expression)                                                                     │
│ ExpressionTransform                                                              │
│   (Limit)                                                                        │
│   Limit │
│     (Sorting)                                                                    │
│     MergingSortedTransform 3 → 1 │
│       BufferChunks × 3 │
│         (Expression)                                                             │
│         ExpressionTransform × 3 │
│           (Expression)                                                           │
│           ExpressionTransform × 3 │
│             (ReadFromMergeTree)                                                  │
│             MergeTreeSelect(pool: ReadPoolInOrder, algorithm: InOrder) × 3 0 → 1 │
└──────────────────────────────────────────────────────────────────────────────────┘

13 rows in set. Elapsed: 0.003 sec.
```
