ClickHouse/docs/ja/migrations/bigquery/migrating-to-clickhouse-cloud.md
2024-11-18 11:58:58 +09:00

35 KiB
Raw Blame History

title slug description keywords
BigQueryからClickHouse Cloudへの移行 /ja/migrations/bigquery/migrating-to-clickhouse-cloud BigQueryからClickHouse Cloudへのデータ移行方法
migrate
migration
migrating
data
etl
elt
bigquery

なぜBigQueryよりもClickHouse Cloudを選ぶのか

TL;DR: ClickHouseは、現代のデータ分析においてBigQueryよりも高速で、安価で、強力です。


<img src={require('../images/bigquery-2.png').default}
class="image" alt="NEEDS ALT" style={{width: '800px'}} />


BigQueryからClickHouse Cloudへのデータロード

データセット

BigQueryからClickHouse Cloudへの典型的な移行を示すためのサンプルデータセットとして、こちらで説明されているStack Overflowデータセットを使用します。これは、2008年から2024年4月までのStack Overflow上のすべてのpostvoteusercomment、およびbadgeを含んでいます。このデータのBigQueryスキーマは以下の通りです


<img src={require('../images/bigquery-3.png').default}
class="image" alt="NEEDS ALT" style={{width: '1000px'}} />


移行手順をテストするために、このデータセットをBigQueryインスタンスで構築したいユーザーのために、GCSバケットでParquet形式のデータを提供しており、BigQueryでのテーブル作成およびロード用のDDLコマンドはこちらにあります。

データ移行

BigQueryとClickHouse Cloud間のデータ移行には、主に2つのワークロードタイプがあります

  • 初回の一括ロードと定期的な更新 - 初回データセットの移行が必要で、その後は一定間隔での定期更新例えば、毎日が必要です。ここでの更新は、変更された行を再送信することで処理され、比較に使用できるカラム例えば、日付またはXMIN値によって識別されます。削除は、データセットの完全な定期的なリロードによって処理されます。
  • リアルタイムレプリケーションまたはCDC - 初回データセットの移行が必要です。このデータセットへの変更は、数秒の遅延のみが許容される状態で、ClickHouseにほぼリアルタイムで反映される必要があります。これは実質的にChange Data Capture (CDC)プロセスであり、BigQueryのテーブルはClickHouseと同期される必要があります、すなわちBigQueryテーブルでの挿入、更新および削除は、ClickHouse内の同等のテーブルに適用されます。

Google Cloud Storage (GCS)による一括ロード

BigQueryはGoogleのオブジェクトストアGCSへのデータエクスポートをサポートしています。我々のサンプルデータセットの場合

  1. 7つのテーブルをGCSにエクスポートします。そのためのコマンドはこちらにあります。

  2. データをClickHouse Cloudにインポートします。ここで、gcsテーブル関数を使用します。DDLとインポートクエリはこちらにあります。ClickHouse Cloudインスタンスが複数のコンピュートードから構成されているため、gcsテーブル関数の代わりに、gcsバケットでも動作しデータを並列でロードするすべてのードを利用するs3Clusterテーブル関数を使用しています。


<img src={require('../images/bigquery-4.png').default}
class="image" alt="NEEDS ALT" style={{width: '600px'}} />


このアプローチには以下の利点があります:

  • BigQueryエクスポート機能は、データサブセットのエクスポートをフィルタリングするためのサポートが付いています。
  • BigQueryはParquet, Avro, JSON, CSV形式およびいくつかの圧縮タイプへのエクスポートをサポートしており、ClickHouseはこれらすべてをサポートしています。
  • GCSはオブジェクトライフサイクル管理をサポートしており、指定した期間後にClickHouseにエクスポートおよびインポートされたデータを削除できます。
  • Googleは1日50TBまでGCSへのエクスポートを無料で許可しています。ユーザーはGCSストレージのみに支払いを行います。
  • エクスポートは自動的に複数のファイルを生成し、テーブルデータを最大1GBに制限します。これにより、Importを並列化することができます。

以下の例を試す前に、ユーザーはエクスポートに必要な権限およびエクスポートとインポートのパフォーマンスを最大化するための地域推薦をレビューすることをお勧めします。

スケジュールされたクエリによるリアルタイムレプリケーションまたはCDC

Change Data Capture (CDC) は、2つのデータベース間でテーブルを同期させておくプロセスです。更新や削除をほぼリアルタイムで処理する場合、これは非常に複雑です。1つのアプローチは、BigQueryのスケジュールされクエリ機能を使って単純に定期的なエクスポートをスケジュールすることです。ClickHouseへのデータ挿入に若干の遅延を許容できるなら、このアプローチは実装が容易で維持が簡単です。このブログ投稿では、一例が示されています。

スキーマの設計

Stack Overflowデータセットには多数の関連テーブルが含まれています。まず主となるテーブルの移行に集中することをお勧めします。これが必ずしも最大のテーブルではなく、最も分析クエリを受けると予想されるテーブルかもしれません。これはClickHouseの主要な概念に精通するための手段となるでしょう。このテーブルは、ClickHouseの特性を最大限利用し、最適なパフォーマンスを得るためにさらなるテーブルを追加する際に再モデリングが必要になるかもしれません。このモデリングプロセスはデータモデリングドキュメントで探求しています。

この原則に従い、主なpostsテーブルに焦点を当てます。このためのBigQueryスキーマは以下の通りです

CREATE TABLE stackoverflow.posts (
    id INTEGER,
    posttypeid INTEGER,
    acceptedanswerid STRING,
    creationdate TIMESTAMP,
    score INTEGER,
    viewcount INTEGER,
    body STRING,
    owneruserid INTEGER,
    ownerdisplayname STRING,
    lasteditoruserid STRING,
    lasteditordisplayname STRING,
    lasteditdate TIMESTAMP,
    lastactivitydate TIMESTAMP,
    title STRING,
    tags STRING,
    answercount INTEGER,
    commentcount INTEGER,
    favoritecount INTEGER,
    conentlicense STRING,
    parentid STRING,
    communityowneddate TIMESTAMP,
    closeddate TIMESTAMP
);

型の最適化

このプロセスを適用すると、以下のスキーマが得られます:

CREATE TABLE stackoverflow.posts
(
   `Id` Int32,
   `PostTypeId` Enum('Question' = 1, 'Answer' = 2, 'Wiki' = 3, 'TagWikiExcerpt' = 4, 'TagWiki' = 5, 'ModeratorNomination' = 6, 'WikiPlaceholder' = 7, 'PrivilegeWiki' = 8),
   `AcceptedAnswerId` UInt32,
   `CreationDate` DateTime,
   `Score` Int32,
   `ViewCount` UInt32,
   `Body` String,
   `OwnerUserId` Int32,
   `OwnerDisplayName` String,
   `LastEditorUserId` Int32,
   `LastEditorDisplayName` String,
   `LastEditDate` DateTime,
   `LastActivityDate` DateTime,
   `Title` String,
   `Tags` String,
   `AnswerCount` UInt16,
   `CommentCount` UInt8,
   `FavoriteCount` UInt8,
   `ContentLicense` LowCardinality(String),
   `ParentId` String,
   `CommunityOwnedDate` DateTime,
   `ClosedDate` DateTime
)
ENGINE = MergeTree
ORDER BY tuple()
COMMENT 'Optimized types'

このテーブルには、GCSからエクスポートされたデータをINSERT INTO SELECTを使って簡単に投入できます。ClickHouse Cloud上では、gcs互換のs3Clusterテーブル関数を使って、複数ノード経由でのロードを並列化することも可能です:

INSERT INTO stackoverflow.posts SELECT * FROM gcs( 'gs://clickhouse-public-datasets/stackoverflow/parquet/posts/*.parquet', NOSIGN);

新しいスキーマにnullは保持されていません。上記のインサートは、これらをそれぞれのタイプに対するデフォルト値に自動的に変換します- 整数の場合は0、文字列の場合は空の値です。ClickHouseは、数値を自動的に対象精度に変換します。

ClickHouseの主キーはどのように異なるのか

こちらで説明されているように、BigQueryと同様に、ClickHouseはテーブルの主キーのカラム値の一意性を強制しません。

BigQueryのクラスタリングと似ていて、ClickHouseテーブルのデータは主キーのカラムによりディスク上で並べ替えられて保存されます。このソート順序は、クエリオプティマイザによってソートを回避し、結合のメモリ使用を最小化し、リミット句のショートカットを可能にします。 BigQueryとは対照的に、ClickHouseは主キーのカラム値に基づいて(スパースな)主インデックスを自動的に作成します。このインデックスは主キーのカラム上でフィルタがあるすべてのクエリを高速化するために利用されます。具体的には:

  • メモリとディスクの効率性は、ClickHouseがしばしば使用されるスケールの重要な要素です。データは部分と呼ばれるチャンクに分かれてClickHouseテーブルに書き込まれ、バックグラウンドで結合のルールが適用されます。ClickHouseでは、各部分は自分自身の主インデックスを持ちます。部分が結合されるとき、結合した部分の主インデックスも結合されます。これらのインデックスは各行ごとに構築されていません。代わりに、部分の主インデックスは行のグループごとに1つのインデックスエントリを持ちます- これはスパースインデキシングと呼ばれる技術です。
  • スパースインデキシングは、ClickHouseが指定されたキーによってディスク上の部分を並べて保存するため可能です。単一行を直接特定するB-Treeベースのインデックスのようにのではなく、スパース主インデックスはクエリに一致する可能性のある行のグループをすばやくバイナリ検索によって特定します。一致する可能性のある行のグループは並列でClickHouseエンジンにストリームされ、マッチを見つけます。このインデックス設計により、主インデックスが小さく主メモリに完全に収まるなりつつ、特にデータ分析の使用例で典型的な範囲クエリの実行時間を大幅に短縮します。詳しい情報については、この詳細ガイドをお勧めします。

<img src={require('../images/bigquery-5.png').default}
class="image" alt="NEEDS ALT" style={{width: '800px'}} />


選択されたClickHouseの主キーは、インデックスだけでなく、データのディスクへの書き込み順序も決定します。このため、これは圧縮レベルに大きな影響を与える可能性があります。そして、それはクエリパフォーマンスに影響を与えることができます。すべてのカラムは指定された順序キーの値に基づいてソートされます。例えばCreationDateがキーとして使われる場合、すべての他のカラムの値の順序はCreationDateカラムの値の順序に対応します。複数の順序キーを指定することができます - これはSELECTクエリのORDER BY句と同じセマンティクスで順序付けされます。

順序キーの選択

順序キーの選択における考慮事項とステップについて、例としてpostsテーブルを使用した場合はこちらを参照してください。

データモデリング技術

BigQueryから移行するユーザーには、ClickHouseでのデータモデリングガイドを読むことをお勧めします。このガイドは同じStack Overflowデータセットを使用し、ClickHouseの機能を使用した複数のアプローチを探ります。

パーティション

BigQueryのユーザーは、テーブルを小さく管理しやすいパーツパーティションに分割することで、大規模なデータベースのパフォーマンスと管理を向上させる方法であるテーブルパーティションの概念に親しんでいるでしょう。このパーティションは、指定されたカラムの範囲日付、定義されたリスト、またはキーに基づくハッシュによって達成されます。これにより、管理者は特定の基準日付範囲や地理的な位置に基づいてデータを整理できます。

パーティション化は、パーティションプルーニングを通じて迅速なデータアクセスと効率的なインデックス作成を実現し、クエリ性能を向上させます。また、バックアップやデータ削除などの保守タスクを個別のパーティション上で実行できるようにし、テーブル全体に対して操作せずに済むようにします。それにより、パーティション化は複数のパーティション間で負荷を分散することで、BigQueryデータベースのスケーラビリティを大幅に向上させます。

ClickHouseでは、パーティション化は最初に表を定義する際にPARTITION BY句によって指定されます。この句は、任意のカラムのSQL式を含むことができ、この結果によって行がどのパーティションに送られるかが定義されます。


<img src={require('../images/bigquery-6.png').default}
class="image" alt="NEEDS ALT" style={{width: '800px'}} />


データパーツはディスク上で各パーティションに論理的に関連付き、単独でクエリを実行できます。以下の例では、toYear(CreationDate)を使用して年ごとに投稿テーブルをパーティション化しています。行がClickHouseに挿入されるとき、この式は各行に対して評価され、クエリ中に結果パーティションにルーティングされます。

CREATE TABLE posts
(
	`Id` Int32 CODEC(Delta(4), ZSTD(1)),
	`PostTypeId` Enum8('Question' = 1, 'Answer' = 2, 'Wiki' = 3, 'TagWikiExcerpt' = 4, 'TagWiki' = 5, 'ModeratorNomination' = 6, 'WikiPlaceholder' = 7, 'PrivilegeWiki' = 8),
	`AcceptedAnswerId` UInt32,
	`CreationDate` DateTime64(3, 'UTC'),
...
	`ClosedDate` DateTime64(3, 'UTC')
)
ENGINE = MergeTree
ORDER BY (PostTypeId, toDate(CreationDate), CreationDate)
PARTITION BY toYear(CreationDate)

アプリケーション

ClickHouseのパーティション化は、BigQueryにおける同様のアプリケーションを持っていますが、いくつかの微妙な違いがあります。具体的には

  • データ管理 - ClickHouseでは、ユーザーは主にパーティションをデータ管理機能と見なすべきです。データを特定のキーに基づいて論理的に分割することで、各パーティションに個別に操作を加えられるようになります。これにより、ユーザーはパーティションを権利により迅速に移動したり、ストレージティアにデータを動かしたりするなどの効率的な操作が可能になります。例として、古いポストを削除する場合:
SELECT DISTINCT partition
FROM system.parts
WHERE `table` = 'posts'

┌─partition─┐
 2008  	
 2009  	
 2010  	
 2011  	
 2012  	
 2013  	
 2014  	
 2015  	
 2016  	
 2017  	
 2018  	
 2019  	
 2020  	
 2021  	
 2022  	
 2023  	
 2024  	
└───────────┘

17 rows in set. Elapsed: 0.002 sec.
	
	ALTER TABLE posts
	(DROP PARTITION '2008')

Ok.

0 rows in set. Elapsed: 0.103 sec.
  • クエリ最適化 - パーティションはクエリ性能に役立つ可能性がありますが、これがどの程度の利点をもたらすかはアクセスパターンに大きく依存します。クエリが少数のパーティション(理想的にはひとつ)に限定されている場合、パフォーマンスが向上する可能性があります。これは、通常、主キーにないパーティションキーでフィルタリングする場合に役立ちます。しかし、多くのパーティションをカバーするクエリは、パーティションが使用されない場合よりもクエリ性能が低下する可能性があります(パーティションによって可能な部分が増加するため)。パーティションを利用してGROUP BYクエリを最適化することができますが、これは各パーティション内の値がユニークである場合のみ効果的です。しかし、一般的には主キーが最適化されていることを確認し、アクセスパターンが特定の予測可能なサブセットに絞られている特別な場合にのみクエリ最適化技法としてパーティション化を検討すべきです、例えば日付でパーティション化し、最近の日付のクエリが多い場合に有用です。

推奨事項

パーティション化はデータ管理技術であると考えるべきです。これは、時系列データの操作時、例えば最も古いパーティションを単に削除するといった方法でクラスタからデータを削除する必要がある場合に理想的です。

重要パーティションキーの式が高いカーディナリティのセットを生成しないようにしてください、すなわち100以上のパーティションの作成は避けるべきです。例えば、クライアント識別子や名前といった高カーディナリティカラムでデータをパーティション化しないでください。代わりに、クライアント識別子や名前をORDER BY式の最初のカラムにします。

内部的に、ClickHouseは挿入されたデータの部分を作成します。より多くのデータが挿入されると、部分の数が増加します。部分の数が指定された制限を超えた場合、ClickHouseは"too many parts"エラーとして設定されたルールに従って挿入時に例外を投げます。これは通常の操作下では発生しません。ClickHouseが不適切に設定されている場合や間違って使用されている場合多くの小さい挿入だけです。部分はパーティションごとに分離して作成されるため、パーティション数を増やすことは部分の数も増加させます。高カーディナリティのパーティションキーはこのエラーを引き起こす可能性があるので避けるべきです。

Materialized ViewとProjectionの違い

ClickHouseのプロジェクションの概念により、ユーザーはテーブルの複数のORDER BY句を指定できます。

ClickHouseのデータモデリングでは、ClickHouseにおいて集計を事前計算するためにMaterialized Viewを使用し、行を変換し、異なるアクセスパターンのクエリを最適化する方法について探ります。後者については、Materialized Viewが行を異なる順序のターゲットテーブルに送信する場合の例を提供しました

例えば、次のクエリを考えてみてください:

SELECT avg(Score)
FROM comments
WHERE UserId = 8592047

   ┌──────────avg(Score)─┐
    0.18181818181818182  
   └────────────────────┘

1 row in set. Elapsed: 0.040 sec. Processed 90.38 million rows, 361.59 MB (2.25 billion rows/s., 9.01 GB/s.)
Peak memory usage: 201.93 MiB.

このクエリはUserIdが順序キーではないため、すべての90m行をスキャンする必要があります。以前は、リッチアップ用のMaterialized Viewを使ってPostIdのLOOKUPを提供しました。プロジェクションを用いても同じ問題を解決できます。以下のコマンドはORDER BY user_idのプロジェクションを追加します。

ALTER TABLE comments ADD PROJECTION comments_user_id (
SELECT * ORDER BY UserId
)

ALTER TABLE comments MATERIALIZE PROJECTION comments_user_id

最初にプロジェクションを作成し、その後にマテリアライズしなければなりません。この後者のコマンドはデータを2回異なる順序でディスクに保存させます。プロジェクションは、以下に示すようにデータが作成されるときに定義することもでき、挿入されるデータに応じて自動的に保守されます。

CREATE TABLE comments
(
	`Id` UInt32,
	`PostId` UInt32,
	`Score` UInt16,
	`Text` String,
	`CreationDate` DateTime64(3, 'UTC'),
	`UserId` Int32,
	`UserDisplayName` LowCardinality(String),
	PROJECTION comments_user_id
	(
    	SELECT *
    	ORDER BY UserId
	)
)
ENGINE = MergeTree
ORDER BY PostId

プロジェクションがALTERコマンドを介して作成される場合、MATERIALIZE PROJECTIONコマンドが発行されたときに作成は非同期です。ユーザーは次のクエリを使ってこの操作の進捗を確認し、is_done=1を待つことができます。

SELECT
	parts_to_do,
	is_done,
	latest_fail_reason
FROM system.mutations
WHERE (`table` = 'comments') AND (command LIKE '%MATERIALIZE%')

   ┌─parts_to_do─┬─is_done─┬─latest_fail_reason─┐
1.        	1    	0                 	
   └─────────────┴─────────┴────────────────────┘

1 row in set. Elapsed: 0.003 sec.

このクエリを繰り返すと、追加のストレージを使用する代わりにパフォーマンスが大幅に向上していることが確認できます。

SELECT avg(Score)
FROM comments
WHERE UserId = 8592047

   ┌──────────avg(Score)─┐
1.  0.18181818181818182 
   └────────────────────┘

1 row in set. Elapsed: 0.008 sec. Processed 16.36 thousand rows, 98.17 KB (2.15 million rows/s., 12.92 MB/s.)
Peak memory usage: 4.06 MiB.

EXPLAINコマンドを使用して、このクエリにプロジェクションが使用されたことを確認することもできます:

EXPLAIN indexes = 1
SELECT avg(Score)
FROM comments
WHERE UserId = 8592047

	┌─explain─────────────────────────────────────────────┐
 1.  Expression ((Projection + Before ORDER BY))     	
 2.    Aggregating                                   	
 3.  	Filter                                      	
 4.    	ReadFromMergeTree (comments_user_id)      	
 5.    	Indexes:                                  	
 6.      	PrimaryKey                              	
 7.        	Keys:                                 	
 8.          	UserId                              	
 9.        	Condition: (UserId in [8592047, 8592047]) 
10.        	Parts: 2/2                            	
11.        	Granules: 2/11360                     	
	└─────────────────────────────────────────────────────┘

11 rows in set. Elapsed: 0.004 sec.

プロジェクションの使用時期

プロジェクションは自動的に保守されるため、新しいユーザーには魅力的です。さらに、一つのテーブルに単にクエリを送信し、プロジェクションによって可能な場合に応じて応答時間を短縮できます。


<img src={require('../images/bigquery-7.png').default}
class="image" alt="NEEDS ALT" style={{width: '800px'}} />


これは、ユーザーが適切な最適化済ターゲットテーブルを選ぶか、フィルタに応じてクエリを書き換える必要があるMaterialized Viewとは対照的です。これにより、ユーザーアプリケーションにより大きな重点が置かれ、クライアント側の複雑さが増します。

これらの利点にもかかわらず、プロジェクションにはいくつかの制約があり、ユーザーはこれを認識し、慎重に展開するべきです:

  • プロジェクションは、元のテーブルと異なる有効期限TTLを使用することはできません。Materialized viewで異なるTTLを使用することができます。
  • プロジェクションは、(サードテーブル)において現在optimize_read_in_orderをサポートしていません。
  • プロジェクションを持つテーブルには論理更新や削除がサポートされていません。
  • Materialized viewはチェーン可能あるMaterialized viewのターゲットテーブルが別のMaterialized viewのソーステーブルになることができ、さらに続けることができます。プロジェクションではこれは不可能です。
  • プロジェクションはジョインをサポートしていませんが、Materialized viewはサポートしています。
  • プロジェクションはフィルタ(WHEREをサポートしていませんが、Materialized viewはサポートしています。

プロジェクションを使うことをお勧めする場合:

  • データの完全な並べ替えが必要です。プロジェクション内の式は理論的にはGROUP BYを用いることができますが、Materialized viewの方が集計を保守するにはより効果的です。クエリオプティマイザも単純な並べ替えを使用するプロジェクションの方がSELECT * ORDER BY xによるものとして利用しやすくなっています。ユーザーはストレージフットプリントを削減するために、この式で一部のカラムを選択することができます。
  • データを2回書き込むことによるストレージフットプリントの拡大、およびオーバーヘッドに慣れている。挿入速度への影響をテストし、ストレージオーバーヘッドを評価します。

BigQueryクエリのClickHouseへの書き換え

以下は、BigQueryとClickHouseを比較するクエリの例を提供します。このリストは、ClickHouseの機能を利用してクエリを大幅に簡素化する方法を示しています。以下の例は、Stack Overflowデータセット2024年4月までを使用しています。

10個以上の質問を持つユーザーで最も多くビューを得たもの

BigQuery

<img src={require('../images/bigquery-8.png').default}
class="image" alt="NEEDS ALT" style={{width: '500px'}} />


ClickHouse

SELECT
    OwnerDisplayName,
    sum(ViewCount) AS total_views
FROM stackoverflow.posts
WHERE (PostTypeId = 'Question') AND (OwnerDisplayName != '')
GROUP BY OwnerDisplayName
HAVING count() > 10
ORDER BY total_views DESC
LIMIT 5

   ┌─OwnerDisplayName─┬─total_views─┐
1.  Joan Venge           25520387 
2.  Ray Vega             21576470 
3.  anon                 19814224 
4.  Tim                  19028260 
5.  John                 17638812 
   └──────────────────┴─────────────┘

5 rows in set. Elapsed: 0.076 sec. Processed 24.35 million rows, 140.21 MB (320.82 million rows/s., 1.85 GB/s.)
Peak memory usage: 323.37 MiB.

最も多くのビューを受けたタグ:

BigQuery


<img src={require('../images/bigquery-9.png').default}
class="image" alt="NEEDS ALT" style={{width: '400px'}} />


ClickHouse

-- ClickHouse
SELECT
    arrayJoin(arrayFilter(t -> (t != ''), splitByChar('|', Tags))) AS tags,
    sum(ViewCount) AS views
FROM stackoverflow.posts
GROUP BY tags
ORDER BY views DESC
LIMIT 5


   ┌─tags───────┬──────views─┐
1.  javascript  8190916894 
2.  python      8175132834 
3.  java        7258379211 
4.  c#          5476932513 
5.  android     4258320338 
   └────────────┴────────────┘

5 rows in set. Elapsed: 0.318 sec. Processed 59.82 million rows, 1.45 GB (188.01 million rows/s., 4.54 GB/s.)
Peak memory usage: 567.41 MiB.

集計関数

可能な限り、ユーザーはClickHouseの集計関数を利用すべきです。以下では、各年で最もビューの多い質問を計算するためにargMax関数を使用する例を示しています。

BigQuery


<img src={require('../images/bigquery-10.png').default}
class="image" alt="NEEDS ALT" style={{width: '500px'}} />


<img src={require('../images/bigquery-11.png').default}
class="image" alt="NEEDS ALT" style={{width: '500px'}} />


ClickHouse

-- ClickHouse
SELECT
    toYear(CreationDate) AS Year,
    argMax(Title, ViewCount) AS MostViewedQuestionTitle,
    max(ViewCount) AS MaxViewCount
FROM stackoverflow.posts
WHERE PostTypeId = 'Question'
GROUP BY Year
ORDER BY Year ASC
FORMAT Vertical


Row 1:
──────
Year:                    2008
MostViewedQuestionTitle: How to find the index for a given item in a list?
MaxViewCount:            6316987

Row 2:
──────
Year:                    2009
MostViewedQuestionTitle: How do I undo the most recent local commits in Git?
MaxViewCount:            13962748



Row 16:
───────
Year:                    2023
MostViewedQuestionTitle: How do I solve "error: externally-managed-environment" every time I use pip 3?
MaxViewCount:            506822

Row 17:
───────
Year:                    2024
MostViewedQuestionTitle: Warning "Third-party cookie will be blocked. Learn more in the Issues tab"
MaxViewCount:            66975

17 rows in set. Elapsed: 0.225 sec. Processed 24.35 million rows, 1.86 GB (107.99 million rows/s., 8.26 GB/s.)
Peak memory usage: 377.26 MiB.

条件式と配列

条件式と配列関数はクエリを大幅に簡素化します。次のクエリは、2022年から2023年にかけて最も割合増加したタグ10000件以上の出現数があるものを計算します。次のClickHouseクエリは、条件式、配列関数、およびHAVINGSELECT句でのエイリアス再利用の能力により簡潔です。

BigQuery


<img src={require('../images/bigquery-12.png').default}
class="image" alt="NEEDS ALT" style={{width: '500px'}} />


ClickHouse

SELECT
    arrayJoin(arrayFilter(t -> (t != ''), splitByChar('|', Tags))) AS tag,
    countIf(toYear(CreationDate) = 2023) AS count_2023,
    countIf(toYear(CreationDate) = 2022) AS count_2022,
    ((count_2023 - count_2022) / count_2022) * 100 AS percent_change
FROM stackoverflow.posts
WHERE toYear(CreationDate) IN (2022, 2023)
GROUP BY tag
HAVING (count_2022 > 10000) AND (count_2023 > 10000)
ORDER BY percent_change DESC
LIMIT 5

┌─tag─────────┬─count_2023─┬─count_2022─┬──────percent_change─┐
 next.js      13788      10520    31.06463878326996 
 spring-boot      16573      17721   -6.478189718413183 
 .net         11458      12968  -11.644046884639112 
 azure        11996      14049  -14.613139725247349 
 docker       13885      16877   -17.72826924216389 
└─────────────┴────────────┴────────────┴─────────────────────┘

5 rows in set. Elapsed: 0.096 sec. Processed 5.08 million rows, 155.73 MB (53.10 million rows/s., 1.63 GB/s.)
Peak memory usage: 410.37 MiB.

これで、BigQueryからClickHouseへのユーザー移行用の基本ガイドが終了します。BigQueryからの移行を行うユーザーには、ClickHouseでのデータモデリングガイドを読み、高度なClickHouseの機能を学ぶことをお勧めします。