Kusto 書くときに意識していること

## 良いクエリとは

Kusto において、良いクエリとは以下の２つに分類できると思います。
* 処理が高速
* 読みやすい（メンテナンスしやすい）

### 処理が高速とは

Kusto は主に何かの調査をするときに使うと思います。調査はなるべく早く終わらせて、次のアクションに移らなければならない、そんなときにクエリの実行に時間がかかっていては仕事が捗りません。なるべく高速に動くクエリを書くことが求められます。

### 読みやすいとは

クエリの管理を自分ひとりが行っていればよいですが、多くの場合、会社チームの複数メンバーで管理すると思います。そのクエリが何をするためのクエリなのか分かりづらいと、クエリのメンテナンスがしづらいです。自分以外の誰が見ても分かりやすいクエリを書くことが重要です。

## 処理が高速なクエリを書くには

Microsoft の公式ドキュメントに、[Kusto ベストプラクティス](https://docs.microsoft.com/ja-jp/azure/data-explorer/kusto/query/best-practices) があります。こちらを参考にしながらクエリを書くことがおすすめです。まずは、これに則った書き方ができれば、おおかたのスピードに関する問題は解決できると思います。

クエリのボリュームによりますが、私は普段、最低でも**1分以内**に結果を返してくれるクエリを書くことを意識しています。

where 句でなるべく検索ボリュームを減らす、contains ではなく has を使う、project で検索フィールドを限定してあげる、などは良く使う手法です。
以下にいくつか例をあげます。

### クエリ例１

クエリの検索ボリュームを減らすことはダイレクトに処理の高速化につながります。
以下のクエリは、過去３時間分のログを取得しています。過去どれくらいの期間のログを確認したいかおおよそのボリュームが把握できているのであれば、時間に関するフィールド（今回の場合 TimeGenerated）などで抽出可能なので、必ず行いましょう。

```
MyTable
| whete TimeGenerated > ago(3h)
```

### クエリ例２

表示するフィールドを限定することも処理の高速化につながります。
以下のクエリは、project で表示したいフィールドを限定しています。project を使用しない場合、検索したいテーブルに格納されているフィールドの種類が多ければ多いほど処理に時間がかかってしまいます。確認したいフィールドが決まっている場合はフィールドを指定するのがおすすめです。


```
MyTable
| project City, SessionId, StartTime, StopTime
```

## 読みやすいクエリを書くには

以下の２点が、読みやすいクエリを書くときに意識していることです。

* 定義する
* 分割する

Kusto において、 extend や let を使うことで、新たな変数や一連の処理を定義することができます。これらをうまく使うと、クエリを綺麗に整理でき読みやすくできます。

例を2つ示して、それぞれポイントを解説していこうと思います。

### クエリ例 1

少し処理が複雑なクエリですが、extend や let を使っている行に注目してみましょう。以下がポイントになるかと思います。
* クエリ内で何度も使う値は extend を使い、一つの変数にセットしています。（4,5,6行目）
* 1行目、２行目は時間を定義している部分ですが、ここは調査の都合によって確認したい時間帯を変更することがあるので、見やすい位置（最上部)に let で定義しています。

```
let _start = datetime(2018-09-24);
let _end = _start + 13d; 
Fruits 
| extend _bin = bin_at(Timestamp, 1d, _start)  
| extend _endRange = iif(_bin + 7d > _end, _end, 
                            iff( _bin + 7d - 1d < _start, _start,
                                iff( _bin + 7d - 1d < _bin, _bin,  _bin + 7d - 1d))) 
| extend _range = range(_bin, _endRange, 1d) 
| mv-expand _range to typeof(datetime) limit 1000000 
| summarize min(Price), max(Price), sum(Price) by Timestamp=bin_at(_range, 1d, _start) ,  Fruit 
| where Timestamp >= _start + 7d; 
```

クエリの引用元: [Kusto クエリのサンプル](https://docs.microsoft.com/ja-jp/azure/data-explorer/kusto/query/samples?pivots=azuredataexplorer)

よろしくないクエリの例として、８行目（_range を定義している部分）にて extend で定義しない場合以下のような形になります。
８行目と９行目で同じ変数 range(_bin, _endRange, 1d) が書かれてしまっているため、可読性や保守性に欠けるクエリになってしまいます

```
let _start = datetime(2018-09-24);
let _end = _start + 13d; 
Fruits 
| extend _bin = bin_at(Timestamp, 1d, _start)  
| extend _endRange = iif(_bin + 7d > _end, _end, 
                            iff( _bin + 7d - 1d < _start, _start,
                                iff( _bin + 7d - 1d < _bin, _bin,  _bin + 7d - 1d))) 
| mv-expand range(_bin, _endRange, 1d) to typeof(datetime) limit 1000000 
| summarize min(Price), max(Price), sum(Price) by Timestamp=bin_at(range(_bin, _endRange, 1d), 1d, _start) ,  Fruit 
| where Timestamp >= _start + 7d; 
```

[Kusto ベストプラクティス](https://docs.microsoft.com/ja-jp/azure/data-explorer/kusto/query/best-practices) にて解説されていますが、一度しか使用されない変数を extend で定義するのはクエリが複雑化する原因になります。その場合は、extend を使わずに１行で書いてしまいましょう。

> T | where predicate(expression) を使用します
> T | extend _value = expression | where predicate(_value) を使用しないでください

### クエリ例 2

このクエリのポイントは分割を上手く活用しているところです。
１行目で１連の処理（MyLogTableの処理）を定義して、2行目と４行目で使い回しています。
初めに定義しておく処理が複雑であればあるほど、分割の効果が大きく発揮されます。

```
let Events = MyLogTable | where ... ;
Events
| where Name == "Start"
| project Name, City, SessionId, StartTime=timestamp
| join (Events 
        | where Name="Stop"
        | project StopTime=timestamp, SessionId) 
    on SessionId
| project City, SessionId, StartTime, StopTime, Duration = StopTime - StartTime
```

クエリの引用元: [let ステートメント](https://docs.microsoft.com/ja-jp/azure/data-explorer/kusto/query/letstatement)

よろしくないクエリの例として、１行目で処理を定義しない場合以下のような形になります。
１行目と４行目で同じ処理（MyLogTable | where ...）が書かれてしまっているため、可読性や保守性に欠けるクエリになってしまいます。

```
MyLogTable | where ...
| where Name == "Start"
| project Name, City, SessionId, StartTime=timestamp
| join (MyLogTable | where ... 
        | where Name="Stop"
        | project StopTime=timestamp, SessionId) 
    on SessionId
| project City, SessionId, StartTime, StopTime, Duration = StopTime - StartTime
```

## Kusto 更なる上達に向けて

さらに良いクエリを書くためには、使える文法を増やすことが次のステップになると思います。
ただ、文法を一から学ぶよりは、分からないことが出てきたらその都度調べて解決していく方が早く上達すると思います。文法の量はとても多く、全部覚えること自体が効率的ではありません。where、extend、project、summarize など基本的な文法を自由自在に扱える状態にして、それ以外は覚えるのではなくその都度検索しながらクエリを書いていくことがコツです。

Microsoft の公式ドキュメントや[スタックオーバーフロー](https://stackoverflow.com/)などが役に立ちます。調べる際は英語での検索がオススメです。