【実務・中級編】 サブクエリとCTE – Cloud Spanner

Spannerにおける「サブクエリ」と「CTE」の真実:実行計画を支配し、レイテンシをねじ伏せる

Cloud Spannerを単なる「リレーショナルデータベース」として扱っていないだろうか。もしそうなら、君が書いたそのSQLは、Spannerが持つ分散クエリエンジンのポテンシャルを殺している可能性がある。

今日は、開発の現場で「なんとなく」使われがちなサブクエリとCTE(共通テーブル式)について、Spannerのアーキテクチャという深淵から切り込む。コードレビューで後輩に「なぜこの書き方なのか」を論理的に説明するための知見を共有しよう。

—

1. サブクエリ:その「入れ子」は、分散のコストを理解しているか

Spannerのクエリエンジンは、SQLを「分散実行計画」に変換する。サブクエリを多用すると、この変換プロセスで意図しない挙動を引き起こすことがある。

実務におけるアンチパターン

よくあるのが、SELECT句の中にサブクエリを詰め込むスタイルだ。

— 【アンチパターン】行ごとにサブクエリが走る可能性がある
SELECT
u.user_id,
(SELECT SUM(amount) FROM orders WHERE user_id = u.user_id) as total_spent
FROM users u;

一見綺麗だが、Spannerのクエリオプティマイザが「Hash Join」を選択できず、行ごとのサブクエリを逐次実行するような悲惨な計画になる場合がある。データ量が増えた瞬間、このクエリは爆発する。

賢い設計:Joinへの書き換え

可能な限り、サブクエリではなく`JOIN`に展開せよ。Spannerは分散Joinが得意だ。`INNER JOIN`や`LEFT OUTER JOIN`を使うことで、クエリオプティマイザに「これはセットベースで処理すべき」というヒントを明確に与えることができる。

—

2. CTE (WITH句):可読性か、それとも実行計画の制約か

CTEはコードの可読性を劇的に向上させる。しかし、SpannerにおいてCTEは「最適化の境界」になることを忘れてはならない。

CTEの正体

SpannerのCTEは、基本的にはサブクエリの糖衣構文(シンタックスシュガー)だ。しかし、複雑なCTEを重ねすぎると、オプティマイザがクエリ全体を俯瞰して最適化する際の「視界」を狭めてしまうことがある。

推奨される設計パターン

再利用性が高いロジックを切り出すならCTEは強力だ。だが、「パフォーマンスがシビアなパス」においては、CTEを解いてフラットな結合に持ち込むのが、極限のパフォーマンスを引き出す定石だ。

— 【推奨】可読性と性能のバランスを取る
WITH monthly_sales AS (
— 特定期間の集計を事前に行う
SELECT user_id, SUM(amount) as total
FROM orders
WHERE order_date > ‘2023-01-01’
GROUP BY user_id
)
SELECT u.name, s.total
FROM users u
JOIN monthly_sales s ON u.user_id = s.user_id;

ここで重要なのは、`monthly_sales` 内のフィルタリング(`WHERE`句)が、適切にインデックスを活用できているかだ。CTEを使う際は、常に `EXPLAIN ANALYZE` を叩き、スキャンが効率的かを確認しろ。

—

3. パフォーマンスを劇的に改善する「極限の知見」

1. `EXPLAIN ANALYZE` は神の視点

書いたSQLを信じるな。実行計画を見ろ。

  • Distributed Cross Apply: サブクエリが相関サブクエリとして実行されているサインだ。データ量が多いなら、即座にJoinへ書き換えろ。
  • Distributed Union: 複数のスプリットにまたがってデータを集めている。WHERE句でキーを絞り込めているか再確認せよ。

2. 述語のプッシュダウン(Predicate Pushdown)

CTEやサブクエリの中に、可能な限り多くのフィルタ条件(`WHERE`句)を閉じ込めろ。Spannerはデータの読み込み量を減らすことが最大の最適化になる。途中で巨大なデータをメモリに載せるような書き方は、Spannerの分散リソースを浪費させるだけだ。

3. パラメータ化を徹底せよ

これはCTE以前の鉄則だが、リテラル値を直書きするな。パラメータ化することで、Spannerのクエリプランキャッシュが効き、コンパイルオーバーヘッドを削減できる。

—

結論:アーキテクトとしての心構え

CTEやサブクエリは「道具」だ。可読性を優先すべきコードと、極限のパフォーマンスを追求すべきコードで使い分ける必要がある。

  • バッチ処理や複雑な集計: CTEを使ってロジックを構造化し、可読性を担保せよ。ただし、実行計画が「分散Join」を正しく選択しているかを必ず検証すること。
  • 低レイテンシが求められるAPIパス: サブクエリを極力排除し、フラットなJoin構造にする。インデックスを最大限に活用し、スキャン範囲を最小化する。

君たちが書くSQLの一行一行が、Googleの巨大な分散インフラの上でどう動くのか。それを想像できるエンジニアこそが、Spannerを自在に操る「真のアーキテクト」だ。

さあ、エディタに戻って、そのSQLの実行計画を確認してみよう。改善の余地は、必ずそこにある。

コメント

タイトルとURLをコピーしました