はじめに
@abe_masanori さんが
- ぐるぐるSQLは止めてくださいという話
という記事を書かれたのですが、そこで「ぐるぐるSQL」なる見慣れない用語が使われていて、それは「N+1問題」というのだとコメントを書いたところ、妙に反発を受けてしまったので改めて記事にしたいと思います。
用語としてどちらが古いか?
さしあたり「ぐるぐるSQL」と「N+1問題」の用語のどちらが正しいかの議論は後回しとして、それぞれの用語の初出を調べてみましょう。例えばtwitterで検索すると、「ぐるぐるSQL」の最古のツイートはこちらのようです。
それだけしか使用してないなら、NP_Listとかでサブプラグインを作成するのがいいんだろうとは思ってるですが、これってやっぱりスマートなSQLはないですよね…?1サブカテゴリ毎にぐるぐるSQL回すしか手はないんですよね?
— 藤咲 (@fjsk) June 2, 2008
2008年6月3日だそうです。
一方、「N+1問題」は英語ではそのまま N+1 Problem
または N+1 SELECT Problem
といい、ツイッター上にはたくさん残っています。
さしあたりこのツイートを選んでおきましょう。
Fixed another couple of "n + 1" SELECT problems.
— Greg Hurrell (@wincent) March 15, 2008
2008年5月15日だそうです。
残念ながらtwitterのサービス自体が2007年頃に生まれたものなのでそれ以外の記事はインターネット上から探すしか無いでしょう。いい記事をご存知でしたらコメントでお知らせください。
「ぐるぐるSQL」という用語はどこがまずいのか
これは元記事でコメントとして書いたように、「ぐるぐるSQL」という用語では日本語の記事しか検索できず、英語の記事が探せないというところになります。はじめから「N+1問題」で統一しておけば、英語の記事を探すときも N+1 Problem
でそのまま検索できるわけです。
「ぐるぐるSQL」のほうがわかりやすいという意見
nested loop joinかと思ったらそういう次元ではなかった。でもN+1も確かにイマイチな呼び方だよなぁ…。
— ぺけよ (@Xyo) January 19, 2021
ぐるぐるSQLは止めてくださいという話 https://t.co/O85DFjerWZ #Qiita
勝手に別名付けるなと言う向きもあるけど、「N+1問題」という名前って、一番届けたい実装している本人に伝わっていないことも多い気がするから、「ぐるぐるSQL」という名前はとっつきやすくて結構アリなのでは。https://t.co/CfSfSjvw4B
— グッティ (@_gutio_) January 19, 2021
といった擁護意見もあり、最終的にはこれらの用語を使う個々人の判断になるでしょう。
「ぐるぐるSQL」は「N+1問題」と違う定義だという意見
@abe_masanori さんはコメント上でこのように書いています。
今回の問題は N+1 問題とは異なるという認識の元、N+1 問題というワードは意識して使っていません。
N+1 問題というワードは 2000-2010 年代半ば(後半?)ぐらいから使われ始めたと記憶していますが、その文脈は Hibernate や RoR といった SQL 文を開発者に(あまり)意識させない ORM において、DB上の1対多のエンティティをアプリ上の1対多のオブジェクトに(ほぼそのまま)マッピングする際に、ORM 内部で暗黙的に SQL 文が繰り返し呼び出されてしまう状況だったと認識しています。
勿論、ORM にも Eager Fetch や JOIN Fetch など、この問題を解決する手段は用意されているので、N+1 問題の根本の原因は、以下にあると思っています。
- 開発者が ORM の内部挙動を理解していない
- ORM の利用方法が適切ではない
一方、今回「ぐるぐる SQL」と呼んだ問題は、条件分岐や複雑な条件の集計、例外処理、行間計算などを要する処理において、SQL 文で集合処理すれば良いところを、開発者がループを使って細かい SQL 文で明示的に逐次処理してしまうということを指しています。
こういった意見に対し、筆者はこのように回答しました。
N+1問題は英語ではそのまま N+1 Problem といい、SQLを始めとしたクエリ言語の運用で頻発する古典的な問題です。SQLに限らずGraphQLでだって発生します。
ORM運用時に表面化しやすいですがORMを使わなくても発生します。
また、集計していようがいまいが無関係で、ループ等で本来1回のクエリ呼び出しで完了するような処理をN回に分割して実行しているときは全てN+1として扱われます。