clickhousedb المبنية على المشغّل الأساسي. وتدعم اللهجة المتزامنة SQLAlchemy 1.4.40 والإصدارات الأحدث، بما في ذلك SQLAlchemy 2.x، مع التركيز على استعلامات Core، وClickHouse DDL، واستكشاف البنية، وعمليات insert البسيطة في ORM. أما اللهجة غير المتزامنة فتتطلب SQLAlchemy 2.0.44 أو أحدث.
ثبّت تبعيات SQLAlchemy باستخدام الـ extra الخاصة بالحزمة:
الاتصال عبر SQLAlchemy
أنشئ محركًا باستخدام صيغة URL clickhousedb:// أو clickhousedb+connect://:
معرّفات جلسات ClickHouse
افتراضيًا، يُنشئ كل اتصال ضمن مجمّع الاتصالات معرّف جلسة ClickHouse خاصًا به، سواء في اللهجة المتزامنة أو غير المتزامنة. وعندما تصل طلبات هذا الاتصال إلى عملية خادم ClickHouse نفسها، تظل الإعدادات المُغيَّرة باستخدامSET والجداول المؤقتة محفوظة لهذا الاتصال. وتجدر الإشارة إلى أن حالة الجلسة المسمّاة وعمليات فحص التداخل ضمن الجلسة نفسها تقتصر على العملية الواحدة. ففي عملية خادم واحدة، يُرفض فورًا أي طلب متداخل للمستخدم نفسه ومعرّف الجلسة نفسه برمز الخادم 373 بدلًا من وضعه في قائمة انتظار. وإذا هيّأت قيمة ثابتة لـ session_id، فاستخدم pool_size=1, max_overflow=0 أو اجعل الوصول تسلسليًا قبل أن تصل الطلبات إلى ClickHouse. أما في ClickHouse Cloud أو غيرها من البيئات التي تعتمد على موازنة الحمل، فقد تصل الطلبات التي تحمل معرّف الجلسة نفسه إلى خوادم مختلفة، لذا لا تعتمد على قيمة ثابتة لـ session_id بوصفها حالة موزّعة أو قفل استبعاد متبادل (mutex) موزّعًا.
الاتصالات غير المتزامنة
تتطلب اللهجة غير المتزامنة الإصدار 2.0.44 من SQLAlchemy أو أحدث، وتعتمد علىAsyncClient الأصلي في ClickHouse Connect. ثبّت الاعتماديات اللازمة لها، ثم أنشئ محركًا غير متزامن باستخدام عنوان URL clickhousedb+async://:
AsyncConnection.stream() الاستثناء InvalidRequestError. يقبل SQLAlchemy استدعاء AsyncSession.stream()، لكنّ اللهجة تخزّن النتيجة كاملةً مؤقتًا قبل إعادتها. للنتائج الكبيرة، استخدم طرق الدفق الأصلية في AsyncClient. ويمكن الوصول إلى العميل الأصلي الخام عبر driver_connection ما دام اتصال SQLAlchemy المرتبط به محجوزًا:
client.close() أو أيًّا من توابع دورة الحياة الخاصة به. ويتولى مجمّع الاتصالات في SQLAlchemy التحكم في تزامن الاتصالات. يمتلك كل اتصال ضمن المجمّع عميلًا أصليًا غير متزامن واحدًا، ويضبط افتراضيًا حدود موصل aiohttp على اتصال واحد إجمالًا واتصال واحد لكل مضيف. لتجاوز إعدادات النقل هذه، اضبط connector_limit أو connector_limit_per_host أو keepalive_timeout في URL أو في connect_args. عند استخدام pool_pre_ping=True، تتحقق SQLAlchemy من الاتصالات المُعاد استخدامها عبر SELECT 1 عند سحب اتصال من المجمّع.
ترسل عمليات الإدراج عبر executemany في SQLAlchemy غير المتزامنة حاليًا طلب HTTP واحدًا لكل مجموعة معلمات، بدلًا من استخدام بروتوكول الإدراج المجمّع الأصلي الخاص بالمشغّل. لذا لا تستخدم هذا المسار إلا للدفعات الصغيرة. أما للبيانات الضخمة، فاستخدم نمط الوصول driver_connection المملوك للمجمّع والموضح أعلاه، وانتظر اكتمال client.insert() قبل إعادة اتصال SQLAlchemy إلى المجمّع. ولأن executemany غير المتزامن يعتمد على ربط معلمات الاستعلام، فإن قيم datetime المجرّدة من المنطقة الزمنية تخضع للإعداد naive_datetime_binding، لا للإعداد naive_datetime_insert المستخدم في executemany الأصلي المتزامن. تحافظ عمليات ربط DateTime64 محددة النوع في SQLAlchemy على أجزاء الثانية، سواء مع المعلمات من جهة العميل أو من جهة الخادم. أما المعلمات غير محددة النوع %s أو %(name)s الممرَّرة إلى exec_driver_sql() فتحتفظ بالتنسيق الافتراضي بالثواني الكاملة لقيم datetime المجرّدة من المنطقة الزمنية. استخدم قيمًا تتضمن المنطقة الزمنية لضمان سلوك واضح لا لبس فيه. واستخدم client.insert() للحصول على دلالات الإدراج المجمّع الأصلي.
أنشئ المحرك غير المتزامن وتخلّص منه داخل حلقة الأحداث نفسها التي يُستخدم فيها. أعِد كل اتصال مسحوب، ثم انتظر engine.dispose() أثناء الإيقاف وقبل استخدام المحرك من حلقة أحداث أخرى. وإذا كانت الحلقة المالكة للمحرك قد أُغلقت بالفعل، فانتظر engine.dispose() في الحلقة الحالية قبل إعادة استخدامه. قد يُبلغ aiohttp عن وسيلة نقل غير مغلقة إذا لم يبدأ التنظيف إلا بعد إغلاق الحلقة المالكة، لذا تخلّص من المحرك قبل نقله كلما أمكن. لا يُغني pool_pre_ping=True عن التخلّص من المحرك عند نقل محرك غير متزامن يستخدم مجمّع اتصالات بين حلقات الأحداث. ولمشاركة محرك واحد عبر حلقات أحداث متعددة دون الاحتفاظ باتصالات مرتبطة بحلقة معينة، اضبط poolclass=NullPool. وإذا جرى التخلّص بينما لا يزال أحد الاتصالات مسحوبًا، تغلق اللهجة ذلك الاتصال عند إعادته أو عند جمعه ضمن جمع المهملات. لا تستدعِ engine.sync_engine.dispose() من شيفرة متزامنة؛ إذ لا تستطيع SQLAlchemy في هذه الحالة انتظار تنظيف الاتصالات غير المتزامنة، وقد تكتفي بتسجيل الخطأ بدلًا من إغلاق وسائل النقل في المجمّع.
يمكن أن تتضمن معلمات استعلام URL إعدادات ClickHouse، أو خيارات عميل ClickHouse Connect مثل compression وquery_limit ومهلات الانتظار، أو خيارات HTTP/TLS مثل ca_cert. أضف البادئة ch_ إلى إعداد ClickHouse لفرض معاملته كإعداد خادم عند الحاجة، مثل ch_http_max_field_name_size=99999.
راجع وسائط الاتصال والإعدادات للاطلاع على خيارات العميل المتاحة.
شغّل أدوات SQLAlchemy المساعدة المتزامنة، مثل DDL والفحص، عبر AsyncConnection.run_sync():
إعدادات خاصة بكل استعلام
مرِّر إعدادات ClickHouse عبر خيارات التنفيذ في SQLAlchemy. يمكن تعيين الإعدادات على المحرك أو الاتصال أو التعليمة. وتكون قيمة التعليمة لها الأسبقية على قيمة الاتصال أو المحرك عند استخدام المفتاح نفسه.تنسيقات القراءة لكل استعلام
عيّن تنسيقات قراءة ClickHouse على مستوى المحرك أو الاتصال أو التعليمة عبر خيارات تنفيذ SQLAlchemy باستخدامquery_formats. تُطبَّق تنسيقات التعليمة أولًا، لذا تتجاوز المفاتيح وأحرف البدل المطابقة على مستوى الاتصال أو المحرك.
معالجة الأخطاء
تستخدم الأخطاء التي يُطلقها المشغّل عبر اتصال SQLAlchemy أصنافَ DB-API المُصدَّرة منclickhouse_connect.dbapi. وهذه الأصناف هي كائنات الأصناف نفسها المقابلة لها في clickhouse_connect.driver.exceptions، ولذلك يغلّفها SQLAlchemy في الفئة الفرعية المطابقة من sqlalchemy.exc.DBAPIError. أما StreamFailureError فهو من نوع OperationalError، ويُغلَّف على هيئة sqlalchemy.exc.OperationalError.
إذا كان الإلغاء من جهة المستدعي قد يقاطع استدعاءً صريحًا لـ AsyncConnection.invalidate()، فشغّل عملية الإبطال ضمن مهمة تملكها، وانتظر اكتمالها قبل تمرير الإلغاء. يتيح ذلك لـ SQLAlchemy إكمال عمليات تتبّع سجلات الاتصال:
await connection.invalidate() وظلت قيمة connection.invalidated هي false، فاستدعِ connection.invalidate() مجددًا مع await لإكمال عملية التنظيف قبل استخدام الاتصال أو إغلاقه.
معلمات من جهة الخادم
يعرض SQLAlchemy المعلمات عادةً من جهة العميل. فعِّل معلمات ClickHouse من جهة الخادم عند إنشاء المحرّك:server_side_params=True مع create_async_engine() للّهجة غير المتزامنة.
في هذا الوضع، يجب أن تكون كل قيمة مقيّدة من نوع SQLAlchemy متوافق مع ClickHouse. وتتحول قوائم IN المدعومة إلى معلمات ClickHouse من النوع Array. ويرفع المصرّف CompileError عندما يتعذر عليه استنتاج نوع متوافق أو معالجة قيمة مقيّدة بأمان.
يجب أن تكون أسماء الربط أسماء ClickHouse من نوع ASCII BareWord. تُرفض الأسماء التي تبدأ وتنتهي بـ $ لأن المشغّل الأساسي يحجزها لمعلمات الاستعلام الثنائية الخام.
استعلامات Core
تدعم هذه اللهجة استعلاماتSELECT في SQLAlchemy Core مع عمليات الربط، وعوامل التصفية، والترتيب، والحدود والإزاحات، وDISTINCT، وعمليات select المركبة.
تُترجم union() وintersect() وexcept_() في SQLAlchemy إلى UNION DISTINCT وINTERSECT DISTINCT وEXCEPT DISTINCT في ClickHouse. وتُترجم نظائرها union_all() وintersect_all() وexcept_all() إلى عوامل التشغيل المقابلة مع ALL. يحافظ هذا التعيين الصريح على دلالات التكرارات في SQLAlchemy بغض النظر عن الإعدادات الافتراضية لعمليات المجموعات في ClickHouse.
DELETE الخفيف ويتطلب عبارة WHERE صريحة:
عرض القيم الحرفية
عندما يضمّن SQLAlchemy قيمة مقيّدة باستخدامliteral_binds أو literal_execute، تستخدم اللهجة أسلوب الاقتباس في ClickHouse لأنواع السلاسل النصية العامة وأنواع ClickHouse. وينطبق ذلك أيضًا عند استخدام أغلفة TypeDecorator واختيارات with_variant(). تحتفظ قيم السلاسل النصية بعلامات النسبة المئوية والشرطات المائلة العكسية، حتى مع وجود مَعلَمات مقيّدة أخرى.
تحتفظ قيم datetime في بايثون المقترنة بنوع SQLAlchemy DateTime64 الخاص بـ ClickHouse بأجزاء الميكروثانية، سواء في المعلمات على جهة العميل أو في القيم الحرفية المضمّنة، بما في ذلك القيم القابلة للإلغاء (nullable) والقيم المتداخلة داخل المصفوفات والصفوف (tuples). ويطبّق ClickHouse الدقة المُعلَنة، علمًا بأن قيم datetime في بايثون توفّر ما يصل إلى ستة أرقام كسرية. أما قيم DateTime العادية فتحتفظ بتنسيق الثواني الكاملة. وفي حالة عبارة text()، حدِّد النوع صراحةً باستخدام bindparam("ts", type_=DateTime64(6)) للحفاظ على أجزاء الثانية.
يجب أن تتطابق أنواع الأعمدة في SQLAlchemy مع المخطط على الخادم؛ إذ إن تعريف DateTime64 لعمود من النوع DateTime على الخادم يؤدي إلى عرض أجزاء الثانية، وقد يتسبب في أخطاء تحويل عند الإدراج وفي مقارنات IN.
في SQLAlchemy 2.x، تتطلب القيم الحرفية المضمّنة لأنواع sqlalchemy.ARRAY العامة التي تحتوي على عناصر Tuple من ClickHouse تعيين dimensions=1، أو عدد الأبعاد الأعلى المناسب في حالة المصفوفات المتداخلة، كي يعامل SQLAlchemy كل صف (tuple) كعنصر واحد. ولا يدعم SQLAlchemy 1.4 القيم الحرفية المضمّنة لأنواع ARRAY العامة.
إذا أُعيد استخدام معلمة تاريخ ووقت مُسمّاة، فيجب أن يكون لكل موضع تظهر فيه نوع ربط DateTime64 متوافق للحفاظ على أجزاء الثانية؛ فأي موضع غير محدد النوع أو ذي نوع متعارض يُبقي على تنسيق الثواني الكاملة. عيِّن type_=DateTime64(6) في كل bindparam، أو استخدم أسماء معلمات مختلفة بالأنواع المناسبة.
تلميحات نوع JSON
أعلن عن مسارات JSON ذات الأنواع المحددة باستخدام تعيينtyped_paths. يمكن أن يكون نوع المسار صنف نوع من ClickHouse SQLAlchemy، أو نسخة مهيَّأة، أو سلسلة نصية تحمل اسم نوع ClickHouse. وتدعم سلاسل أسماء الأنواع الأنواعَ التي لا يوجد لها مُنشئ في SQLAlchemy، مثل Dynamic، كما يمكن استخدامها لتعبيرات الأنواع المهيَّأة المعقدة، وهي تحافظ على الأسماء داخل Tuple المسمّى.
يمكن أن تحتوي سلاسل أسماء الأنواع على أنواع JSON متداخلة مهيَّأة مثل Array(JSON(`child` UInt32)). وأسماء أنواع ClickHouse المعروفة غير حساسة لحالة الأحرف داخل هذه السلاسل، وتُخرَج بحالة الأحرف القياسية الخاصة بها. ويجب أن تحتوي السلسلة على تعبير نوع واحد كامل، إذ يُرفض أي نص لاحق أو وسائط JSON متداخلة غير صحيحة التكوين.
لا يُدعم Tuple() الفارغ كمسار JSON ذي نوع محدد، لأن ClickHouse لا يستطيع تسلسله عبر تنسيق Native الخاص بـ JSON column. ويدعم المشغّل الأساسي Tuple() في أعمدة الاستعلام والإدراج في أي موضع، بما في ذلك التداخل داخل الصفوف الموضعية أو المسمّاة، وداخل Array، وكذلك بصيغة Nullable(Tuple()) حيثما كان ذلك مفعّلًا في الخادم.
typed_paths، على سبيل المثال JSON(user_id=UInt32). استخدم typed_paths مع المسارات التي تحتوي على نقاط أو مسافات أو backticks أو نقاطًا مُرمَّزة بصيغة %2E، أو الأسماء التي تتطابق مع خيارات المُنشئ. ويُدعم مسار مُحدَّد النوع باسم SKIP من خلال الربط. المفاتيح في typed_paths والقيم في skip_paths هي أسماء مفكوكة الترميز. وتُعامل الـ backticks وعلامات الاقتباس المزدوجة في بداية المسار أو نهايته بوصفها محارف حرفية ضمن المسار، لا بوصفها اقتباس SQL مطبّقًا مسبقًا. أما داخل سلسلة النوع الخام، فتمثّل الـ backticks وعلامات الاقتباس المزدوجة صياغة معرّفات ClickHouse.
يمكن تهيئة ما يصل إلى 1000 مسار مُحدَّد النوع. ويقبل max_dynamic_paths قيمًا من 0 إلى 10000، ويقبل max_dynamic_types قيمًا من 0 إلى 254. وتنطبق هذه النطاقات أيضًا داخل سلاسل نوع JSON المتداخلة الخام. وتُحذف القيم الافتراضية الصريحة للخادم البالغة 1024 و32 من الـ DDL المُولَّد. وتُزال التكرارات من مسارات التخطي البسيطة. ولا تتحقق بايثون من صحة سلاسل التعابير النمطية لأن ClickHouse يستخدم صياغة RE2، ويُحتفظ بالتعابير النمطية المكررة كما هي.
لا يمكن أن يحمل مسار تخطٍّ بسيط الاسم REGEXP تحديدًا، لأن ClickHouse يحجز هذا الـ token لصيغة SKIP REGEXP. أما أسماء مثل REGEXP_foo فتبقى صالحة. وفي سلسلة نوع JSON خام، يجب أن يكون معامل SKIP البسيط معرّف ClickHouse واحدًا أو معرّفًا مركبًا مفصولًا بنقاط. ولا يمكن أن يبدأ المعرّف المركب غير المقتبس بـ REGEXP؛ فاقتبس المكوّن الأول عندما يكون جزءًا من بيانات المسار. ويجب أن يتضمن SKIP REGEXP قيمة حرفية نصية واحدة بين علامتي اقتباس مفردتين. واقتبس أجزاء المعرّف باستخدام الـ backticks أو علامات الاقتباس المزدوجة عندما تحتوي على مسافات أو علامات ترقيم. وتدعم تلميحات نوع JSON الخام Variant(...)؛ أما Variant بمفرده فليس له مُنشئ عام في SQLAlchemy. وتُرتَّب أعضاء Variant وتُزال تكراراتها وفق الأسماء القياسية نفسها التي يستخدمها ClickHouse.
ويرتّب المُنشئ الوسائط بالصيغة القياسية نفسها التي يعيدها ClickHouse. كما تحافظ الأنواع المنعكسة، ونسخ أنواع SQLAlchemy، والتوليد التلقائي في Alembic على التهيئة.
الأعمدة الفرعية لـ JSON
بالنسبة إلى عمود مُعرَّف أو ممثَّل في ClickHouse بصفتهJSON، استخدم الأقواس المربعة لاختيار مقطع واحد في كل مرة من مسار عمود فرعي مدعوم بالتخزين:
payload["severity"] إلى صياغة المعرّف المنقّط في ClickHouse. يُقتبس كل جزء على حدة، على سبيل المثال `events`.`payload`.`severity`. يقرأ العمود الفرعي JSON المخزّن في ClickHouse ولا يستدعي getSubcolumn. استخدم [] أو .subcolumn() بشكل متسلسل، مرة واحدة لكل مقطع من المسار. يجب أن يكون كل مقطع سلسلة غير فارغة.
يؤدي تمرير type_ إلى .subcolumn() إلى تغليف المسار المنقّط بعملية CAST في SQL وإسناد هذا النوع إلى تعبير SQLAlchemy. من دون type_، تتصرف .subcolumn("segment") مثل ["segment"].
يكون نوع المسار غير المحدد Dynamic في ClickHouse. لا يسمح ClickHouse باستخدام قيم Dynamic مباشرةً في ORDER BY أو GROUP BY. مرّر type_ عند استخدام عمود فرعي في هذه المواضع.
بالنسبة إلى الشيفرة ذات الأنواع الثابتة، استورد json_subcolumn من clickhouse_connect.cc_sqlalchemy. تقبل الدالة المساعدة أيضًا مقطعًا واحدًا في كل مرة وتحافظ على نوع نتيجة بايثون المحدد بواسطة type_:
request_id على أنه ColumnElement[int].
يُقتبس كل مقطع بشكل مستقل، بما في ذلك الأسماء التي تحتوي على مسافات أو علامات اقتباس خلفية. لا تجعل علامات الاقتباس الخلفية النقطة قيمة حرفية في معالجة مسارات JSON في ClickHouse. عند تمكين json_type_escape_dots_in_keys، استخدم ترميز ClickHouse %2E للنقاط الحرفية في المفاتيح. للوصول إلى مفتاح باسم a.b، استخدم payload["a%2Eb"]، وليس payload["a.b"].
امتدادات استعلام ClickHouse
استوردselect من clickhouse_connect.cc_sqlalchemy لتمكين أدوات التحقق الساكنة من الأنواع من التعرّف على طرائق ClickHouse المعرّفة الأنواع. كما تتوفر هذه الطرائق أيضًا في sqlalchemy.select القياسي وقت التشغيل.
Select في ClickHouse هي:
تُعد
Select.with_hint() في SQLAlchemy واجهة برمجة تطبيقات لتلميحات الجداول. لا تُنشئ لهجة ClickHouse تلميحات الجداول. يؤدي استخدام تلميح wildcard أو clickhousedb قابل للتطبيق إلى إصدار SAWarning مع إبقاء SQL المُولَّد دون تغيير. استخدم final() أو sample() أو prewhere() أو limit_by() لبنود ClickHouse هذه.
تُعد Select.with_statement_hint() واجهة برمجة تطبيقات لتوجيه خام يُضاف في النهاية. وهي تُلحق النص المقدَّم بنهاية SELECT دون تحقق خاص بـ ClickHouse. يظل ذلك متاحًا لاستخدامه مع SQL ثابت وموثوق، مثل SETTINGS max_threads=1:
GLOBAL ANY LEFT JOIN في ClickHouse دون الحاجة إلى تداخل FromClause مخصّص:
Lambda مع الدوال عالية الرتبة في ClickHouse:
values() عند الترجمة إلى صياغة دالة الجدول VALUES في ClickHouse، بما في ذلك عند استخدامها في تعبير الجدول الشائع. يتطلب شكل تعبير الجدول الشائع استخدام SQLAlchemy 2.0.42 أو إصدار أحدث، إذ أُضيفت Values.cte().
تعبيرات الجدول الشائعة المُجسَّدة
يُضمّن ClickHouse تعبير الجدول الشائع تلقائيًا، لذا إذا أُشير إلى CTE أكثر من مرة، يُنفَّذ محتواه مرةً لكل مرجع. مرّرmaterialized=True إلى .cte() لإنتاج WITH <name> AS MATERIALIZED (...)، بحيث يُحسب المحتوى مرةً واحدة:
enable_materialized_cte=1 وتمكين المحلِّل. عيّن enable_materialized_cte على التعليمة أو الاتصال أو المحرك كما هو موضح في إعدادات خاصة بكل استعلام. يكون المحلِّل مُمكّنًا افتراضيًا على كل خادم يدعم هذه الميزة، لذا يُعد تعيين enable_analyzer=1 صراحةً إجراءً احترازيًا. يُعد enable_materialized_cte إعدادًا تجريبيًا في ClickHouse. عند استخدام enable_materialized_cte=0 أو enable_analyzer=0، ينجح الاستعلام ويُرجع الصفوف نفسها. يتجاهل ClickHouse قيمة MATERIALIZED بصمت ويضمّن تعبير الجدول الشائع مجددًا، لذا فإن نسيان الإعداد يؤثر في الأداء دون ظهور أي تنبيه. تتطلب تعبيرات الجدول الشائع المُجسَّدة ClickHouse 26.3 أو إصدارًا أحدث. ترفض الخوادم الأقدم الكلمة المفتاحية باعتبارها خطأً نحويًا.
بالنسبة إلى تعليمة مُنشأة باستخدام sqlalchemy.select القياسي، استخدم cte() على مستوى الوحدة بدلًا من ذلك. تأخذ التعليمة كوسيط أول، وتكافئ Select.cte() فيما عدا ذلك:
ValueError عند تعيين كلٍّ من recursive=True وmaterialized=True.
DDL واستكشاف البنية
يوفّر ClickHouse Connect أنواع بيانات ClickHouse، ومحركات الجداول، وبُنى القواميس، وDDL لقواعد البيانات، واستكشاف بنية الجداول. تُستكشف أعمدةVariant المستقلة (standalone) عبر نوع SQLAlchemy داخلي، ويحافظ التوليد التلقائي في Alembic على أسماء أنواعها الخام القياسية دون تغييرات متكررة في الأنواع. أما أعمدة Geometry وMultiPoint فتُستكشف كأنواع SQLAlchemy عامة.
server_default لتعبيرات DEFAULT، وسمات خاصة بكل dialect مثل clickhouse_codec وclickhouse_ttl وclickhouse_materialized وclickhouse_alias إن وُجدت.
تستخدم قيم السلاسل النصية في عبارات DEFAULT وMATERIALIZED وALIAS وTTL إفلات السلاسل النصية في ClickHouse. وينطبق الإفلات نفسه على تعليقات الجداول والقواميس والأعمدة، بما في ذلك التعليقات التي يصدرها Alembic.
تقبل وسائط مفاتيح MergeTree مثل order_by وpartition_by وprimary_key وsample_by وttl أعمدة SQLAlchemy وتعبيرات SQL، بالإضافة إلى السلاسل النصية العادية.
يمكن استدعاء Memory() وLog() وStripeLog() وTinyLog() وNull() وSet() دون أي وسائط، وتبقى بنيتها سليمة عند كتابتها ثم قراءتها عبر التوليد التلقائي في Alembic. ولا تزال وسيطة القاموس الحالية مدعومة. استخدم settings={...} لتمرير إعدادات المحرك.
يقبل SummingMergeTree وReplicatedSummingMergeTree وسيطة columns اختيارية لا تُمرَّر إلا بالاسم. وتحتفظ الوسائط الموضعية الحالية بمعناها، لذا لا يزال SummingMergeTree("id") يضبط ORDER BY id.
"delta" أو "(delta, n_tx)". ويتطلب الخادم معرّفات لهذه الأعمدة. احذف columns لتترك لـ ClickHouse تحديد الأعمدة المراد جمعها. ويحافظ استكشاف البنية والتوليد التلقائي في Alembic على قائمة الأعمدة المحددة صراحةً.
عمليات الإدراج واستخدام ORM الأساسي
عمليات إدراج Core ونماذج ORM البسيطة مدعومة. في اللهجة المتزامنة، يُفضَّل استخدام عمليات إدراج Core عبر executemany لمسارات البيانات المجمّعة المتوافقة. أما عمليات الإدراج المجمّعة غير المتزامنة، فاستخدم لها المسار الأصليAsyncClient.insert() الموضّح في الاتصالات غير المتزامنة.
executemany البسيطة في Core التي يولّدها مصرّف SQLAlchemy عملية إدراج مجمّعة Native واحدة. أما executemany غير المتزامن فيرسل طلبًا واحدًا لكل مجموعة معلمات، كما هو موضّح في الاتصالات غير المتزامنة. أما استعلامات Raw SQL وعمليات الإدراج التي تتضمن تعابير أو دلالات أخرى يتعذّر توجيهها بأمان، فتحتفظ بعبارة SQL الأصلية وتُنفَّذ مرة واحدة لكل مجموعة معلمات. وإذا فشلت مجموعة معلمات لاحقة، تبقى الصفوف التي كتبتها مجموعات المعلمات السابقة مثبّتة.
تعمل عبارات insert(events).values([...]) الصريحة متعددة الصفوف مع صفوف القواميس، والصفوف من نوع tuple المرتّبة وفق ترتيب أعمدة الجدول، وتعابير SQL الخاصة بكل صف. ويستخدم Pandas to_sql(method="multi") هذا الشكل؛ إذ يُدرج الصفوف لكنه يُرجع 0، لأن عبارات INSERT النصية تُبلغ عن عدد صفوف يساوي 0 عبر مؤشر DB-API. ويحدد SQLAlchemy قائمة الأعمدة استنادًا إلى الصف الأول، فتُتجاهَل مفاتيح القاموس الإضافية في الصفوف اللاحقة وقيم tuple الواقعة خارج قائمة الأعمدة المحددة. وإذا خلا صف لاحق من إحدى القيم المحددة، يفشل التصريف. لذا احرص على أن تتضمن جميع الصفوف الأعمدة نفسها.
مع حدود نموذج HTTP الافتراضية في ClickHouse 26.4 والإصدارات الأحدث، لا يصلح server_side_params=True إلا للدفعات الصريحة الصغيرة، أي أقل من 1000 قيمة ربط تقريبًا مع ترك هامش للحقول الأخرى. ويمكن رفع هذا الحد الأقصى عبر تهيئة الخادم. أما الدفعات البسيطة الكبيرة مع اللهجة المتزامنة، فمرّر الصفوف بوصفها الوسيط الثاني إلى execute() كي يتمكن المشغّل من استخدام مسار الإدراج المجمّع Native الخاص به. وللبيانات المجمّعة غير المتزامنة، استخدم await مع الطريقة الأصلية AsyncClient.insert().
ترحيلات Alembic
يتضمن ClickHouse Connect تكاملًا مع Alembic لإجراء ترحيلات مخطط ClickHouse. ثبّته باستخدام:alembic.ini المُولَّد الإعداد script_location = %(here)s/alembic. أبقِ على هذا الإعداد إذا كان اسم دليل الترحيل alembic، وإلا فحدّثه ليشير إلى الدليل الذي مرّرته إلى alembic init. استبدل alembic/env.py بـ مثال env.py غير المتزامن لـ Alembic المُضمَّن في المستودع، ثم اضبط sqlalchemy.url في alembic.ini.
استورد clickhouse_connect.cc_sqlalchemy.alembic في ملف env.py الخاص بـ Alembic لتسجيل تكامل اللهجة. يدعم التوليد التلقائي تغييرات الجداول الشائعة، بما في ذلك إنشاء الجداول وإزالتها، وإضافة الأعمدة وتعديلها وحذفها، والقيم الافتراضية، والتعليقات. استخدم العمليات اليدوية لإعادة تسمية الجداول والأعمدة. راجع كل عملية ترحيل مولَّدة قبل تطبيقها.
تظل دوال الترحيل في Alembic متزامنة. إذ تُنشئ البيئة غير المتزامنة كائن AsyncEngine، وتفتح AsyncConnection، ثم تمرّر دالة الترحيل المتزامنة إلى await connection.run_sync(...). أما الترحيلات دون اتصال فتستدعي context.configure(url=..., literal_binds=True, dialect_opts={"paramstyle": "named"}) مباشرةً دون إنشاء محرك. ويتضمن مثال env.py غير المتزامن لـ Alembic المُضمَّن في المستودع كلا المسارين، ويقرأ URL الاتصال من تهيئة sqlalchemy.url القياسية في Alembic. كما يحتفظ بخطافات Alembic وخياراتها الخاصة بـ ClickHouse الواردة في المثال العملي، بما في ذلك include_object وmake_include_name(...) وclickhouse_writer وversion_table. لا تستخدم engine.sync_engine لتشغيل الترحيلات غير المتزامنة أو التخلص منها.
تشمل أدوات op.* المساعدة الخاصة بـ ClickHouse ما يلي:
- فهارس تخطي البيانات، بما في ذلك عمليات الإضافة وmaterialize والحذف.
- الإسقاطات، بما في ذلك عمليات الإضافة وmaterialize والحذف.
- تعديل إعدادات جدول MergeTree وإعادة ضبطها.
- إنشاء materialized view وإزالتها.
- إنشاء القواميس وإزالتها وإعادة تحميلها.
Index وColumn(index=True) وop.create_index وop.drop_index لتجنّب عبارات DDL الجزئية أو غير الصحيحة. استخدم op.add_clickhouse_index وop.drop_clickhouse_index.
راجع المثال العملي الكامل لـ Alembic. كما ينبغي للمستخدمين الذين يرحّلون من clickhouse-sqlalchemy قراءة دليل الترحيل.
النطاق والقيود
- لا يوفّر ClickHouse المعاملات التقليدية عبر لهجة HTTP هذه. ينظّم
engine.begin()وSession.commit()العمل على جانب بايثون، لكن commit و التراجع لا يُحدثان أي تأثير على الخادوم. - لا تدعم هذه اللهجة
UPDATE، والمعاملات ثنائية الطور، والتسلسلات، وRETURNING، ومستويات العزل المتقدمة. استخدم ClickHouse SQL الصريح لتنفيذ تعديلات الخادوم عند الحاجة. - يوفّر
Column(..., primary_key=True)هوية الكائن في SQLAlchemy، لكنه لا ينشئ قيد تفرد على جانب الخادوم. حدِّد تعبيرات الفرز وتعبيرات المفتاح الأساسي الاختيارية من خلال محرك الجدول. - لا تتوفر البيانات الوصفية التقليدية للمفاتيح الخارجية وقيود التفرد والفهارس القياسية، لأن ClickHouse لا يفرض هذه القيود.
- تخرج إدارة العلاقات في ORM، وتحديثات وحدة العمل، والتتابعات، والتحميل الفوري أو المؤجل للعلاقات، عن نطاق ORM المدعوم.