NRQLマスターになろう - サブクエリ編

NRQLでSQLのようなサブクエリが使えるって知ってますか？サブクエリを使ってデータを組み合わせて可視化する方法をご紹介します。


# はじめに
[NRQL（New Relic Query Language）](https://docs.newrelic.com/jp/docs/nrql/nrql-syntax-clauses-functions/)はNew Relicに収集したデータをいい感じで可視化し、分析するためのクエリ言語です。ビルトインで提供されているUIで十分に分析をすることはできますが、複雑な条件でデータ分析をしたい場合やビルトインのUIではカバーされていないような可視化をしたい場合にはNRQLによる可視化は非常に強力な手段となります。

SQLやその類のクエリ言語を一度でも使ったことある人がどうしてもNRQLでやりたくなってしまうこと、そう、それはサブクエリやJOINです。特性の違いからSQLと完全に同じことができるわけではありませんが、NRQLでもサブクエリはサポートされており、うまく使えばデータを組み合わせてより高度な分析ができるようになります。

この記事では、以下のことが理解できるようにサブクエリの使い方を紹介します。
- サブクエリの構文
- サブクエリの例
- 使用上の注意点

# サブクエリの構文
サブクエリは、文字通り別のクエリの中にネストされたクエリです。同じイベントやメトリクスに対するサブクエリをネストすることもできますし、異なるイベントやメトリクスでも使えます。まず、簡単な例を使ってサブクエリがどんなものかイメージできるように説明します。

```sql:サブクエリの例
FROM Transaction SELECT count(*)
WHERE duration > (
	FROM Transaction SELECT average(duration)
)
```

このクエリは全トランザクションのレスポンスタイムの平均よりレスポンスタイムが遅いトランザクションの数を出すクエリです。全トランザクションのレスポンスタイムの平均を出すのに一旦全トランザクション舐めるのでサブクエリを使っています。 (***FROM Transaction SELECT average(duration)*** の部分) 


この例も含めていくつかのサブクエリの利用シーンを列挙してみます。

### 数値や文字列の演算子の項で利用
<や>、=などの比較演算子でサブクエリの結果を利用できます。例えば、latest()やmax()、average()などを使ったサブクエリが返す単一の結果を、外側のクエリの属性と比較することで絞り込み条件に利用します。例では > を使っていますが、<や=なども同様に使えます。

```sql:演算子でサブクエリを利用
...WHERE xxxx > (FROM yyyy SELECT max(zzzz))...
```

さらに***サブクエリは対象とする期間を変えられる***ので過去、もしくは長期の傾向をサブクエリで取得して、外側のクエリで短期の傾向と比較する、といったことも可能です。

```sql:期間の違うデータをサブクエリで取得
...WHERE xxxx > (FROM yyyy SELECT average(zzzz) since 1 months ago)... since 1 day ago
```

また、in演算子を使うとサブクエリの結果をin演算子の候補にすることができます。

```sql:in句の候補にサブクエリの結果を利用
...WHERE xxxx IN (FROM yyyy SELECT uniques(zzzz))...
```

### サブクエリの結果をSELECTで加工して利用
外側のクエリのSELCT句で利用することもできます。サブクエリの結果に外側のクエリの属性の値を組み合わせたりできます。

```sql:サブクエリの結果を加工
FROM ... SELECT average(xxxx * (FROM ... SELECT ...))  WHERE ...
```

### サブクエリの結果を外側のFROMで利用
通常、FROM句にはイベントやメトリック（Metric）を指定しますが、サブクエリの結果を仮想的な表として扱うこともできます。一旦サブクエリで集計した結果をさらに集計したい場合などに有効です。

```sql:サブクエリの結果をFROMで利用
FROM ( FROM ... SELECT ...) SELECT ... WHERE ...
```

:::note warn
厳密には、「サブクエリ」はSELECT、WHEREで使われるものを指し、FROM句で利用されるクエリは「ネストされた集計クエリ」と呼ばれます。サブクエリはアラートで利用できませんが、ネストされた集計クエリはアラートで利用できるなど、機能的な差がある点はご注意ください。

ネストされた集約クエリの詳細については[公式ドキュメント](https://docs.newrelic.com/jp/docs/nrql/using-nrql/nested-aggregation-make-ordered-computations-single-query/)をご参照ください。
:::


いくつかの代表的なケースを抜粋しましたが、利用方法はほぼSQLと同じですね。SQL使われたことある方はスッと入ってきたのではないでしょうか。この他にも関数の引数にサブクエリを使ったり、複数のサブクエリを使ったりできます。その他の使い方に関しては公式ドキュメントをご参照ください。

https://docs.newrelic.com/jp/docs/nrql/using-nrql/subqueries-in-nrql/


https://docs.newrelic.com/jp/docs/nrql/using-nrql/nested-aggregation-make-ordered-computations-single-query/


# サブクエリの使用例
具体的なユースケースをいくつかご紹介します。

### 例1: KubernetesのPodをデプロイする際のリソースのRequests/Limitsをノード単位に集計
KubernetesのPodをデプロイする際に割り当てるリソースとしてRequests/Limitsが指定されている場合、その値はK8sContainerSampleというコンテナ単位のイベントに保存されます。コンテナ単位のRequests/Limitsはそのイベントから情報が取れますが、ノード単位で確認したいということは度々耳にします。ノード単位で情報を見るにはコンテナ単位のRequests/Limitsをノード単位で集計する必要がありますが、これはサブクエリを使うことで実現できます。

以下がクエリの例です。

サブクエリでは、K8sContainerSampleからcpu/memoryのRequests/Limitsの最新の値をとっています。これをpodName、nodeName、clusterName事にグルーピング（FACET）している点がポイントです。

サブクエリの結果(コンテナ単位のRequests/Limits)を、外側のクエリで合算(sum)しています。外側のクエリでは、nodeName、clusterName事にグルーピング（FACET)しており、podNameではグルーピングしません。nodeName、clusterNameでグルーピングすることでノード単位の合計を計算しています。

```sql:Requests/Limitsをノード単位に集計
FROM (
　FROM K8sContainerSample  
　SELECT latest(cpuLimitCores) as cpuLimitCores, 
　　　　　latest(cpuRequestedCores) as cpuRequestedCores,
　　　　　latest(memoryLimitBytes) as  memoryLimitBytes,
　　　　　latest(memoryRequestedBytes) as memoryRequestedBytes 
　FACET podName, nodeName, clusterName
) 
SELECT sum(cpuLimitCores), 
　　　　sum(cpuRequestedCores), 
　　　　sum(memoryLimitBytes), 
　　　　sum(memoryRequestedBytes) 
FACET nodeName, clusterName
```

### 例2: スロークエリが発生しているAPIを確認
アプリケーションのパフォーマンス情報はTransactionやSpanといったイベントに格納されています。TransactionはAPI呼び出し単位のデータで、Spanはトレースの一要素ごとのイベントです。Spanには、データベースクエリやそのレスポンスタイムが、trace.idとともに保存されているので、これを活用することでスロークエリが把握できます。Transactionにはトランザクション全体のレスポンスタイムなどが、trace.idとともに保存されています。

クエリの情報が入ったSpanと、トランザクションの情報が入ったTransactionを組み合わせることで、スロークエリが発生しているトランザクションを効率的に一覧できます。


以下がクエリの例です。

サブクエリではSpanのデータのうち、データベースクエリに該当するSpanで、かつレスポンスタイムが遅いものを特定し、その外側で該当のtrace.idをトレースIDとしてもつトランザクションを一覧しています。

```sql:スロークエリがあるトランザクションの一覧
FROM Transaction 
SELECT appName, name, traceId   
WHERE traceId in (
　FROM Span 
　SELECT trace.id  
　WHERE category='datastore' and duration > xxxx
)
```
:::note warn
TransactionやSpanデータはサンプリングされるので、全てのスロークエリが抽出されるわけでは無い点は注意が必要です。全体的な傾向を捉えて問題がある場合に詳細を深ぼるという、面から点への分析の流れが良いでしょう。
:::

# 注意事項
いくつかの制約に注意しながら利用しながらサブクエリを利用する必要があります。
- 演算子の項としてサブクエリを利用する場合は、単一の結果を返す必要があります。複数の属性を返したり、FACETによるグルーピングはできません。
- サブクエリが返す結果の最大数は、通常のクエリの結果の最大数と同じです。クエリの上限については公式ドキュメントをご確認ください

https://docs.newrelic.com/docs/nrql/nrql-syntax-clauses-functions/#sel-limit


# まとめ
NRQLでサポートされる様々な機能のうちサブクエリをご紹介しました。異なるデータを組み合わせた分析やや複雑な分析はビルトインのUIの出番ですが、そこではサポートされていないケースがある場合は是非サブクエリを使ってみてください。

構文や活用方法の例については公式ブログもご参照ください。


https://docs.newrelic.com/jp/docs/nrql/using-nrql/subqueries-in-nrql/


# その他
New Relicでは、**新しい機能やその活用方法**について、QiitaやXで発信しています！
**無料でアカウント作成**も可能なのでぜひお試しください！

:::note info
New Relic株式会社の[X(旧Twitter)](https://x.com/NewRelicJapan) や [Qiita OrganizationOrganization](https://qiita.com/organizations/newrelic)では、
新機能を含む活用方法を公開していますので、ぜひフォローをお願いします。
:::

:::note info
無料のアカウントで試してみよう！
[New Relic フリープランで始めるオブザーバビリティ！](https://qiita.com/ktst79/items/5ee74b34e6adb8a7a6fa)
:::

### NRQLマスターになろうシリーズはこちら

https://qiita.com/ktst79/items/c1ddf33e18e9cfeba893

https://qiita.com/ktst79/items/88a80229d226b5c0abcf

https://qiita.com/ktst79/items/56e49f71487928c5bca1

https://qiita.com/ktst79/items/f743ad2207c435134615