SQL サブセレクト(Rails Tutorial 14章)

またつらつらと書き始めますヽ(•̀ω•́ )ゝ✧ gvimのguifontwideをゆたぽんにしたのだ。かわいいぽん。

サブクエリ

  • フォローしてるユーザの投稿(と,自分自身の投稿)を取得したい。
def feed
  Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
end

は遅い。 なぜなら

following_idsでフォローしているすべてのユーザーをデータベースに問い合わせし、さらに、フォローしているユーザーの完全な配列を作るために再度データベースに問い合わせしている

データが数千件あると死ぬ。

解決策

サブセレクトを使う。 集合のロジックをDB内に保存する。結果的に問い合わせ回数が減る。

  • まずfollowing_idsをSQLに置き換える
# レシーバのユーザがフォローしてるユーザを全て選択
following_ids = "SELECT followed_id FROM relationships
                 WHERE  follower_id = :user_id"
  • これを既存のSQLに内包させる
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

そもそもサブセレクトって?

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

入れ子にして内側のクエリが生成した値を外側のクエリが評価する。らしい。

イマイチピンとこないから実際に違いを観る

条件
  • User.firstが999人のユーザをフォロー
  • ひとりのユーザにつき50件のMicropost(投稿)を保持
# びふぉー
def feed
    Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
end

# ちょい冗長だけど勉強のためにSQL文も記録しとく。
 User Load (2.9ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
   (7.1ms)  SELECT "users".id FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ?  [["follower_id", 1]]
  Micropost Load (888.7ms)  SELECT "microposts".* FROM "microposts" WHERE (user_id IN (3,4,5,6,7,8,...

2.9 + 7.1 + 888.7 = 898.7ms
# あふたー
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

[1] pry(main)> User.first.feed
  User Load (2.7ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
  Micropost Load (627.2ms)  SELECT "microposts".* FROM "microposts" WHERE (user_id IN (SELECT followed_id FROM relationships WHERE follower_id = 1) OR user_id = 1) ORDER BY "microposts"."created_at" DESC

2.7 + 627.2= 629.9ms

ひえーこれだけでも3割程度違っちゃうのかー うーんすごい。 SQL周りの基本的なところ抑えないとだ・・・。

ActiveRecord#Base.includes

pry(main)> @books = Book.includes(:bookmarks, :reviews).order('updated_at DESC')
  Book Load (3.2ms)  SELECT "books".* FROM "books"  ORDER BY updated_at DESC
  Bookmark Load (4.9ms)  SELECT "bookmarks".* FROM "bookmarks" WHERE "bookmarks"."book_id" IN (3, 2)
  Review Load (0.2ms)  SELECT "reviews".* FROM "reviews" WHERE "reviews"."book_id" IN (3, 2)
@books.to_sql
=> "SELECT \"books\".* FROM \"books\"  ORDER BY updated_at DESC"

?

rspec assigns(:var)がnilになる問題.

今日からまた書きます(ふらぐ

今日詰まったとこ

Q, RSpec controllerのテストにおいて

assigns(:instance_var) 

nilになる.全てがnilになる。

A,

authenticate_user! 掛けてるコントローラの場合, ログインが必要。 今回はexceptがexpectになってた( ˘•ω•˘ ).。oஇ( ˘•ω•˘ ).。oஇ

それとcontrollerに定義してあるローカル変数は見えないっぽい。 スコープの問題?

perfect_ruby 15章

p548

poti:todo user1$ be irb
irb(main):001:0> require "todo"
=> true
irb(main):002:0> cmd = Todo::Command.new
=> #<Todo::Command:0x007fbad7b10cf8>
irb(main):003:0> cmd.execute
=> nil
irb(main):004:0> cmd.create_task("create_name", "create_content")
TypeError: Cannot visit Integer
    from /Users/user1/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/arel-3.0.3/lib/arel/visitors/visitor.rb:25:in `rescue in visit'
    from /Users/user1/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/arel-3.0.3/lib/arel/visitors/visitor.rb:19:in `visit'
    from /Users/user1/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/arel-3.0.3/lib/arel/visitors/to_sql.rb:241:in `visit_Arel_Nodes_Limit'
    from /Us...
irb(main):005:0> Todo::Task.all
=> [#<Todo::Task id: 6, name: "create_name", content: "create_content", status: 0, created_at: "2017-02-07 00:30:44", updated_at: "2017-02-07 00:30:44">]
#データ自体はできてる

データ自体はできてるからって無視してたら無理が出た.
(変数に格納できないとか…)
結果から書くとrubyのversonを
2.4.0 -> 2.0.0-p195
へ下げて解決.
写経時はversion合わせないと偉い目に合う。ことを学びました.
しくしく

ローカル環境でrails s がとおらないよお><

cloud9が面倒くさい($rails s -b $IP -p $PORTとか。)っていうかsublimeが便利。
なのでローカルで学習することにする

rails s が通らない問題。

cloud9と同様の手順でrails new => gem install => rails s

% rubocop --auto-gen-config
/Users/user1/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/dependency.rb:315:in `to_specs': Could not find 'rubocop' (>= 0.a) among 9 total gem(s) (Gem::LoadError)
Checked in 'GEM_PATH=/Users/user1/.gem/ruby/2.2.0:/Users/user1/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0', execute `gem env` for more information
    from /Users/user1/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/dependency.rb:324:in `to_spec'
    from /Users/user1/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_gem.rb:64:in `gem'
    from /usr/local/bin/rubocop:22:in `<main>'

ん??通らない。
cloud9では通ってたのになんでえ( ˘•ω•˘ ).。oஇ
てゆか検索対象のgemが9しかないし,pathがおかしい。
どーやらglobalなgemを見に行ってるらしい。
(rubyでgem周りを勉強しないと。てゆかパーフェクトrubyを読破しなきゃ)

てことで。
ここ の手順通りにローカルで環境構築。

よおしこれで鯖立つはずううう!

Sorry, you can't use byebug without Readline. To solve this, you need to
    rebuild Ruby with Readline support.

なんでえ( ˘•ω•˘ ).。oஇ( ˘•ω•˘ ).。oஇ( ˘•ω•˘ ).。oஇ
ぐぐった

単純にbyebugに必要なgem入ってなかっただけだった。
てことで三度目の正直、bundle exec rails s。

f:id:tanukikawaii:20161211010136j:plain

わあい。

しかし時間かかりすぎたなーあー。
解決してくのは楽しいんだけどあんまり時間がないのだ。

追記。

Q,上記手順の後にrubocop動かそうとしたらrakeが怒ってるって怒られた。
bundle installし直したら解決した。

追追記。

Q,rubocop.ymlてどこにおくの? A,プロジェクトのルートディレクトリ(いちばんうえ)

Q,rubocopで ファイル数 > 10000読みに行ってる。

AllCops:
  Exclude:
    - vendor/**/*

vendorを監視対象から外す。