- 厳密な型を使用する: カラムには常に正しい data type を選択してください。数値フィールドや日付フィールドには、汎用的な String type ではなく、適切な数値型や日付型を使うべきです。これにより、filter や集計において正しい意味が保たれます。
- nullable columns を避ける: Nullable なカラムは、null 値を追跡するための別カラムを維持する必要があるため、追加のオーバーヘッドが発生します。空の状態と null の状態を明示的に区別する必要がある場合にのみ Nullable を使用してください。それ以外では、通常はデフォルト値やゼロ相当の値で十分です。必要な場合を除いてこの型を避けるべき理由の詳細については、Avoid nullable Columns を参照してください。
- 数値の精度を最小限にする: 想定されるデータ範囲を収容できる範囲で、ビット幅が最小の数値型を選択してください。たとえば、負の値が不要で、範囲が 0–65535 に収まる場合は、Int32 より UInt16 を選択します。
- 日付と時刻の精度を最適化する: クエリ要件を満たす範囲で、できるだけ粒度の粗い日付型または日時型を選択してください。日付のみのフィールドには Date または Date32 を使用し、ミリ秒以下の精度が不可欠でない限り、DateTime64 ではなく DateTime を優先してください。
- LowCardinality と特化型を活用する: 一意な値が概ね 10,000 未満のカラムでは、LowCardinality 型を使用すると、dictionary encoding によってストレージを大幅に削減できます。同様に、FixedString はカラム値が厳密に固定長の文字列である場合 (たとえば国コードや通貨コード) にのみ使用し、取り得る値の集合が有限なカラムには Enum 型を使うことで、効率的な保存と組み込みのデータ検証を実現できます。
- データ検証のための Enums: Enum 型は、列挙型を効率的にエンコードするために使用できます。Enums は、格納する必要がある一意な値の数に応じて、8 ビットまたは 16 ビットのいずれかになります。insert time の検証 (宣言されていない値は拒否されます) が必要な場合や、Enum 値の自然な順序を活用するクエリを実行したい場合は、これを検討してください。たとえば、ユーザーの応答を含むフィードバックカラムに Enum(’:(’ = 1, ’:|’ = 2, ’:)’ = 3) を使うケースが考えられます。
例
DESCRIBE コマンドでシンプルなスキーマ推論を実行すると、最適化前の初期スキーマを取得できます。
デフォルトでは、ClickHouse はこれらを対応する Nullable 型として扱います。これは、スキーマが行の一部のサンプルのみに基づいているためです。
以下では、
stackoverflow/parquet/posts フォルダ内のすべてのファイルを読み込むために、glob パターン *.parquet を使用します。| カラム | 数値型 | 最小値、最大値 | 一意な値 | NULL | コメント | 最適化型 |
|---|---|---|---|---|---|---|
PostTypeId | はい | 1, 8 | 8 | なし | Enum('Question' = 1, 'Answer' = 2, 'Wiki' = 3, 'TagWikiExcerpt' = 4, 'TagWiki' = 5, 'ModeratorNomination' = 6, 'WikiPlaceholder' = 7, 'PrivilegeWiki' = 8) | |
AcceptedAnswerId | あり | 0, 78285170 | 12282094 | あり | NULLと値0を区別する | UInt32 |
CreationDate | いいえ | 2008-07-31 21:42:52.667000000, 2024-03-31 23:59:17.697000000 | * | いいえ | ミリ秒単位の精度は不要なため、DateTime を使用 | DateTime |
Score | はい | -217, 34970 | 3236 | いいえ | Int32 | |
ViewCount | はい | 2, 13962748 | 170867 | いいえ | UInt32 | |
Body | いいえ | - | * | いいえ | String | |
OwnerUserId | はい | -1, 4056915 | 6256237 | はい | Int32 | |
OwnerDisplayName | いいえ | - | 181251 | はい | NULL は空文字列として扱う | String |
LastEditorUserId | はい | -1, 9999993 | 1104694 | あり | 0 は未使用の値のため、NULL に使用できます | Int32 |
LastEditorDisplayName | いいえ | * | 70952 | はい | NULL は空文字列として扱います。LowCardinality を試しましたが、利点はありませんでした | String |
LastEditDate | いいえ | 2008-08-01 13:24:35.051000000, 2024-04-06 21:01:22.697000000 | - | いいえ | ミリ秒精度は不要なため、DateTime を使用 | DateTime |
LastActivityDate | いいえ | 2008-08-01 12:19:17.417000000, 2024-04-06 21:01:22.697000000 | * | いいえ | ミリ秒の粒度は不要なため、DateTimeを使用 | DateTime |
Title | いいえ | - | * | いいえ | NULL は空文字列として扱う | String |
タグ | No | - | * | No | NULL は空文字列として扱う | String |
AnswerCount | はい | 0, 518 | 216 | いいえ | NULL と 0 を同一として扱う | UInt16 |
CommentCount | はい | 0, 135 | 100 | いいえ | NULL と 0 は同じとみなす | UInt8 |
FavoriteCount | はい | 0, 225 | 6 | はい | NULL と 0 を同じものとみなす | UInt8 |
ContentLicense | いいえ | - | 3 | いいえ | LowCardinality は FixedString よりも高性能 | LowCardinality(String) |
ParentId | いいえ | * | 20696028 | はい | NULLを空文字列として扱う | String |
CommunityOwnedDate | いいえ | 2008-08-12 04:59:35.017000000, 2024-04-01 05:36:41.380000000 | - | はい | NULL値にはデフォルト値として1970-01-01を検討してください。ミリ秒単位の粒度は不要なため、DateTimeを使用してください | DateTime |
ClosedDate | いいえ | 2008-09-04 20:56:44, 2024-04-06 18:49:25.393000000 | * | はい | NULL にはデフォルト値として 1970-01-01 を検討してください。ミリ秒単位の粒度は不要なため、DateTime を使用してください | DateTime |
ヒントカラムの型を見極めるには、その数値の範囲と一意の値の数を把握することが重要です。すべてのカラムの値の範囲と異なる値の数を調べるには、シンプルなクエリ
SELECT * APPLY min, * APPLY max, * APPLY uniq FROM table FORMAT Vertical を使用できます。ただし、これはコストが高くなる可能性があるため、より小さいデータのサブセットに対して実行することを推奨します。Nullable 型のカラムを避ける
Nullable カラム (例: Nullable(String)) は、別途 UInt8 型のカラムを作成します。ユーザーが Nullable カラムを扱うたびに、この追加カラムも毎回処理する必要があります。その結果、追加のストレージ容量が必要になり、ほとんどの場合はパフォーマンスに悪影響を及ぼします。
Nullable カラムを避けるには、そのカラムにデフォルト値を設定することを検討してください。たとえば、次のようにする代わりに: