IN وNOT IN وGLOBAL IN وGLOBAL NOT IN بشكل منفصل، نظرًا لغنى وظائفها وتعدد إمكانياتها.
الجانب الأيسر من المعامل هو إما عمود واحد أو Tuple.
أمثلة:
SELECT داخل أقواس.
للتوافق مع الإصدارات السابقة، عندما يكون الجانب الأيمن تعبير tuple واحدًا، يمكن تفسيره إما كمجموعة من القيم أو كقيمة tuple واحدة، وذلك بحسب الجانب الأيسر من عامل IN. إذا كان الجانب الأيسر قيمةً scalar، فإن ClickHouse يعامل عناصر تعبير tuple الواحد هذا في الجانب الأيمن كقيم IN منفصلة:
Query
Response
SELECT 1 IN (1, 2). إذا كان الجانب الأيسر أيضاً Tuple، فإن الجانب الأيمن يُفسَّر باعتباره مجموعة من قيم Tuple:
Query
Response
tuple واحداً. لا يمكن مطابقة الجانب الأيسر القياسي (scalar) مع جانب أيمن يحتوي على قيم tuple متعددة:
Query
Response
IN.
في هذه الحالة، يُحوِّل النظام قيمة الجانب الأيمن إلى نوع الجانب الأيسر،
كما لو طُبِّقت الدالة accurateCastOrNull على الجانب الأيمن.
يعني هذا أن نوع البيانات يصبح Nullable، وإذا تعذّر إجراء التحويل، فإنه يُرجع NULL.
مثال
Query
Response
UserID IN users)، فهذا يعادل الاستعلام الفرعي UserID IN (SELECT * FROM users). استخدم هذا عند التعامل مع البيانات الخارجية المُرسَلة مع الاستعلام. على سبيل المثال، يمكن إرسال الاستعلام مصحوبًا بمجموعة من معرّفات المستخدمين المحمَّلة في الجدول المؤقت ‘users’، والتي يجب تصفيتها.
إذا كان الجانب الأيمن من المعامل اسمَ جدول يستخدم محرك Set (مجموعة بيانات جاهزة تُحفظ دائمًا في ذاكرة RAM)، فلن تُعاد إنشاء مجموعة البيانات من جديد عند كل استعلام.
قد يحدد الاستعلام الفرعي أكثر من عمود واحد لتصفية المجموعات.
مثال:
Query
IN ويمينه من نفس النوع.
يمكن أن يرد عامل التشغيل IN والاستعلام الفرعي في أي جزء من الاستعلام، بما في ذلك داخل الدوال التجميعية ودوال lambda.
مثال:
Query
Response
IN مرة واحدة فقط دائمًا وعلى خادم واحد. لا توجد استعلامات فرعية معتمدة.
معالجة NULL
IN أن ناتج أي عملية على NULL يساوي دائمًا 0، بغض النظر عمّا إذا كانت NULL على الجانب الأيمن أو الأيسر من المعامل. لا تُدرَج قيم NULL في أي مجموعة بيانات، ولا تُعدّ متساوية فيما بينها، ولا يمكن مقارنتها إذا كانت transform_null_in = 0.
فيما يلي مثال باستخدام الجدول t_null:
SELECT x FROM t_null WHERE y IN (NULL,3) النتيجة التالية:
y = NULL يُستبعَد من نتائج الاستعلام. ويعود ذلك إلى أن ClickHouse لا يستطيع تحديد ما إذا كانت NULL ضمن المجموعة (NULL,3)، فيُرجِع 0 نتيجةً لهذه العملية، ولذلك يستبعد SELECT هذا الصف من المخرجات النهائية.
الاستعلامات الفرعية الموزعة
IN مع الاستعلامات الفرعية (على غرار عوامل JOIN): الصيغة العادية IN / JOIN والصيغة العامة GLOBAL IN / GLOBAL JOIN، ويختلفان في طريقة تنفيذهما عند معالجة الاستعلامات الموزعة.
تذكّر أن الخوارزميات الموضّحة أدناه قد تعمل بصورة مختلفة تبعًا لإعداد
distributed_product_mode ضمن الإعدادات.IN العادية، يُرسَل الاستعلام إلى الخوادم البعيدة، وتُنفِّذ كلٌّ منها الاستعلامات الفرعية الواردة في جملة IN أو JOIN.
عند استخدام GLOBAL IN / GLOBAL JOIN، تُنفَّذ أولاً جميع الاستعلامات الفرعية الخاصة بـ GLOBAL IN / GLOBAL JOIN، وتُجمَّع نتائجها في جداول مؤقتة. ثم تُرسَل هذه الجداول المؤقتة إلى كل خادم بعيد، حيث تُنفَّذ الاستعلامات باستخدام هذه البيانات المؤقتة.
بالنسبة لـ GLOBAL ... JOIN، يعتمد تحديد أي جانب من الـ join يُحسب كاستعلام فرعي على نوع الـ join: ففي حالتَي LEFT وINNER، يُحسب الجدول الأيمن؛ أما في حالة RIGHT، فيُحسب الجدول الأيسر بدلًا من ذلك، نظرًا لأن الجدول الأيمن هو الجانب المحفوظ وينبغي قراءته من الأجزاء (shards).
بالنسبة للاستعلام غير الموزَّع، استخدم IN / JOIN العادي.
توخَّ الحذر عند استخدام الاستعلامات الفرعية في عبارتَي IN / JOIN عند معالجة الاستعلامات الموزعة.
لنستعرض بعض الأمثلة. افترض أن كل خادم في الكلستر يحتوي على local_table عادية. كما يحتوي كل خادم على جدول distributed_table من نوع Distributed، والذي يشمل جميع الخوادم في الكلستر.
عند توجيه استعلام إلى distributed_table، يُرسَل الاستعلام إلى جميع الخوادم البعيدة وينفَّذ عليها باستخدام local_table.
على سبيل المثال، الاستعلام
IN:
- حساب تقاطع قيم audience لموقعين.
IN على كل خادم بصورة مستقلة، استناداً فقط إلى البيانات المخزنة محلياً على كل خادم من الخوادم.
سيعمل هذا بشكل صحيح وأمثل إذا كنت مستعدًا لهذه الحالة وقد وزّعت البيانات على خوادم الـ cluster بحيث تقع بيانات كل UserID بالكامل على خادم واحد. في هذه الحالة، ستكون جميع البيانات اللازمة متاحةً محليًا على كل خادم. وإلا، فستكون النتيجة غير دقيقة. نشير إلى هذا النوع من الـ query بـ “local IN”.
لتصحيح طريقة عمل الاستعلام عندما تكون البيانات موزَّعة عشوائيًا عبر خوادم الكلستر، يمكنك تحديد distributed_table داخل استعلام فرعي. سيبدو الاستعلام كالتالي:
GLOBAL IN بدلًا من IN. لنرَ كيف يعمل ذلك في الاستعلام:
_data1 إلى كل خادم بعيد مع الاستعلام (اسم الجدول المؤقت يعتمد على التنفيذ).
هذا أكثر كفاءة من استخدام IN العادي. ومع ذلك، ضع النقاط التالية في الاعتبار:
- عند إنشاء جدول مؤقت، لا تصبح البيانات مميّزة تلقائيًا. لتقليل حجم البيانات المنقولة عبر الشبكة، حدِّد DISTINCT في الاستعلام الفرعي. (لا تحتاج إلى فعل ذلك مع
INالعادي.) - سيُرسَل الجدول المؤقت إلى جميع الخوادم البعيدة. ولا يراعي الإرسال طوبولوجيا الشبكة. على سبيل المثال، إذا كانت 10 خوادم بعيدة موجودة في مركز بيانات بعيد جدًا عن خادم الطالب، فستُرسَل البيانات 10 مرات عبر القناة إلى مركز البيانات البعيد. حاول تجنب مجموعات البيانات الكبيرة عند استخدام
GLOBAL IN. - عند نقل البيانات إلى الخوادم البعيدة، لا يمكن تهيئة القيود المفروضة على عرض نطاق الشبكة. وقد يؤدي ذلك إلى إرهاق الشبكة.
- حاول توزيع البيانات على الخوادم بحيث لا تضطر إلى استخدام
GLOBAL INبانتظام. - إذا كنت بحاجة إلى استخدام
GLOBAL INكثيرًا، فخطط لموقع عنقود ClickHouse بحيث لا توجد مجموعة واحدة من النسخ المتماثلة إلا في مركز بيانات واحد، مع توفر شبكة سريعة بينها، بحيث يمكن معالجة الاستعلام بالكامل داخل مركز بيانات واحد.
GLOBAL IN، إذا كان هذا الجدول المحلي متاحًا فقط على خادم الطالب وكنت تريد استخدام بياناته على الخوادم البعيدة.
الاستعلامات الفرعية الموزعة و max_rows_in_set
max_rows_in_set و max_bytes_in_set للتحكم في مقدار البيانات المنقولة أثناء الاستعلامات الموزعة.
وتزداد أهمية ذلك بشكل خاص إذا كان استعلام GLOBAL IN يُرجِع كمية كبيرة من البيانات. انظر عبارة SQL التالية:
some_predicate انتقائيًا بدرجة كافية، فسيُرجع كمية كبيرة من البيانات ويتسبب في مشكلات بالأداء. في مثل هذه الحالات، من الحكمة الحدّ من نقل البيانات عبر الشبكة. لاحظ أيضًا أن set_overflow_mode مُعيَّن إلى throw (افتراضيًا)، ما يعني أنه يتم رفع استثناء عند بلوغ هذه العتبات.
الاستعلامات الفرعية الموزعة و max_parallel_replicas
M بين 1 و3 بحسب النسخة المتماثلة التي يُنفَّذ عليها الاستعلام المحلي.
تؤثر هذه الإعدادات في كل جدول من عائلة MergeTree ضمن الاستعلام، ولها التأثير نفسه كما لو جرى تطبيق SAMPLE 1/3 OFFSET (M-1)/3 على كل جدول.
لذلك، فإن إضافة الإعداد max_parallel_replicas لن تُنتج نتائج صحيحة إلا إذا كان للجدولين مخطط النسخ المتماثل نفسه، وكان أخذ العينات فيهما يتم باستخدام UserID أو مفتاح فرعي له. وعلى وجه الخصوص، إذا لم يكن لدى local_table_2 مفتاح أخذ عينات، فستنتج نتائج غير صحيحة. وتنطبق القاعدة نفسها على JOIN.
أحد الحلول البديلة، إذا كان local_table_2 لا يستوفي المتطلبات، هو استخدام GLOBAL IN أو GLOBAL JOIN.
إذا لم يكن لدى الجدول مفتاح أخذ عينات، فيمكن استخدام خيارات أكثر مرونة لـ parallel_replicas_custom_key، وقد ينتج عنها سلوك مختلف وأكثر ملاءمة.