الانتقال إلى المحتوى الرئيسي
يوجد نوعان على الأقل* من الدوال: الدوال العادية (ويُشار إليها ببساطة باسم “الدوال”) والدوال التجميعية. وهما مفهومان مختلفان تمامًا. تُطبَّق الدوال العادية على كل صف على حدة (فبالنسبة إلى كل صف، لا تعتمد نتيجة الدالة على الصفوف الأخرى). أما الدوال التجميعية فتجمع مجموعة من القيم من صفوف متعددة (أي إنها تعتمد على مجموعة الصفوف بأكملها). في هذا القسم نناقش الدوال العادية. أما الدوال التجميعية، فانظر إلى قسم “الدوال التجميعية”.
يوجد نوع ثالث من الدوال تندرج ضمنه دالة ‘arrayJoin’. ويمكن أيضًا الإشارة إلى دوال الجداول على نحو منفصل.

الكتابة الصارمة للأنواع

على عكس SQL القياسي، يعتمد ClickHouse كتابةً صارمة للأنواع. وبعبارة أخرى، لا يُجري تحويلات ضمنية بين الأنواع. تعمل كل دالة مع مجموعة محددة من الأنواع. وهذا يعني أنك تحتاج أحيانًا إلى استخدام دوال تحويل الأنواع.

إزالة التعبيرات الفرعية المشتركة

تُعدّ جميع التعبيرات في الاستعلام التي لها AST نفسه (أي السجل نفسه أو الناتج نفسه عن التحليل النحوي) ذات قيم متطابقة. لذلك تُدمج هذه التعبيرات وتُنفَّذ مرة واحدة فقط. وتُزال الاستعلامات الفرعية المتطابقة بالطريقة نفسها أيضًا.

أنواع النتائج

تعيد جميع الدوال قيمةً واحدة كنتيجة (وليس عدة قيم، ولا عدمَ وجود أي قيمة). ويُحدَّد نوع النتيجة عادةً بأنواع الوسائط فقط، لا بالقيم. والاستثناءان هما الدالة tupleElement (المعامل a.N) والدالة toFixedString.

الثوابت

للتبسيط، لا يمكن لبعض الدوال العمل إلا مع ثوابت في بعض الوسيطات. على سبيل المثال، يجب أن تكون الوسيطة اليمنى لمعامل LIKE ثابتة. تعيد جميع الدوال تقريبًا قيمة ثابتة عند تمرير وسيطات ثابتة. والاستثناء هو الدوال التي تولّد أرقامًا عشوائية. تعيد الدالة ‘now’ قيمًا مختلفة للاستعلامات التي شُغِّلت في أوقات مختلفة، لكن النتيجة تُعد ثابتة، لأن خاصية الثبات لا تهم إلا داخل استعلام واحد. ويُعد التعبير الثابت أيضًا ثابتًا (على سبيل المثال، يمكن تكوين الجزء الأيمن من معامل LIKE من عدة ثوابت). يمكن تنفيذ الدوال بطرق مختلفة للوسيطات الثابتة وغير الثابتة (تُنفَّذ شيفرة مختلفة). لكن يجب أن تتطابق النتائج بين الثابت وعمود فعلي لا يحتوي إلا على القيمة نفسها.

معالجة NULL

للدوال السلوكيات التالية:
  • إذا كانت واحدة على الأقل من وسيطات الدالة هي NULL، فستكون نتيجة الدالة أيضًا NULL.
  • سلوك خاص يُحدَّد بشكل منفصل في وصف كل دالة. في الشيفرة المصدرية لـ ClickHouse، تكون لهذه الدوال القيمة UseDefaultImplementationForNulls=false.

الثبات

لا يمكن للدوال تغيير قيم وسائطها؛ فأي تغيير يُعاد كنتيجة. لذا، فإن نتيجة حساب الدوال كلٌّ على حدة لا تعتمد على ترتيب كتابة هذه الدوال في الاستعلام.

الدوال عالية الرتبة

المعامل -> ودوال lambda(params, expr)

لا تقبل الدوال عالية الرتبة إلا دوال lambda بوصفها وسيطًا داليًا. ولتمرير دالة lambda إلى دالة عالية الرتبة، استخدم المعامل ->. يحتوي الجانب الأيسر من السهم على معلمة شكلية، ويمكن أن تكون أي ID، أو عدة معلمات شكلية — أي ID داخل tuple. أما الجانب الأيمن من السهم، فيحتوي على تعبير يمكنه استخدام هذه المعلمات الشكلية، بالإضافة إلى أي أعمدة في الجدول. أمثلة:
x -> 2 * x
str -> str != Referer
يمكن أيضًا تمرير lambda function تقبل عدة وسائط إلى دالة من الرتبة الأعلى. في هذه الحالة، تُمرَّر إلى الدالة من الرتبة الأعلى عدة مصفوفات ذات طول متماثل، بحيث تقابل هذه الوسائط تلك المصفوفات. في بعض الدوال، يمكن حذف الوسيط الأول (lambda function). في هذه الحالة، يُفترض تعيين متطابق.

أسماء الدوال وحدها كتعبيرات لامبدا

بدلًا من كتابة تعبير لامبدا كامل، يمكنك تمرير اسم دالة مباشرةً إلى دالة عالية الرتبة. ويُحوَّل اسم الدالة تلقائيًا إلى تعبير لامبدا مكافئ. على سبيل المثال، الأزواج التالية متكافئة:
SELECT arrayMap(negate, [1, 2, 3]);            -- [-1, -2, -3]
SELECT arrayMap(x -> negate(x), [1, 2, 3]);    -- [-1, -2, -3]

SELECT arrayMap(plus, [1, 2, 3], [10, 20, 30]);            -- [11, 22, 33]
SELECT arrayMap((x, y) -> plus(x, y), [1, 2, 3], [10, 20, 30]); -- [11, 22, 33]

SELECT arrayFilter(isNotNull, [1, NULL, 3, NULL, 5]);            -- [1, 3, 5]
SELECT arrayFilter(x -> isNotNull(x), [1, NULL, 3, NULL, 5]);    -- [1, 3, 5]

SELECT arrayFold(plus, [1, 2, 3, 4, 5], toUInt64(0));                      -- 15
SELECT arrayFold((acc, x) -> plus(acc, x), [1, 2, 3, 4, 5], toUInt64(0));  -- 15
يعمل هذا مع الدوال المضمنة، وSQL UDFs، وUDFs القابلة للتنفيذ، وWebAssembly UDFs. وتكون لأسماء الأعمدة والأسماء المستعارة أولوية على أسماء الدوال عند وجود لبس. يُستمد عدد وسيطات لامبدا من الدالة الداخلية. على سبيل المثال، يستخدم arrayMap(plus, ...) عدد وسيطات مقداره 2 لأن plus يأخذ وسيطتين، لذا فهو يعمل أيضًا مع مدخلات Tuple مثل arrayMap(plus, [(1, 10), (2, 20)])، حيث تُفك عناصر الـ Tuple إلى وسيطات لامبدا. بالنسبة إلى الدوال الداخلية متغيرة الوسائط (مثل concat، التي تقبل أي عدد من الوسائط)، يعود عدد وسيطات لامبدا إلى عدد وسائط المصفوفة. وهذا صحيح للدوال عالية الرتبة مثل arrayMap وarrayFilter وarrayFold. أما الدوال عالية الرتبة التي تقبل معلمات ثابتة غير مصفوفية بالإضافة إلى المصفوفات — على سبيل المثال، arrayPartialSort(f, limit, arr) — فقد تؤدي أسماء الدوال متغيرة الوسائط المستخدَمة مباشرةً إلى عدد وسيطات غير صحيح، وفي هذه الحالة تكون لامبدا صريحة مطلوبة. كذلك، لا تفك الدوال الداخلية متغيرة الوسائط مدخلات الـ Tuple تلقائيًا. على سبيل المثال، تُعاد كتابة arrayMap(concat, [('a', 'b'), ('c', 'd')]) إلى لامبدا أحادية، وهي ليست مكافئة لـ arrayMap((x, y) -> concat(x, y), [('a', 'b'), ('c', 'd')]). استخدم لامبدا صريحة عندما تريد تفكيك عناصر الـ Tuple إلى استدعاء متغير الوسائط.

الدوال المعرّفة من قبل المستخدم (UDFs)

يدعم ClickHouse الدوال المعرّفة من قبل المستخدم. راجع UDFs.
آخر تعديل في ٢٥ يونيو ٢٠٢٦