複雑なJoin句を使用せず、アプリケーションロジックで構成・集計した
それでもAIが答えてくれないこと #3
複雑な JOIN を避け、アプリケーションロジックで集計した話
導入
開発をしていると、どうしても SQL にロジックを詰め込みすぎてしまう場面がある。INNER JOIN や LEFT JOIN が連なり、GROUP BY が複数並んだ SELECT 文。
一見すると「SQL で一発で出せる」ことはスマートに思えるが、長期的に見れば負債になりやすい。
今回のプロジェクトでも、まさにその壁にぶつかった。
事例
あるアプリケーションで、複雑な SELECT 文が増えすぎていた。
- 多数の JOIN と GROUP BY が詰め込まれた SQL
- どのテーブルをどう組み合わせているのか、一読して理解できない
- 新規メンバーが参加するときにキャッチアップが非常に辛い
さらに、適切なインデックスが貼られていない場合、DB 側のクエリ実行に時間がかかる。
その結果、同じテーブルを参照する他サービスに影響を及ぼす危険すらある。
あるユーザーへの参照が多ければ、ホットスポットになる可能性も高い。
「スムーズに知識がインプットできないサービス」は、それだけで負債だと私は思う。
自分たちの選択
大規模バッチ処理の新プロジェクトでは、あえて 「SQL 側では最小限」 を選んだ。
- 単一 SELECT + シンプルな WHERE 句だけ
- 集計やグルーピングは アプリケーションロジック側で実装
具体的には以下の工夫をした:
- chunk 単位で SELECT を行い、回数を最小限にする
- Go のライブラリ samber/lo を採用し、GroupBy / CountBy / Reduce などで集計
- メモリ展開を考慮し、構造体のフィールドは必要最小限かつプリミティブ型中心に設計
- SELECT するテーブルは、アプリケーションロジックの「コア」となる大規模データから優先的に取得
このアプローチのメリット
-
クエリのシンプル化
SQL が簡潔になり、新規メンバーでもキャッチアップが容易になった -
DB 負荷の分散
複雑な JOIN を避けたことで、DB サーバーの CPU/メモリ負荷を下げ、ホットスポットも軽減 -
スケーラビリティ
アプリケーションサーバーを水平スケールすることで負荷を吸収できる
将来的なシャーディングやレプリカ導入にも柔軟に対応可能 -
アプリケーション側の柔軟性
Go のコードでロジックを書き換えられるため、ビジネス要件の変更に強い -
開発スピードの向上
SQL を大改修せず、アプリケーションの変更で対応可能 -
テスト容易性
DB 依存を減らし、Go 側でユニットテストを書けるようになった -
障害切り分けがしやすい
SQL に全て押し込むよりも、アプリケーションで責務を分けることでボトルネックを特定しやすい
学び
結局のところ、今回のメリットは二つに集約される:
- SQL をシンプルにして理解・保守しやすくすること
- DB に依存しすぎず、アプリケーションで柔軟に負荷分散できること
まとめ
SQL でできることは多い。
だが、「できること」と「やるべきこと」は違う。
未来のエンジニアを苦しめるなら、それは技術的負債だ。
複雑な JOIN を避け、アプリケーションロジックに集計を任せる。
この判断こそ、AI では答えられないことであり、だからこそ記事に残す意味がある。
Discussion