【182日目】【1日20分のRailsチュートリアル】【第12章】SQLサブセレクトを使用する
今日は「12.3.3 サブセレクト」から。
12.3.3 サブセレクト
前節のヒントでおわかりのように、12.3.2のフィードの実装は、投稿されたマイクロポストの数が膨大になったときにうまくスケールアップできません。フォローしているユーザーが5000人程度になるとこういうことが起きる可能性があります。
あぁ、マイクロポストの読み込みというより、ユーザーの配列を作るのに大変なのか。
12.3.2で示したコードの問題は、following_idsでフォローしているすべてのユーザーをメモリーから一気に取り出し、フォローしているユーザーの完全な配列を作り出したことです。
(中略)
これを解決する方法は、フォローしているユーザーのidの検索をデータベースに保存するときにサブセレクト (subselect) を使用することです。
サブセレクト…???
[SQL] 7. サブクエリ 1 | TECHSCORE(テックスコア)
複数のクエリを組み合わせて、1つのクエリが生成した出力で、他のクエリの出力を制御することができます。少し分かりやすく言うと、クエリを入れ子にして、内側のクエリが値を生成し、それを外側のクエリの述語が評価して TRUE かどうか判断します。
どうもSQLの用語で「サブクエリ」とも言うっぽい。
複数のクエリを組み合わせることか…。
ちょっと変わった。
app/models/user.rb
def feed Micropost.where("user_id IN (:following_ids) OR user_id = :user_id", following_ids: following_ids, user_id: id) end
前者の疑問符を使用した文法も便利ですが、同じ変数を複数の場所に挿入したい場合は、後者の置き換え後の文法を使用するのがより便利です。
今は一箇所しか挿入してないけど、これから増やすのか。だからこっちのが便利なんだね。
このサブセレクトは、集合のロジックを (Railsではなく) データベースに保存するので、より効率が高まります。
へー…?そうなのか。なんかぱっと見変わってないんだけどこれで効率良くなるのか。
Rubyコード使うよりSQL使う方が効率いいってことなのかー。
app/models/user.rb
def feed following_ids = "SELECT followed_id FROM relationships WHERE follower_id = :user_id" Micropost.where("user_id IN (#{following_ids}) OR user_id = :user_id", user_id: id) end
テストは特に問題なし。
$ bundle exec rake test 74 tests, 371 assertions, 0 failures, 0 errors, 0 skips
もちろん、サブセレクトを使用すればいくらでもスケールアップできるなどということはありません。大規模なWebサイトでは、バックグラウンドジョブを使用して、フィードを非同期で生成するなどの対策が必要でしょう。Webサイトのスケーリングのようなデリケートな問題は本書の範疇を超えます。
あ、その辺りは扱わないのか…。Twitterとかは非同期なのかもね。Ajax使ったりするサイトもありそう。
まぁこれは「チュートリアル」だからね。Webサイトって奥が深い。。
今日の学習時間は【21分】。
次は「12.3.3 サブセレクト」のHomeページにフィードを追加するところから。