少なくとも*2種類の関数があります。通常の関数 (単に「関数」と呼ばれます) と集約関数です。これらはまったく異なる概念です。通常の関数は、各行に個別に適用されるかのように動作します (各行において、関数の結果は他の行に依存しません) 。一方、集約関数は複数の行にまたがる値の集合を蓄積します (つまり、行全体の集合に依存します) 。
このセクションでは通常の関数について説明します。集約関数については、「集約関数」セクションを参照してください。
厳格な型付け
標準的な SQL とは異なり、ClickHouse は厳格な型付けを採用しています。つまり、型の間で暗黙的な変換は行いません。各関数は、特定の型の組み合わせに対してのみ動作します。そのため、場合によっては型変換関数を使用する必要があります。
共通部分式除去
同じ AST (同じレコード、または構文パースの結果が同じもの) を持つクエリ内のすべての式は、同一の値を持つと見なされます。こうした式はまとめられ、1 回だけ実行されます。同一のサブクエリも同様に除去されます。
結果の型
すべての関数は、結果として単一の値を返します (複数の値を返すことも、値をまったく返さないこともありません) 。結果の型は通常、値ではなく引数の型のみによって決まります。例外は、tupleElement関数 (a.N演算子) とtoFixedString関数です。
定数
簡単にするため、一部の関数では、特定の引数には定数しか使用できません。たとえば、LIKE 演算子の右側の引数は定数でなければなりません。 ほぼすべての関数は、定数の引数に対して定数を返します。例外は、乱数を生成する関数です。 'now' 関数は、異なる時刻に実行されたクエリに対して異なる値を返しますが、定数性が重要なのは単一のクエリ内だけであるため、その結果は定数と見なされます。 定数式も定数と見なされます (たとえば、LIKE 演算子の右辺は複数の定数から構成できます) 。
関数は、定数の引数と非定数の引数とで異なる方法で実装されることがあります (つまり、実行されるコードが異なります) 。ただし、定数に対する結果と、同じ値だけを含む実際のカラムに対する結果は一致している必要があります。
NULL の処理
関数には、次のような動作があります。
- 関数の引数のうち少なくとも 1 つが
NULLの場合、関数の結果もNULLになります。 - 各関数の説明で個別に定義される特別な動作。ClickHouse のソースコードでは、これらの関数には
UseDefaultImplementationForNulls=falseが設定されています。
不変性
関数は引数の値を変更できず、変更が必要な場合は結果として返されます。したがって、個々の関数の計算結果は、クエリ内でそれらの関数を記述する順序に依存しません。
高階関数
-> 演算子と lambda(params, expr) 関数
高階関数では、関数引数として受け取れるのはラムダ関数のみです。ラムダ関数を高階関数に渡すには、-> 演算子を使用します。矢印の左側には仮引数を指定します。これは任意の ID 1 つ、またはタプル内の任意の ID による複数の仮引数です。矢印の右側には式を指定し、この式ではこれらの仮引数に加えて、任意のテーブルのカラムも使用できます。
例:
x -> 2 * x
str -> str != Referer複数の引数を受け取るラムダ関数も、高階関数に渡すことができます。この場合、高階関数には、それらの引数に対応する同じ長さの複数の配列が渡されます。
一部の関数では、最初の引数 (ラムダ関数) を省略できます。この場合、恒等写像が仮定されます。
関数名をそのままラムダとして使う
完全なラムダ式を書く代わりに、関数名を直接高階関数に渡すことができます。関数名は自動的に、等価なラムダ式に変換されます。
たとえば、次の組み合わせは同等です。
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 UDF、実行可能 UDF、WebAssembly UDF で動作します。曖昧な場合は、関数名よりもカラム名とエイリアス名が優先されます。
ラムダのアリティは内側の関数から決まります。たとえば、arrayMap(plus, ...) では、plus が 2 つの引数を取るため、アリティ 2 が使われます。そのため、arrayMap(plus, [(1, 10), (2, 20)]) のようなタプル入力でも動作し、この場合はタプル要素がラムダの引数にアンパックされます。
可変長の内側の関数 (任意個の引数を受け取る concat など) の場合、ラムダのアリティは配列引数の数にフォールバックします。これは、arrayMap、arrayFilter、arrayFold のような高階関数では正しい動作です。一方、配列に加えて固定の非配列パラメータも受け取る高階関数、たとえば arrayPartialSort(f, limit, arr) では、可変長関数名をそのまま使うと誤ったアリティになることがあり、その場合は明示的なラムダが必要です。
また、可変長の内側の関数では、タプル入力は自動的にはアンパックされません。たとえば、arrayMap(concat, [('a', 'b'), ('c', 'd')]) は単項ラムダに書き換えられ、arrayMap((x, y) -> concat(x, y), [('a', 'b'), ('c', 'd')]) と同等ではありません。タプル要素を分解して可変長呼び出しに渡したい場合は、明示的なラムダを使用してください。
ユーザー定義関数 (UDFs)
ClickHouse はユーザー定義関数に対応しています。詳しくは UDFs を参照してください。