📝

複雑な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