このガイドでは、ClickHouseで配列を使用する方法と、よく使われる配列関数の一部を紹介します。
Array の概要
array 関数を使って Array を作成できます。
[] を使用することもできます:
異なる型の配列を作成する
異なる型の配列を作成する
上で説明したデフォルトの動作は、また、型名を指定して配列から各型の値を取り出すこともできます。
use_variant_as_common_type 設定を使って変更できます。
これにより、引数の型に共通の型がない場合でも、if/multiIf/array/map 関数の結果型として Variant 型を使用できます。例えば、次のとおりです。[] を使った索引指定は、配列要素にアクセスする便利な方法です。
ClickHouse では、配列の索引は常に 1 から始まる点を理解しておくことが重要です。
これは、配列の索引が 0 から始まる他のプログラミング言語とは異なる場合があります。
たとえば、配列がある場合、次のように記述すると最初の要素を選択できます。
配列関数
length、arrayEnumerate、indexOf、has* 関数
length 関数は、配列の要素数を返すために使用します。
arrayEnumerate 関数を使うと、要素の索引からなる配列を返すこともできます:
indexOf 関数を使用できます。
indexOfAssumeSorted 関数を使用できます。
関数 has、hasAll、hasAny は、配列に指定した値が含まれているかどうかを判定する際に便利です。
次の例を見てみましょう。
配列関数を使ったフライトデータの分析
groupArray
groupArray 集約関数を使用できます。これは各行の指定したカラムの値を取り出し、配列としてまとめるものです。
以下のクエリを実行して、どのように動作するかを確認しましょう。
上のクエリにある toStringCutToZero は、一部の空港の 3 文字コードの末尾に現れる null 文字を取り除くために使用しています。
データがこのフォーマットになっていれば、まとめた “Destinations” 配列の長さを調べることで、混雑する空港の順位を簡単に求められます。
arrayMap と arrayZip
arrayMap 関数はそのような高階関数の一例で、元の配列の各要素にラムダ関数を適用して、指定した配列から新しい配列を返します。
以下のクエリを実行すると、arrayMap 関数を使って、どの便が遅延し、どの便が定時だったかを確認できます。
出発地と到着地の組ごとに、各便の機体記号とステータスが表示されます。
上記のクエリでは、arrayMap 関数が単一要素の配列 [DepDelayMinutes] を受け取り、ラムダ関数 d -> if(d >= 30, 'DELAYED', if(d >= 15, 'WARNING', 'ON-TIME' を適用して分類します。
その後、結果の配列の最初の要素が [DepDelayMinutes][1] で取り出されます。
arrayZip 関数は、Tail_Number 配列と statuses 配列を 1 つの配列にまとめます。
arrayFilter
DEN、ATL、DFW について、30分以上遅延したフライト数のみを見てみましょう。
上のクエリでは、arrayFilter 関数の第1引数としてラムダ関数を渡しています。
このラムダ関数は、遅延時間 (分) を表す d を受け取り、条件を満たす場合は 1、そうでない場合は 0 を返します。
arraySort と arrayIntersect
arraySort 関数と arrayIntersect 関数を使って、米国の主要空港のどの組み合わせが最も多くの共通宛先に就航しているかを調べます。
arraySort は Array を受け取り、デフォルトでは要素を昇順にソートしますが、ソート順を定義するためにラムダ関数を渡すこともできます。
arrayIntersect は複数の Array を受け取り、すべての Array に共通して含まれる要素を持つ Array を返します。
以下のクエリを実行して、これら 2 つの配列関数の動作を確認してください。
このクエリは、大きく 2 つの段階で処理されます。
まず、Common Table Expression (CTE) を使って airport_routes という一時的な dataset を作成します。ここでは、2024 年 1 月 1 日のすべてのフライトを対象に、各出発空港について、その空港が就航している一意な宛先をソート済みのリストとしてまとめます。
たとえば airport_routes の result set では、DEN は ['ATL', 'BOS', 'LAX', 'MIA', ...] のように、その空港から就航しているすべての都市を含む Array を持つことになります。
第 2 段階では、米国の 5 つの主要ハブ空港 (DEN、ATL、DFW、ORD、LAS) を取り上げ、それらのすべての組み合わせを比較します。
これは cross join を使って行われ、これらの空港の全組み合わせを生成します。
次に、各組み合わせについて arrayIntersect 関数を使い、両方の空港のリストに含まれる共通の宛先を求めます。
length 関数は、それらの共通宛先の数を数えます。
条件 a1.Origin < a2.Origin によって、各組み合わせは 1 回だけ現れるようになります。
これがないと、JFK-LAX と LAX-JFK の両方が別々の結果として返されますが、同じ比較を表しているため冗長です。
最後に、このクエリは共通する宛先数が多い順に結果をソートし、上位 10 件だけを返します。
これにより、どの主要ハブ同士が最も重複した路線ネットワークを持っているかがわかります。これは、複数の航空会社が同じ都市ペアに就航している競争の激しい市場や、似た地理的エリアをカバーしていて、旅行者にとって代替の乗り継ぎ拠点になり得るハブを示している可能性があります。
arrayReduce
arrayReduce を使って、Denver International Airport 発の各路線について平均遅延と最大遅延を求めてみましょう。
上の例では、DEN から出発する各便について、arrayReduce を使って平均遅延と最大遅延を求めました。
arrayReduce は、関数の第 1 パラメータで指定した集約関数を、関数の第 2 パラメータで指定した配列の要素に適用します。
arrayJoin
arrayJoin 関数です。
arrayJoin は配列を「展開」し、各要素ごとに個別の行を作成します。
これは、他のデータベースにおける UNNEST や EXPLODE SQL 関数に似ています。
配列やスカラー値を返すほとんどの配列関数とは異なり、arrayJoin は行数を増やすことで結果セットを根本的に変化させます。
以下のクエリは、0 から 100 までを 10 刻みで並べた値の配列を返します。
この配列は、0 分、10 分、20 分といった遅延時間だと考えることができます。
arrayJoin を使うと、2 つの空港間でその分数以下の遅延が何件あったかを求めるクエリを書けます。
以下のクエリは、累積遅延バケットを使って、2024 年 1 月 1 日の Denver (DEN) から Miami (MIA) へのフライト遅延の分布を示すヒストグラムを作成します。
上のクエリでは、CTE 句 (WITH 句) を使って遅延の配列を返しています。
Destination は宛先コードを文字列に変換します。
arrayJoin を使って、遅延の配列を個別の行に展開します。
delay 配列の各値は、それぞれ別名 del を持つ独立した行になり、
del=0 の行、del=10 の行、del=20 の行、というように 10 行が得られます。
各遅延しきい値 (del) について、クエリは countIf(DepDelayMinutes >= del) を使って、
そのしきい値以上の遅延だったフライト数を数えます。
arrayJoin には、これに相当する SQL コマンド ARRAY JOIN もあります。
比較のため、上のクエリを同等の SQL コマンドで書き直したものを以下に示します。
次のステップ
groupArray、arrayFilter、arrayMap、arrayReduce、arrayJoin といった強力な関数まで学びました。
さらに学習を進めるには、配列関数の完全なリファレンスを参照して、arrayFlatten、arrayReverse、arrayDistinct などの追加の関数を確認してみてください。
また、配列と相性のよい関連データ構造として、タプル、JSON、Map 型について学ぶのもおすすめです。
これらの概念を自分のデータセットに適用して練習し、SQL playground やその他のサンプルデータセットでさまざまなクエリを試してみてください。
配列は ClickHouse の基本的な機能の 1 つであり、効率的な分析クエリを実現します。配列関数に慣れてくると、複雑な集計や時系列分析を大幅に簡素化できることがわかるでしょう。
配列をもっと楽しく学びたい方には、ClickHouse のデータエキスパートである Mark による以下の YouTube 動画がおすすめです。