このセクションでは、ClickHouse の SQL 構文について見ていきます。 ClickHouse は SQL ベースの構文を採用していますが、さまざまな拡張機能や最適化も提供しています。
クエリのパース
ClickHouse には 2 種類のパーサーがあります。
- 完全な SQL パーサー (再帰下降パーサー) 。
- データフォーマットパーサー (高速なストリームパーサー) 。
完全な SQL パーサーは、両方のパーサーを使用する INSERT クエリを除き、すべてのケースで使われます。
以下のクエリを見てみましょう。
INSERT INTO t VALUES (1, 'Hello, world'), (2, 'abc'), (3, 'def')すでに述べたように、INSERTクエリでは 2 種類のパーサーの両方が使われます。
INSERT INTO t VALUES の部分は完全パーサーでパースされ、
データ (1, 'Hello, world'), (2, 'abc'), (3, 'def') はデータフォーマットパーサー、つまり高速ストリームパーサーでパースされます。
完全パーサーを有効にする
input_format_values_interpret_expressions 設定を使うと、
データに対して完全パーサーを有効にすることもできます。
前述の設定が 1 に設定されている場合、
ClickHouse はまず高速ストリームパーサーで値をパースしようとします。
それに失敗すると、ClickHouse はデータを SQL の 式 として扱い、完全パーサーでパースしようとします。
データには任意のフォーマットを使用できます。
クエリを受信すると、server はリクエストのうち max_query_size バイトを超えない部分だけを RAM 上で処理し
(デフォルトでは 1 MB) 、残りはストリームとしてパースされます。
これは、大きな INSERTクエリに関する問題を避けるためです。これは、ClickHouse にデータを挿入する推奨方法でもあります。
INSERTクエリで Values フォーマットを使用する場合、
データは SELECTクエリ内の式と同じようにパースされるように見えるかもしれませんが、実際にはそうではありません。
Values フォーマットは、はるかに制限が多くなっています。
この節の残りでは、完全パーサーについて説明します。
空白
- 構文要素の間には、任意の数の空白文字を入れられます (クエリの先頭と末尾を含む) 。
- 空白文字には、スペース、タブ、改行、CR、およびフォームフィードが含まれます。
ClickHouse は、SQL スタイルと C スタイルの両方のコメントをサポートしています。
- SQL スタイルのコメントは
--、#!、または#で始まり、行末まで続きます。--と#!の後のスペースは省略できます。 - C スタイルのコメント:
//(または/が 3 文字以上連続するもの) の後にテキストを記述し、行末までコメントとして扱われます。/の後にスペースは不要です。- 複数行コメントとして、
/*から*/まで記述することもできます。この場合もスペースは不要です。 - C スタイルのコメントはネストできます。
たとえば:
/*
* 2つの日付の間の日数を計算します。
* /* どちらかの引数がNULLの場合はNULLを返します */
*/
SELECT
dateDiff('day', toDate('2024-01-01'), toDate('2024-12-31')) AS days_in_year, -- 365
dateDiff('day', toDate('2020-01-01'), today()) AS days_since #! 2020年以降
///////////////////////////////////////////////////////////////////
# TODO: 時間/分のバリアントを追加するキーワード
ClickHouse のキーワードは、コンテキストに応じて 大文字と小文字を区別する 場合と 大文字と小文字を区別しない 場合があります。
キーワードが 大文字と小文字を区別しない のは、次に該当するものです。
- SQL 標準。たとえば、
SELECT、select、SeLeCtはいずれも有効です。 - 一般的な DBMS (MySQL や Postgres) の一部実装。たとえば、
DateTimeはdatetimeと同じです。
標準 SQL とは異なり、そのほかのすべてのキーワード (関数名を含む) は 大文字と小文字を区別します。
また、キーワードは予約語ではありません。 キーワードとして扱われるのは、対応するコンテキストにおいてのみです。 キーワードと同じ名前の 識別子 を使用する場合は、二重引用符またはバッククォートで囲んでください。
たとえば、次のクエリは、テーブル table_name に "FROM" という名前のカラムがある場合に有効です。
SELECT "FROM" FROM table_name識別子
識別子には、次のものがあります。
識別子は引用符で囲むことも囲まないこともできますが、推奨されるのは後者です。
引用符で囲まない識別子は、正規表現 ^[a-zA-Z_][0-9a-zA-Z_]*$ に一致する必要があり、キーワード と同じ名前にはできません。
有効な識別子と無効な識別子の例については、以下の表を参照してください。
| 有効な識別子 | 無効な識別子 |
|---|---|
xyz, _internal, Id_with_underscores_123_ |
1x, tom@gmail.com, äußerst_schön |
キーワードと同じ名前の識別子を使いたい場合や、識別子に他の記号を使いたい場合は、たとえば "id" や `id` のように、二重引用符またはバッククォートで囲んでください。
リテラル
ClickHouse では、リテラルとはクエリ内に直接記述される値のことです。 つまり、クエリの実行中に変化しない固定の値です。
リテラルには次の種類があります。
以下の各セクションで、それぞれについて詳しく見ていきます。
String
文字列リテラルはシングルクォートで囲む必要があります。ダブルクォートはサポートされていません。
エスケープは、次のいずれかの方法で行います。
- シングルクォート文字
'の場合に限り、直前にシングルクォートを付けて''としてエスケープする、または - 直前にバックスラッシュを付け、下の表に示すサポートされているエスケープシーケンスを使用する。
| サポートされるエスケープ | 説明 |
|---|---|
\xHH |
任意個の16進数桁 (H) が続く 8 ビット文字指定。 |
\N |
予約済みで、何もしません (例: SELECT 'a\Nb' は ab を返します) |
\a |
ベル |
\b |
バックスペース |
\e |
エスケープ文字 |
\f |
改ページ |
\n |
改行文字 |
\r |
復帰 |
\t |
水平タブ |
\v |
垂直タブ |
\0 |
null 文字 |
\\ |
バックスラッシュ |
\' (or '') |
シングルクォート |
\" |
ダブルクォート |
` |
バッククォート |
\/ |
スラッシュ |
\= |
等号 |
| ASCII control characters (c <= 31). |
Numeric
数値リテラルは、次のようにパースされます。
- リテラルの先頭にマイナス記号
-が付いている場合、そのトークンはいったん読み飛ばされ、パース後に結果が負の値にされます。 - 数値リテラルはまず、strtoull 関数を使って 64 ビット符号なし整数としてパースされます。
- 値の先頭に
0bまたは0x/0Xが付いている場合、数値はそれぞれ 2 進数または 16 進数としてパースされます。 - 値が負で、その絶対値が 263 を超える場合は、エラーが返されます。
- 値の先頭に
- これに失敗した場合、次に strtod 関数を使って浮動小数点数としてパースされます。
- それ以外の場合は、エラーが返されます。
リテラル値は、その値が収まる最小の型に CAST されます。 例:
1はUInt8としてパースされます256はUInt16としてパースされます。
詳細は、データ型 を参照してください。
数値リテラル内のアンダースコア _ は無視され、可読性を高めるために使えます。
次の数値リテラルがサポートされています。
| 数値リテラル | 例 |
|---|---|
| 整数 | 1, 10_000_000, 18446744073709551615, 01 |
| 10 進数 | 0.1 |
| 指数表記 | 1e100, -1e-100 |
| 浮動小数点数 | 123.456, inf, nan |
| 16 進数 | 0xc0fe |
| SQL 標準互換の 16 進文字列 | x'c0fe' |
| 2 進数 | 0b1101 |
| SQL 標準互換の 2 進文字列 | b'1101' |
複合型
Array は [] で構築します: [1, 2, 3]。Tuple は () で構築します: (1, 'Hello, world!', 2)。
厳密には、これらはリテラルではなく、それぞれ Array 作成演算子および Tuple 作成演算子を使った式です。
Array は少なくとも 1 つの要素を含んでいる必要があり、Tuple は少なくとも 2 つの要素を含んでいる必要があります。
NULL
NULL は、値が存在しないことを示すために使用されます。
テーブルのフィールドに NULL を格納するには、そのフィールドが Nullable 型である必要があります。
ヒアドキュメント
ヒアドキュメント は、元のフォーマットを保ったまま文字列 (多くの場合、複数行) を定義する方法です。
ヒアドキュメント は、2 つの $ 記号の間に置くカスタム文字列リテラルとして定義します。
例えば:
SELECT $heredoc$SHOW CREATE VIEW my_view$heredoc$;
┌クエリパラメータの定義と使用
クエリパラメータを使用すると、具体的な識別子の代わりに抽象的なプレースホルダーを含む汎用的なクエリを記述できます。 クエリパラメータを含むクエリを実行すると、 すべてのプレースホルダーが解決され、実際のクエリパラメータの値に置き換えられます。
クエリパラメータは、いくつかの方法で定義できます。
SET param_<name>=<value>— クエリ内でSETコマンドを使用する方法。--param_<name>='<value>'— コマンドラインでclickhouse-clientに引数として渡す方法。param_<name>=<value>— HTTP インターフェイスの URL クエリ文字列パラメータとして指定する方法。
クエリパラメータは、クエリ内で {<name>: <datatype>} を使って参照できます。ここで、<name> はクエリパラメータ名、<datatype> は変換先のデータ型です。
SET コマンドの例
たとえば、次の SQL では a、b、c、d という名前のパラメータを定義しており、それぞれ異なるデータ型を持ちます。
SET param_a = 13;
SET param_b = 'str';
SET param_c = '2022-08-04 18:30:53';
SET param_d = {'10': [11, 12], '13': [14, 15]};
SELECT
{a: UInt32},
{b: String},
{c: DateTime},
{d: Map(String, Array(UInt8))};
13 str 2022-08-04 18:30:53 {'10':[11,12],'13':[14,15]}clickhouse-client の例
clickhouse-client を使用している場合、パラメータは --param_name=value として指定します。たとえば、次のパラメータの名前は message で、String として取得されます。
clickhouse-client --param_message='hello' --query="SELECT {message: String}"
helloクエリパラメータが database、table、function、またはその他の識別子の名前を表す場合は、型として Identifier を使用してください。たとえば、次のクエリは uk_price_paid という名前のテーブルから行を返します。
SET param_mytablename = "uk_price_paid";
SELECT * FROM {mytablename:Identifier};HTTP インターフェイスの例
クエリパラメータは、param_ プレフィックス付きの URL クエリ文字列パラメータとして渡せます。たとえば、次のようになります。
curl -s "http://localhost:8123/?param_message=hello" --data-binary "SELECT {message: String}"
helloWeb UI の例
組み込みの Web UI (play.html) は、クエリ内の {name:Type} 形式のパラメータプレースホルダーを自動的に検出し、各パラメータに対応するラベル付きの入力フィールドを表示します。パラメータ値は HTTP リクエストに含まれ、さらにブックマークや共有のためにページ URL にも保持されます。
関数
関数呼び出しは、識別子に () で囲んだ引数のリスト (空の場合もあります) を続ける形で記述します。
標準SQLとは異なり、引数のリストが空であっても括弧は必須です。
例:
now()以下もあります:
一部の集約関数では、括弧内に 2 つの引数リストを指定できます。例:
quantile (0.9)(x) これらの集約関数は「パラメトリック」関数と呼ばれ、 最初のリストにある引数は「パラメーター」と呼ばれます。
演算子
演算子は、クエリのパース時に、優先順位と結合規則を考慮して対応する関数に変換されます。
たとえば、次の式では
1 + 2 * 3 + 4に変換されます
plus(plus(1, multiply(2, 3)), 4)`データ型とデータベースのテーブルエンジン
CREATEクエリ内のデータ型とテーブルエンジンは、識別子や関数と同じように記述します。
つまり、括弧付きの引数リストを含むことも、含まないこともあります。
詳細については、以下のセクションを参照してください。
式
式には、次のいずれかを指定できます。
- 関数
- 識別子
- リテラル
- 演算子の適用
- 括弧で囲まれた式
- サブクエリ
- アスタリスク
また、別名を含めることもできます。
式のリストとは、1 つ以上の式をカンマで区切ったものです。 関数や演算子は、その引数として式を取ることもできます。
定数式とは、クエリ解析中、つまり実行前に結果が判明している式です。 たとえば、リテラルに対する式は定数式です。
式の別名
別名とは、クエリ内の式に付けるユーザー定義の名前です。
expr AS alias上記の構文の各部分について、以下で説明します。
| 構文の要素 | 説明 | Example | 注記 |
|---|---|---|---|
AS |
別名を定義するためのキーワードです。AS キーワードを使わなくても、SELECT 句でテーブル名またはカラム名に別名を付けることができます。 |
SELECT table_name_alias.column_name FROM table_name table_name_alias. |
CAST 関数では、AS キーワードは別の意味で使われます。関数の説明を参照してください。 |
expr |
ClickHouse でサポートされる任意の式です。 | SELECT column_name * 2 AS double FROM some_table |
|
alias |
expr に付ける名前です。別名は identifiers の構文に従う必要があります。 |
SELECT "table t".column_name FROM table_name AS "table t". |
使用上の注意
- 別名はクエリまたはサブクエリ全体で有効で、任意の式に対する別名をクエリ内のどの部分でも定義できます。たとえば、次のようになります。
SELECT (1 AS n) + 2, n`.- 別名はサブクエリ内やサブクエリ同士の間では参照できません。たとえば、次のクエリを実行すると、ClickHouse は例外
Unknown identifier: numを返します。
`SELECT (SELECT sum(b.a) + num FROM b) - a.a AS num FROM a`- サブクエリの
SELECT句で結果のカラムに別名が定義されている場合、これらのカラムは外側のクエリから参照できます。たとえば:
SELECT n + m FROM (SELECT 1 AS n, 2 AS m)`.- カラム名やテーブル名と同じ別名を付ける場合は注意してください。次の例を見てみましょう。
CREATE TABLE t
(
a Int,
b Int
)
ENGINE = TinyLog();
SELECT
argMax(a, b),
sum(b) AS b
FROM t;
Received exception from server (version 18.14.17):
Code: 184. DB::Exception: Received from localhost:9000, 127.0.0.1. DB::Exception: Aggregate function sum(b) is found inside another aggregate function in query.前の例では、カラム b を持つテーブル t を宣言しました。
次に、データを選択する際に、sum(b) AS b という別名を定義しました。
別名はグローバルであるため、
ClickHouse は式 argMax(a, b) 内の b を式 sum(b) に置き換えました。
この置換によって例外が発生しました。
アスタリスク
SELECTクエリでは、アスタリスクを式の代わりに使用できます。
詳しくは、SELECTのセクションを参照してください。
コメント