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

# الاستعلامات المتقدمة

<div id="querycontexts">
  ## QueryContexts
</div>

ينفّذ ClickHouse Connect الاستعلامات القياسية ضمن `QueryContext`. يحتوي `QueryContext` على البُنى الأساسية المستخدمة لبناء الاستعلامات على قاعدة بيانات ClickHouse، بالإضافة إلى التهيئة المستخدمة لمعالجة النتيجة وتحويلها إلى `QueryResult` أو أي بنية بيانات أخرى خاصة بالاستجابة. ويشمل ذلك الاستعلام نفسه، والمعلمات، والإعدادات، وتنسيقات القراءة، وخصائص أخرى.

يمكن الحصول على `QueryContext` باستخدام الطريقة `create_query_context` الخاصة بالعميل. تأخذ هذه الطريقة المعلمات نفسها التي تأخذها طريقة الاستعلام الأساسية. ويمكن بعد ذلك تمرير سياق الاستعلام هذا إلى الطرائق `query` أو `query_df` أو `query_np` بوصفه وسيط الكلمة المفتاحية `context` بدلًا من أي من الوسائط الأخرى الخاصة بتلك الطرائق أو جميعها. لاحظ أن أي وسائط إضافية تُحدَّد عند استدعاء الطريقة ستتجاوز أي خصائص موجودة في QueryContext.

أوضح حالة استخدام لـ `QueryContext` هي إرسال الاستعلام نفسه مع قيم مختلفة لمعاملات الربط. يمكن تحديث جميع قيم المعلمات باستدعاء الطريقة `QueryContext.set_parameters` مع قاموس، كما يمكن تحديث أي قيمة مفردة باستدعاء `QueryContext.set_parameter` مع زوج `key` و`value` المطلوب.

```python theme={null}
client.create_query_context(query='SELECT value1, value2 FROM data_table WHERE key = {k:Int32}',
                            parameters={'k': 2},
                            column_oriented=True)
result = client.query(context=qc)
assert result.result_set[1][0] == 'second_value2'
qc.set_parameter('k', 1)
result = test_client.query(context=qc)
assert result.result_set[1][0] == 'first_value2'
```

لاحظ أن كائنات `QueryContext` ليست آمنة للاستخدام المتزامن بين الخيوط، لكن يمكن الحصول على نسخة منها في بيئة متعددة الخيوط عن طريق استدعاء الطريقة `QueryContext.updated_copy`.

<div id="streaming-queries">
  ## الاستعلامات المتدفقة
</div>

يوفّر ClickHouse Connect Client عدة طرائق لاسترجاع البيانات كتدفّق (مُنفَّذ على شكل مولّد في بايثون):

* `query_column_block_stream` -- يعيد بيانات الاستعلام على شكل كتل، كسلسلة من الأعمدة، باستخدام كائنات بايثون الأصلية
* `query_row_block_stream` -- يعيد بيانات الاستعلام على شكل كتلة من الصفوف باستخدام كائنات بايثون الأصلية
* `query_rows_stream` -- يعيد بيانات الاستعلام على شكل سلسلة من الصفوف باستخدام كائنات بايثون الأصلية
* `query_np_stream` -- يعيد كل كتلة من بيانات الاستعلام في ClickHouse على هيئة مصفوفة NumPy
* `query_df_stream` -- يعيد كل كتلة من بيانات الاستعلام في ClickHouse على هيئة Pandas DataFrame
* `query_arrow_stream` -- يعيد بيانات الاستعلام على هيئة PyArrow RecordBlocks
* `query_df_arrow_stream` -- يعيد كل كتلة من بيانات الاستعلام في ClickHouse على هيئة Pandas DataFrame مدعوم بـ Arrow أو Polars DataFrame، وفقًا للوسيطة المسمّاة `dataframe_library` (والقيمة الافتراضية هي "pandas").

تعيد كل واحدة من هذه الطرائق كائن `ContextStream`، ويجب فتحه باستخدام عبارة `with` لبدء استهلاك التدفق.

<div id="data-blocks">
  ### كتل البيانات
</div>

يعالج ClickHouse Connect جميع البيانات من طريقة `query` الأساسية كتدفق من الكتل المستلَمة من خادم ClickHouse. وتُنقل هذه الكتل من ClickHouse وإليه باستخدام التنسيق المخصص "Native". والـ"كتلة" هي ببساطة تسلسل من أعمدة البيانات الثنائية، بحيث يحتوي كل عمود على عدد متساوٍ من قيم البيانات من نوع البيانات المحدد. (وباعتباره قاعدة بيانات عمودية، يخزّن ClickHouse هذه البيانات بصيغة مشابهة.) ويتحكم في حجم الكتلة المُعادة من الاستعلام إعدادان للمستخدم يمكن تعيينهما على عدة مستويات (ملف تعريف المستخدم، أو المستخدم، أو الجلسة، أو الاستعلام). وهما:

* [max\_block\_size](/docs/ar/reference/settings/session-settings#max_block_size) -- حد حجم الكتلة بالصفوف. القيمة الافتراضية 65536.
* [preferred\_block\_size\_bytes](/docs/ar/reference/settings/session-settings#preferred_block_size_bytes) -- حد مرن لحجم الكتلة بالبايت. القيمة الافتراضية 1,000,0000.

وبصرف النظر عن `preferred_block_size_setting`، لن تتجاوز أي كتلة أبدًا `max_block_size` صفًا. واعتمادًا على نوع الاستعلام، قد تكون الكتل الفعلية المُعادة بأي حجم. على سبيل المثال، قد تتضمن الاستعلامات على جدول موزع يغطي عدة أجزاء كتلًا أصغر تُسترجع مباشرةً من كل جزء.

عند استخدام إحدى طرق Client ‏`query_*_stream`، تُعاد النتائج على أساس كتلة تلو الأخرى. ولا يحمّل ClickHouse Connect سوى كتلة واحدة في كل مرة. ويتيح ذلك معالجة كميات كبيرة من البيانات من دون الحاجة إلى تحميل مجموعة نتائج كبيرة بالكامل إلى الذاكرة. لاحظ أنه ينبغي أن يكون التطبيق مستعدًا لمعالجة أي عدد من الكتل، ولا يمكن التحكم في الحجم الدقيق لكل كتلة.

<div id="http-data-buffer-for-slow-processing">
  ### مخزن بيانات HTTP المؤقت عند بطء المعالجة
</div>

نظرًا للقيود المفروضة في بروتوكول HTTP، إذا كانت الكتل تُعالَج بمعدل أبطأ بكثير من معدل تدفق البيانات من خادم ClickHouse، فسيغلق خادم ClickHouse الاتصال، ما يؤدي إلى إطلاق استثناء في خيط المعالجة. ويمكن التخفيف من ذلك جزئيًا بزيادة حجم المخزن المؤقت لتدفق HTTP (والذي يبلغ 10 ميغابايت افتراضيًا) باستخدام الإعداد العام `http_buffer_size`. ولا بأس باستخدام قيم كبيرة لـ `http_buffer_size` في هذه الحالة إذا كانت هناك ذاكرة كافية متاحة للتطبيق. وتُخزَّن البيانات في المخزن المؤقت بصيغة مضغوطة عند استخدام ضغط `lz4` أو `zstd`، لذا فإن استخدام نوعَي الضغط هذين يزيد إجمالي حجم المخزن المؤقت المتاح.

<div id="streamcontexts">
  ### StreamContexts
</div>

تعيد كل واحدة من طرائق `query_*_stream` (مثل `query_row_block_stream`) كائن `StreamContext` من ClickHouse، وهو كائن يجمع في بايثون بين السياق والمولِّد. وهذا هو الاستخدام الأساسي:

```python theme={null}
with client.query_row_block_stream('SELECT pickup, dropoff, pickup_longitude, pickup_latitude FROM taxi_trips') as stream:
    for block in stream:
        for row in block:
            <do something with each row of Python trip data>
```

لاحظ أن محاولة استخدام `StreamContext` من دون عبارة `with` ستؤدي إلى حدوث خطأ. ويضمن استخدام مدير السياق في بايثون إغلاق التدفق (في هذه الحالة، استجابة HTTP متدفقة) إغلاقًا صحيحًا حتى إذا لم تُستهلك جميع البيانات و/أو حدث استثناء أثناء المعالجة. كذلك، لا يمكن استخدام `StreamContext` لاستهلاك التدفق إلا مرة واحدة. وستؤدي محاولة استخدام `StreamContext` بعد انتهاء سياقه إلى ظهور `StreamClosedError`.

يمكنك استخدام الخاصية `source` في `StreamContext` للوصول إلى الكائن الأصل `QueryResult`، الذي يتضمن أسماء الأعمدة والأنواع.

<div id="stream-types">
  ### أنواع التدفق
</div>

تعيد الطريقة `query_column_block_stream` الكتلة كتسلسل من بيانات الأعمدة المخزّنة كأنواع بيانات بايثون الأصلية. وباستخدام استعلامات `taxi_trips` المذكورة أعلاه، ستكون البيانات المعادة قائمةً يكون كل عنصر فيها قائمةً أخرى (أو tuple) تحتوي على جميع البيانات الخاصة بالعمود المقابل. لذا سيكون `block[0]` عبارة عن tuple لا يحتوي إلا على سلاسل نصية. وتُستخدم التنسيقات الموجّهة نحو الأعمدة غالبًا لإجراء عمليات تجميع على جميع القيم في عمود معيّن، مثل جمع إجمالي الأجرة.

تعيد الطريقة `query_row_block_stream` الكتلة كتسلسل من الصفوف، كما في قواعد البيانات العلائقية التقليدية. وبالنسبة إلى رحلات سيارات الأجرة، ستكون البيانات المعادة قائمةً يكون كل عنصر فيها قائمةً أخرى تمثّل صفًا من البيانات. لذا فإن `block[0]` سيحتوي على جميع الحقول (بالترتيب) الخاصة بأول رحلة سيارة أجرة، بينما سيحتوي `block[1]` على صف يضم جميع الحقول الخاصة برحلة سيارة الأجرة الثانية، وهكذا. وتُستخدم النتائج الموجّهة نحو الصفوف عادةً في العرض أو عمليات التحويل.

تُعد `query_row_stream` طريقةً ميسّرة تنتقل تلقائيًا إلى الكتلة التالية عند التكرار عبر التدفق. وفيما عدا ذلك، فهي مطابقة تمامًا لـ `query_row_block_stream`.

تعيد الطريقة `query_np_stream` كل كتلة على شكل مصفوفة NumPy ثنائية الأبعاد. وداخليًا، تُخزَّن مصفوفات NumPy (عادةً) على هيئة أعمدة، لذلك لا حاجة إلى طرق منفصلة للصفوف أو الأعمدة. وسيُعبَّر عن "shape" لمصفوفة NumPy بالشكل (الأعمدة، الصفوف). وتوفّر مكتبة NumPy العديد من الطرق لمعالجة مصفوفات NumPy. لاحظ أنه إذا كانت جميع الأعمدة في الاستعلام تشترك في نفس `dtype` الخاص بـ NumPy، فستحتوي مصفوفة NumPy المعادة أيضًا على `dtype` واحد فقط، ويمكن إعادة تشكيلها/تدويرها من دون تغيير بنيتها الداخلية فعليًا.

تعيد الطريقة `query_df_stream` كل كتلة ClickHouse على شكل Pandas DataFrame ثنائي الأبعاد. إليك مثالًا يوضّح أن الكائن `StreamContext` يمكن استخدامه كسياق بأسلوب مؤجَّل (ولكن مرة واحدة فقط).

```python theme={null}
df_stream = client.query_df_stream('SELECT * FROM hits')
column_names = df_stream.source.column_names
with df_stream:
    for df in df_stream:
        <do something with the pandas DataFrame>
```

تُرجع الطريقة `query_df_arrow_stream` كل كتلة في ClickHouse على هيئة DataFrame باستخدام الواجهة الخلفية لأنواع البيانات في PyArrow. تدعم هذه الطريقة كلاً من DataFrames في Pandas ‏(2.x أو أحدث) وPolars عبر المعامل `dataframe_library` (وقيمته الافتراضية `"pandas"`). وفي كل تكرار، تُنتج DataFrame محوَّلاً من دفعات سجلات PyArrow، مما يوفّر أداءً أفضل وكفاءة أعلى في استخدام الذاكرة لبعض أنواع البيانات.

أخيرًا، تُرجع الطريقة `query_arrow_stream` نتيجة ClickHouse منسَّقة بصيغة `ArrowStream` على هيئة `pyarrow.ipc.RecordBatchStreamReader` ومغلَّفة داخل `StreamContext`. وفي كل تكرار من التدفق، يتم إرجاع PyArrow RecordBlock.

<div id="streaming-examples">
  ### أمثلة على التدفق
</div>

<div id="stream-rows">
  #### تدفق الصفوف
</div>

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Stream large result sets row by row
with client.query_rows_stream("SELECT number, number * 2 as doubled FROM system.numbers LIMIT 100000") as stream:
    for row in stream:
        print(row)  # Process each row
        # Output:
        # (0, 0)
        # (1, 2)
        # (2, 4)
        # ....
```

<div id="stream-row-blocks">
  #### تمرير كتل الصفوف
</div>

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Stream in blocks of rows (more efficient than row-by-row)
with client.query_row_block_stream("SELECT number, number * 2 FROM system.numbers LIMIT 100000") as stream:
    for block in stream:
        print(f"Received block with {len(block)} rows")
        # Output:
        # Received block with 65409 rows
        # Received block with 34591 rows
```

<div id="stream-pandas-dataframes">
  #### تدفق Pandas DataFrames
</div>

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Stream query results as Pandas DataFrames
with client.query_df_stream("SELECT number, toString(number) AS str FROM system.numbers LIMIT 100000") as stream:
    for df in stream:
        # Process each DataFrame block
        print(f"Received DataFrame with {len(df)} rows")
        print(df.head(3))
        # Output:
        # Received DataFrame with 65409 rows
        #    number str
        # 0       0   0
        # 1       1   1
        # 2       2   2
        # Received DataFrame with 34591 rows
        #    number    str
        # 0   65409  65409
        # 1   65410  65410
        # 2   65411  65411
```

<div id="stream-arrow-batches">
  #### تدفق دفعات Arrow
</div>

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Stream query results as Arrow record batches
with client.query_arrow_stream("SELECT * FROM large_table") as stream:
    for arrow_batch in stream:
        # Process each Arrow batch
        print(f"Received Arrow batch with {arrow_batch.num_rows} rows")
        # Output:
        # Received Arrow batch with 65409 rows
        # Received Arrow batch with 34591 rows
```

<div id="numpy-pandas-and-arrow-queries">
  ## استعلامات NumPy وPandas وArrow
</div>

يوفّر ClickHouse Connect طريقة استعلام متخصصة للعمل مع بُنى بيانات NumPy وPandas وArrow. تتيح لك هذه الطريقة استرداد نتائج الاستعلام مباشرةً بهذه التنسيقات الشائعة للبيانات من دون تحويل يدوي.

<div id="numpy-queries">
  ### استعلامات NumPy
</div>

تُرجِع الطريقة `query_np` نتائج الاستعلام على شكل مصفوفة NumPy بدلًا من كائن `QueryResult` من ClickHouse Connect.

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Query returns a NumPy array
np_array = client.query_np("SELECT number, number * 2 AS doubled FROM system.numbers LIMIT 5")

print(type(np_array))
# Output:
# <class "numpy.ndarray">

print(np_array)
# Output:
# [[0 0]
#  [1 2]
#  [2 4]
#  [3 6]
#  [4 8]]
```

<div id="pandas-queries">
  ### استعلامات Pandas
</div>

تعيد الطريقة `query_df` نتائج الاستعلام في صورة Pandas DataFrame بدلًا من `QueryResult` من ClickHouse Connect.

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Query returns a Pandas DataFrame
df = client.query_df("SELECT number, number * 2 AS doubled FROM system.numbers LIMIT 5")

print(type(df))
# Output: <class "pandas.core.frame.DataFrame">
print(df)
# Output:
#    number  doubled
# 0       0        0
# 1       1        2
# 2       2        4
# 3       3        6
# 4       4        8
```

<div id="pyarrow-queries">
  ### استعلامات PyArrow
</div>

تعيد الطريقة `query_arrow` نتائج الاستعلام على شكل PyArrow Table. وهي تستخدم تنسيق `Arrow` في ClickHouse مباشرةً، لذا فهي لا تقبل سوى ثلاث وسيطات مشتركة مع الطريقة الرئيسية `query`: `query` و`parameters` و`settings`. بالإضافة إلى ذلك، توجد وسيطة إضافية هي `use_strings`، وتحدد ما إذا كان Arrow Table سيعرض أنواع String في ClickHouse كسلاسل نصية (إذا كانت True) أو كبايتات (إذا كانت False).

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Query returns a PyArrow Table
arrow_table = client.query_arrow("SELECT number, toString(number) AS str FROM system.numbers LIMIT 3")

print(type(arrow_table))
# Output:
# <class "pyarrow.lib.Table">

print(arrow_table)
# Output:
# pyarrow.Table
# number: uint64 not null
# str: string not null
# ----
# number: [[0,1,2]]
# str: [["0","1","2"]]
```

<div id="arrow-backed-dataframes">
  ### DataFrames المستندة إلى Arrow
</div>

يدعم ClickHouse Connect إنشاء DataFrame بسرعة وكفاءة في استخدام الذاكرة من نتائج Arrow عبر الطريقتين `query_df_arrow` و`query_df_arrow_stream`. وهاتان الطريقتان مجرد غلافين خفيفين حول طريقتي استعلام Arrow، وتُجريان تحويلات إلى DataFrames من دون نسخ حيثما أمكن:

* `query_df_arrow`: ينفّذ الاستعلام باستخدام تنسيق الإخراج `Arrow` في ClickHouse ويُرجع DataFrame.
  * بالنسبة إلى `dataframe_library='pandas'`، يُرجع DataFrame من pandas 2.x باستخدام أنواع بيانات مستندة إلى Arrow (`pd.ArrowDtype`). ويتطلب ذلك pandas 2.x، ويستفيد من المخازن المؤقتة من دون نسخ حيثما أمكن لتحقيق أداء ممتاز وعبء منخفض على الذاكرة.
  * بالنسبة إلى `dataframe_library='polars'`، يُرجع DataFrame من Polars مُنشأ من جدول Arrow (`pl.from_arrow`)، وهو بالكفاءة نفسها تقريبًا، ويمكن أن يكون من دون نسخ بحسب البيانات.
* `query_df_arrow_stream`: يبث النتائج كتسلسل من DataFrames ‏(pandas 2.x أو Polars) مُحوَّلة من دفعات تدفق Arrow.

<div id="query-to-arrow-backed-dataframe">
  #### استعلام إلى DataFrame مستند إلى Arrow
</div>

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Query returns a Pandas DataFrame with Arrow dtypes (requires pandas 2.x)
df = client.query_df_arrow(
    "SELECT number, toString(number) AS str FROM system.numbers LIMIT 3",
    dataframe_library="pandas"
)

print(df.dtypes)
# Output:
# number    uint64[pyarrow]
# str       string[pyarrow]
# dtype: object

# Or use Polars
polars_df = client.query_df_arrow(
    "SELECT number, toString(number) AS str FROM system.numbers LIMIT 3",
    dataframe_library="polars"
)
print(df.dtypes)
# Output:
# [UInt64, String]

# Streaming into batches of DataFrames (polars shown)
with client.query_df_arrow_stream(
    "SELECT number, toString(number) AS str FROM system.numbers LIMIT 100000", dataframe_library="polars"
) as stream:
    for df_batch in stream:
        print(f"Received {type(df_batch)} batch with {len(df_batch)} rows and dtypes: {df_batch.dtypes}")
        # Output:
        # Received <class 'polars.dataframe.frame.DataFrame'> batch with 65409 rows and dtypes: [UInt64, String]
        # Received <class 'polars.dataframe.frame.DataFrame'> batch with 34591 rows and dtypes: [UInt64, String]
```

<div id="notes-and-caveats">
  #### ملاحظات ومحاذير
</div>

* تعيين أنواع Arrow: عند إرجاع البيانات بتنسيق Arrow، يطابق ClickHouse الأنواع مع أقرب أنواع Arrow مدعومة. بعض أنواع ClickHouse لا تملك مقابلًا أصليًا في Arrow، لذا تُعاد كبايتات خام في حقول Arrow (عادةً `BINARY` أو `FIXED_SIZE_BINARY`).
  * أمثلة: يُمثَّل `IPv4` كـ Arrow `UINT32`؛ أما `IPv6` والأعداد الصحيحة الكبيرة (`Int128/UInt128/Int256/UInt256`) فغالبًا ما تُمثَّل كـ `FIXED_SIZE_BINARY`/`BINARY` مع بايتات خام.
  * في هذه الحالات، سيحتوي عمود DataFrame على قيم بايتية مستندة إلى حقل Arrow؛ وتقع على عاتق شيفرة العميل مسؤولية تفسير هذه البايتات أو تحويلها وفقًا لدلالات ClickHouse.
* لا تُخرَج أنواع بيانات Arrow غير المدعومة (مثل UUID/ENUM كأنواع Arrow فعلية)؛ وبدلًا من ذلك، تُمثَّل القيم باستخدام أقرب نوع Arrow مدعوم (وغالبًا كبايتات ثنائية) في المخرجات.
* متطلب Pandas: تتطلب `dtypes` المستندة إلى Arrow إصدار pandas 2.x. بالنسبة إلى إصدارات pandas الأقدم، استخدم `query_df` (غير Arrow) بدلًا من ذلك.
* Strings مقابل الثنائي: يتحكم الخيار `use_strings` (عندما يكون مدعومًا بواسطة إعداد الخادم `output_format_arrow_string_as_string`) في ما إذا كانت الأعمدة من نوع ClickHouse `String` ستُعاد كسلاسل Arrow أو كبيانات ثنائية.

<div id="mismatched-clickhousearrow-type-conversion-examples">
  #### أمثلة على تحويل الأنواع غير المتطابقة بين ClickHouse وArrow
</div>

عندما يعيد ClickHouse الأعمدة على هيئة بيانات ثنائية خام (مثل `FIXED_SIZE_BINARY` أو `BINARY`)، تكون مسؤولية شيفرة التطبيق تحويل هذه البايتات إلى أنواع بايثون المناسبة. توضّح الأمثلة أدناه أن بعض التحويلات يمكن تنفيذها باستخدام واجهات برمجة تطبيقات مكتبة DataFrame، بينما قد يتطلب بعضها الآخر أساليب pure Python مثل `struct.unpack` (وهي تضحي بالأداء لكنها تحافظ على المرونة).

قد تصل أعمدة `Date` بصيغة `UINT16` (عدد الأيام منذ حقبة Unix، 1970‑01‑01). ويكون التحويل داخل DataFrame فعّالًا ومباشرًا:

```python theme={null}
# Polars
df = df.with_columns(pl.col("event_date").cast(pl.Date))

# Pandas
df["event_date"] = pd.to_datetime(df["event_date"], unit="D")
```

قد تصل أعمدة مثل `Int128` بصيغة `FIXED_SIZE_BINARY` على شكل بايتات خام. ويوفّر Polars دعمًا مباشرًا للأعداد الصحيحة ذات 128 بت:

```python theme={null}
# Polars - native support
df = df.with_columns(pl.col("data").bin.reinterpret(dtype=pl.Int128, endianness="little"))
```

اعتبارًا من الإصدار 2.3 من NumPy، لا يتوفر `dtype` عام لعدد صحيح بطول 128 بت، لذا نضطر إلى الاعتماد على بايثون فقط، ويمكننا إجراء شيء مثل:

```python theme={null}
# Assuming we have a pandas dataframe with an Int128 column of dtype fixed_size_binary[16][pyarrow]

print(df)
# Output:
#   str_col                                        int_128_col
# 0    num1  b'\\x15}\\xda\\xeb\\x18ZU\\x0fn\\x05\\x01\\x00\\x00\\x00...
# 1    num2  b'\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00...
# 2    num3  b'\\x15\\xdfp\\x81r\\x9f\\x01\\x00\\x00\\x00\\x00\\x00\\x...

print([int.from_bytes(n, byteorder="little") for n in df["int_128_col"].to_list()])
# Output:
# [1234567898765432123456789, 8, 456789123456789]
```

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

<div id="read-formats">
  ## تنسيقات القراءة
</div>

تتحكم تنسيقات القراءة في أنواع بيانات القيم المُعادة من طرق `query` و`query_np` و`query_df` في العميل. (أما `raw_query` و`query_arrow` فلا يغيّران البيانات الواردة من ClickHouse، لذا لا ينطبق عليهما التحكم في التنسيق.) على سبيل المثال، إذا جرى تغيير تنسيق القراءة لمعرّف UUID من التنسيق `native` الافتراضي إلى التنسيق البديل `string`، فسيُعاد استعلام ClickHouse لعمود `UUID` على شكل قيم نصية (باستخدام تنسيق RFC 1422 القياسي 8-4-4-4-12) بدلًا من كائنات UUID في بايثون.

يمكن أن تتضمن وسيطة "نوع البيانات" لأي دالة تنسيق محارف بديلة. ويكون التنسيق عبارة عن سلسلة نصية واحدة بأحرف صغيرة.

يمكن تعيين تنسيقات القراءة على عدة مستويات:

* على المستوى العام، باستخدام الطرق المعرّفة في حزمة `clickhouse_connect.datatypes.format`. سيتحكم ذلك في تنسيق نوع البيانات المُكوَّن لجميع الاستعلامات.

```python theme={null}
from clickhouse_connect.datatypes.format import set_read_format

# Return both IPv6 and IPv4 values as strings
set_read_format('IPv*', 'string')

# Return all Date types as the underlying epoch second or epoch day
set_read_format('Date*', 'int')
```

* للاستعلام بالكامل، باستخدام وسيطة القاموس الاختيارية `query_formats`. في هذه الحالة، سيستخدم أي عمود (أو عمود فرعي) من أنواع البيانات المحددة التنسيق المُهيأ.

```python theme={null}
# Return any UUID column as a string
client.query('SELECT user_id, user_uuid, device_uuid from users', query_formats={'UUID': 'string'})
```

* بالنسبة إلى القيم في عمود معيّن، باستخدام وسيطة القاموس الاختيارية `column_formats`. يكون المفتاح هو اسم العمود كما يعيده ClickHouse، وتكون القيمة إما تنسيقًا لعمود البيانات أو قاموس "format" من المستوى الثاني يحتوي على اسم نوع في ClickHouse وقيمة من تنسيقات الاستعلام. يمكن استخدام هذا القاموس الثانوي مع أنواع الأعمدة المتداخلة مثل Tuples أو Maps.

```python theme={null}
# Return IPv6 values in the `dev_address` column as strings
client.query('SELECT device_id, dev_address, gw_address from devices', column_formats={'dev_address':'string'})
```

<div id="read-format-options-python-types">
  ### خيارات تنسيق القراءة (أنواع بايثون)
</div>

| نوع ClickHouse          | نوع بايثون الأصلي       | تنسيقات القراءة   | ملاحظات                                                                                                        |
| ----------------------- | ----------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------- |
| Int\[8-64], UInt\[8-32] | int                     | -                 |                                                                                                                |
| UInt64                  | int                     | signed            | لا يدعم Superset حاليًا قيم UInt64 الكبيرة غير الموقعة                                                         |
| \[U]Int\[128,256]       | int                     | string            | الحد الأقصى لقيم int في Pandas وNumPy هو 64 بت، لذا يمكن إرجاع هذه القيم كسلاسل نصية                           |
| BFloat16                | float                   | -                 | جميع قيم float في بايثون تكون داخليًا بدقة 64 بت                                                               |
| Float32                 | float                   | -                 | جميع قيم float في بايثون تكون داخليًا بدقة 64 بت                                                               |
| Float64                 | float                   | -                 |                                                                                                                |
| Decimal                 | decimal.Decimal         | -                 |                                                                                                                |
| String                  | string                  | bytes             | لا تحتوي أعمدة String في ClickHouse على ترميز مضمّن، لذا تُستخدم أيضًا للبيانات الثنائية متغيرة الطول          |
| FixedString             | bytes                   | string            | FixedStrings هي مصفوفات بايت ثابتة الحجم، ولكنها تُعامل أحيانًا كسلاسل نصية في بايثون                          |
| Enum\[8,16]             | string                  | string, int       | لا تقبل enum في بايثون السلاسل النصية الفارغة، لذا تُعرَض جميع قيم enum إما كسلاسل نصية أو كقيمة int الأساسية. |
| Date                    | datetime.date           | int               | يخزّن ClickHouse قيم Date على أنها عدد الأيام منذ 01/01/1970. وهذه القيمة متاحة كـ int                         |
| Date32                  | datetime.date           | int               | مثل Date، ولكن ضمن نطاق أوسع من التواريخ                                                                       |
| DateTime                | datetime.datetime       | int               | يخزّن ClickHouse قيم DateTime كثوانٍ منذ epoch. وهذه القيمة متاحة كـ int                                       |
| DateTime64              | datetime.datetime       | int               | يقتصر datetime.datetime في بايثون على دقة الميكروثانية. وتتوفر القيمة الخام من نوع int ‏64 بت                  |
| Time                    | datetime.timedelta      | int, string, time | تُحفَظ النقطة الزمنية كـ Unix timestamp. وهذه القيمة متاحة كـ int                                              |
| Time64                  | datetime.timedelta      | int, string, time | يقتصر datetime.timedelta في بايثون على دقة الميكروثانية. وتتوفر القيمة الخام من نوع int ‏64 بت                 |
| IPv4                    | `ipaddress.IPv4Address` | string            | يمكن قراءة عناوين IP كسلاسل نصية، كما يمكن إدراج سلاسل نصية منسقة بشكل صحيح كعناوين IP                         |
| IPv6                    | `ipaddress.IPv6Address` | string            | يمكن قراءة عناوين IP كسلاسل نصية، كما يمكن إدراج قيم منسقة بشكل صحيح كعناوين IP                                |
| Tuple                   | dict or tuple           | tuple, json       | تُعاد named tuples كقواميس افتراضيًا. ويمكن أيضًا إرجاع named tuples كسلاسل JSON                               |
| Map                     | dict                    | -                 |                                                                                                                |
| Nested                  | Sequence\[dict]         | -                 |                                                                                                                |
| UUID                    | uuid.UUID               | string            | يمكن قراءة معرّفات UUID كسلاسل نصية منسقة وفق RFC 4122<br />                                                   |
| JSON                    | dict                    | string            | يُعاد قاموس بايثون افتراضيًا. ويُرجع تنسيق `string` سلسلة JSON                                                 |
| Variant                 | object                  | -                 | يُرجع نوع بايثون المطابق لنوع بيانات ClickHouse المخزَّن لهذه القيمة                                           |
| Dynamic                 | object                  | -                 | يُرجع نوع بايثون المطابق لنوع بيانات ClickHouse المخزَّن لهذه القيمة                                           |

<div id="external-data">
  ## البيانات الخارجية
</div>

يمكن لاستعلامات ClickHouse قبول بيانات خارجية بأي تنسيق من تنسيقات ClickHouse. تُرسل هذه البيانات الثنائية مع سلسلة الاستعلام لاستخدامها في معالجة البيانات. تجد تفاصيل ميزة البيانات الخارجية [هنا](/docs/ar/reference/engines/table-engines/special/external-data). تقبل طرائق `query*` في العميل معلمة اختيارية باسم `external_data` للاستفادة من هذه الميزة. يجب أن تكون قيمة المعلمة `external_data` كائنًا من نوع `clickhouse_connect.driver.external.ExternalData`. يقبل مُنشئ هذا الكائن الوسيطات التالية:

| الاسم      | النوع             | الوصف                                                                                                                                         |
| ---------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| file\_path | str               | مسار ملف على النظام المحلي لقراءة البيانات الخارجية منه. يجب توفير أحد الخيارين `file_path` أو `data`                                         |
| file\_name | str               | اسم "ملف" البيانات الخارجية. إذا لم يتم توفيره، فسيُستمد من `file_path` (من دون الامتدادات)                                                   |
| data       | bytes             | البيانات الخارجية بصيغة ثنائية (بدلًا من قراءتها من ملف). يجب توفير أحد الخيارين `data` أو `file_path`                                        |
| fmt        | str               | [تنسيق الإدخال](/docs/ar/reference/formats) للبيانات في ClickHouse. القيمة الافتراضية هي `TSV`                                                     |
| types      | str or seq of str | قائمة بأنواع بيانات الأعمدة في البيانات الخارجية. إذا كانت سلسلة نصية، فيجب فصل الأنواع بفواصل. يجب توفير أحد الخيارين `types` أو `structure` |
| structure  | str or seq of str | قائمة تتضمن اسم العمود + نوع البيانات في البيانات (راجع الأمثلة). يجب توفير أحد الخيارين `structure` أو `types`                               |
| mime\_type | str               | نوع MIME اختياري لبيانات الملف. يتجاهل ClickHouse حاليًا هذا الترويس الفرعي في HTTP                                                           |

لإرسال استعلام مع ملف CSV خارجي يحتوي على بيانات "movie"، ودمج هذه البيانات مع جدول `directors` الموجود مسبقًا على خادم ClickHouse:

```python theme={null}
import clickhouse_connect
from clickhouse_connect.driver.external import ExternalData

client = clickhouse_connect.get_client()
ext_data = ExternalData(file_path='/data/movies.csv',
                        fmt='CSV',
                        structure=['movie String', 'year UInt16', 'rating Decimal32(3)', 'director String'])
result = client.query('SELECT name, avg(rating) FROM directors INNER JOIN movies ON directors.name = movies.director GROUP BY directors.name',
                      external_data=ext_data).result_rows
```

يمكن إضافة ملفات بيانات خارجية إضافية إلى كائن `ExternalData` الأولي باستخدام الطريقة `add_file`، التي تأخذ المعلمات نفسها التي يأخذها المُنشئ. بالنسبة إلى HTTP، تُرسَل جميع البيانات الخارجية كجزء من رفع ملف بصيغة `multi-part/form-data`.

<div id="time-zones">
  ## المناطق الزمنية
</div>

توجد عدة آليات لتطبيق منطقة زمنية على قيم ClickHouse من نوعي DateTime وDateTime64. داخليًا، يخزّن خادم ClickHouse دائمًا أي كائن DateTime أو `DateTime64` على شكل رقم غير مرتبط بمنطقة زمنية يمثّل عدد الثواني منذ epoch، أي 1970-01-01 00:00:00 بتوقيت UTC. وبالنسبة إلى قيم `DateTime64`، قد يكون هذا التمثيل بالمللي ثانية أو الميكروثانية أو النانوثانية منذ epoch، وذلك حسب precision. ونتيجة لذلك، فإن تطبيق أي معلومات خاصة بالمنطقة الزمنية يحدث دائمًا على جهة العميل. لاحظ أن هذا يتطلب عمليات حسابية إضافية ملحوظة، لذا يُنصح في التطبيقات الحساسة للأداء بالتعامل مع أنواع DateTime على أنها timestamps منذ epoch، باستثناء العرض للمستخدم والتحويل (فعلى سبيل المثال، تكون Pandas Timestamps دائمًا عددًا صحيحًا من 64 بت يمثّل نانوثواني epoch لتحسين الأداء).

عند استخدام أنواع بيانات مرتبطة بمنطقة زمنية في الاستعلامات، وبخاصة كائن Python ‏`datetime.datetime`، يطبّق `clickhouse-connect` منطقة زمنية على جهة العميل وفق قواعد الأسبقية التالية:

1. إذا جرى تحديد parameter للأسلوب query باسم `client_tzs` لهذا query، فستُطبَّق المنطقة الزمنية المحددة للعمود
2. إذا كان عمود ClickHouse يتضمن metadata للمنطقة الزمنية (أي إذا كان من نوع مثل DateTime64(3, 'America/Denver'))، فستُطبَّق المنطقة الزمنية الخاصة بعمود ClickHouse. (لاحظ أن metadata هذه للمنطقة الزمنية غير متاحة لـ clickhouse-connect لأعمدة DateTime قبل إصدار ClickHouse 23.2)
3. إذا جرى تحديد parameter للأسلوب query باسم `query_tz` لهذا query، فستُطبَّق "المنطقة الزمنية الخاصة بالاستعلام".
4. إذا طُبِّق إعداد timezone على query أو session، فستُطبَّق تلك المنطقة الزمنية. (هذه الوظيفة لم تُطرح بعد في خادم ClickHouse)
5. وأخيرًا، إذا كانت parameter ‏`apply_server_timezone` للعميل مضبوطة على True (وهو الإعداد الافتراضي)، فستُطبَّق server timezone الخاصة بـ ClickHouse.

لاحظ أنه إذا كانت المنطقة الزمنية المطبقة وفق هذه القواعد هي UTC، فإن `clickhouse-connect` سيُرجِع *دائمًا* كائن Python من النوع `datetime.datetime` غير مرتبط بمنطقة زمنية. ويمكن بعد ذلك إضافة معلومات منطقة زمنية إضافية إلى هذا الكائن غير المرتبط بمنطقة زمنية من خلال شيفرة التطبيق إذا لزم الأمر.
