ぞえの技術めも

Ruby on Rails勉強中

【182日目】【1日20分のRailsチュートリアル】【第12章】SQLサブセレクトを使用する

Ruby on Railsチュートリアル(第3版)

今日は「12.3.3 サブセレクト」から。

12.3.3 サブセレクト

前節のヒントでおわかりのように、12.3.2のフィードの実装は、投稿されたマイクロポストの数が膨大になったときにうまくスケールアップできません。フォローしているユーザーが5000人程度になるとこういうことが起きる可能性があります。

あぁ、マイクロポストの読み込みというより、ユーザーの配列を作るのに大変なのか。

12.3.2で示したコードの問題は、following_idsでフォローしているすべてのユーザーをメモリーから一気に取り出し、フォローしているユーザーの完全な配列を作り出したことです。
(中略)
これを解決する方法は、フォローしているユーザーのidの検索をデータベースに保存するときにサブセレクト (subselect) を使用することです。

サブセレクト…???

[SQL] 7. サブクエリ 1 | TECHSCORE(テックスコア)

複数のクエリを組み合わせて、1つのクエリが生成した出力で、他のクエリの出力を制御することができます。少し分かりやすく言うと、クエリを入れ子にして、内側のクエリが値を生成し、それを外側のクエリの述語が評価して TRUE かどうか判断します。

どうもSQLの用語で「サブクエリ」とも言うっぽい。
複数のクエリを組み合わせることか…。

リスト12.45でコードを若干修正し、フィードをリファクタリングすることから始めましょう。

ちょっと変わった。

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ページにフィードを追加するところから。