メインコンテンツへスキップ
メインコンテンツへスキップ

通常関数

関数には少なくとも* 2種類あります。通常関数 (単に「関数」とも呼ばれます) と集約関数です。これは互いにまったく別の概念です。通常関数は、各行に対して個別に適用されるかのように動作します (各行に対する関数の結果は、他の行には依存しません) 。集約関数は、複数の行から値の集合を蓄積します (つまり、すべての行の集合に依存します) 。

このセクションでは通常関数について説明します。集約関数については「集約関数」のセクションを参照してください。

注記

3番目の種類の関数として、'arrayJoin' 関数 に分類されるものがあります。また、テーブル関数 も別に挙げられます。

強い型付け

標準的な SQL と異なり、ClickHouse は強い型付けを採用しています。言い換えると、型間の暗黙的な変換は行われません。各関数は特定の型の組み合わせに対してのみ利用できます。このため、場合によっては型変換関数を使用する必要があります。

共通部分式除去

クエリ内で同一の AST (同一のノード、または構文解析結果が同じもの) を持つすべての式は、同じ値を持つと見なされます。これらの式はまとめられ、一度だけ実行されます。同一の副問い合わせも同様の方法で除去されます。

結果の型

すべての関数は、結果として単一の値のみを返します (複数の値を返したり、値を返さないことはありません) 。結果の型は通常、値ではなく引数の型のみによって決まります。例外は、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 など) の場合、ラムダのアリティは Array 引数の数にフォールバックします。これは、arrayMaparrayFilterarrayFold のような高階関数では正しい動作です。一方、配列に加えて固定の非配列パラメーターも受け取る高階関数、たとえば arrayPartialSort(f, limit, arr) では、可変長関数名をそのまま指定するとアリティが誤ることがあり、その場合は明示的なラムダが必要です。

また、可変長の内側関数では、タプル入力は自動展開されません。たとえば、arrayMap(concat, [('a', 'b'), ('c', 'd')]) は単項ラムダに書き換えられるため、arrayMap((x, y) -> concat(x, y), [('a', 'b'), ('c', 'd')]) と同等ではありません。タプル要素を分解して可変長呼び出しに渡したい場合は、明示的なラムダを使用してください。

ユーザー定義関数 (UDF)

ClickHouse はユーザー定義関数をサポートしています。詳しくは UDFs を参照してください。