ActiveRecord CRUDサンプル

ActiveRecordのコードサンプル。

子テーブルの先読みやINNER JOIN, OUTER JOINなどについては [こちらのページ](http://qiita.com/leon-joel/items/f26556c9e56833983856) にまとめた。

# 検索

```rb
Post.all

Post.first
Post.last
```

## find系

```rb
# id = 1 LIMIT 1
Post.find(1)  # 見つからなかったら ActiveRecord::RecordNotFound が throw される

# title = "title1"  LIMIT 1
Post.find_by(:title, "title1")  # 見つからなかったら nil が返る [Rails4以降]
Post.find_by_title("title1")  # 同上

# title="title1" AND id=3  LIMIT 1
Post.find_by_title_and_id("title1", 3)
```

## whereメソッド

```rb
# title="title1" AND id=1
Post.where(title: "title1", id: 1)

# 同上 ※プレースホルダータイプ
Post.where(:title = ? and id = ?", "title1", 1)

# 同上 ※シンボル指定
Post.where("title = :title and id = :id", {:title => "title1", :id => 1}

# 条件の否定(NOT)
# title != "title1"
Post.where.not(title: "title1")
```

範囲指定やLIKEなど

```rb
# title LIKE "title%"
Post.where("title LIKE ?", "title%")

# id <= 3 OR id = 5
Post.where("id <= 3 OR id = ?", 3, 5)

# id BETWEEN 1 AND 3
Post.where(id: 1..3)

# id IN (1, 3)
Post.where(id: [1, 3])
```

## select
取得するカラムを絞り込む

```rb
Post.where(id: 3).select(:title, :boby)
```

## pluck *[Rails4]*
指定列の配列を取得

```rb
Post.where(id: 1..3).pluck(:title, :body)
# => 
# [
#   [ "t1", "b1"]
#   [ "t2", "b2"]
#   [ "t3", "b3"]
# ]
```

## exists?  *[Rails4]*

```rb
# SELECT 1 AS one FROM posts WHERE id = 10 LIMIT 1
Post.where(id: 10).exists?

# id=1 が存在する?
Post.exists?(1)
# 5000<price が存在する?
Book.exists?(['? < price', 5000])
# publish='テスト' が存在する
Book.exists?(publish: 'テスト')
# booksテーブルに1件でもデータが存在する?
Book.exists?
```

## distinct

```rb
Post.select(:title).distinct

# distinctの破棄
Post.select(:title).distinct.distinct(false)
```

## order/reorder

```rb
Post.where("title LIKE ?", "title%").order(updated_at: :desc, created_at: :asc)

# 前のorderを無視して（リセットして）、新しいorderをセットする
Post.order(updated_at: :desc).reorder(created_at: :desc)

# 前のorderをリセットするだけ
Post.order(updated_at: :desc).reorder(nil)

```

## limit/offset

```rb
# SELECT * FROM posts ORDER BY title DESC LIMIT 3 OFFSET 4
Post.order(title: :desc).limit(3).offset(4)
```

## group / having

```rb
# SELECT publish, AVG(price) AS avg_price
#   FROM books
#  GROUP BY publish
Book.select('publish, AVG(price) AS avg_price').group(:publish)

# SELECT publish, AVG(price) AS avg_price
#   FROM books
#  GROUP BY publish
# HAVING 2500 <= AVG(price)
Book.select('publish, AVG(price) AS avg_price').group(:publish).having('? <= AVG(price)', 2500)
```

## 生SQL(find_by_sql)

```rb
Books.find_by_sql("
  SELECT publish, AVG(price) AS avg_price
    FROM books
   GROUP BY publish
  HAVING 2500 <= AVG(price)
")
```

## 破壊的クエリーメソッド *[Rails4]*
where!, order!, select!, limit!, offset!, group!, having!, distinct! など、変数への再代入不要な破壊的メソッドが追加された。

## 条件式除去 *[Rails4]*

```rb
# where条件とselect条件を除去する例
Post.where("title LIKE ?", "title%").order(updated_at: :desc, created_at: :asc).select(:title, :body).unscope(:where, :select)

# where条件のcd列に対する条件を除去
Book.where(publish: 'テスト', cd: true).unscope(where: :cd)
```

## 空の結果を返す *[Rails4]*

```rb
Book.none
```

# 挿入系

## new + save

```rb
post = Post.new(title: "xxx", body: "bbb")
post.save
# saveは結果がtrue/falseで返される
# save!とすると、失敗時に例外がthrowされる

post = Post.new
post.title = "xxx"
post.body = "bbb"
post.save

post = Post.new do |p|
  p.title = "xxx"
  p.body = "bbb"
end
post.save
```

## create

new と save を一度に実行。

```rb
Post.create(title: "xxx", body: "bbb")
```

## find_or_create_by *[Rails4]*

```rb
# Rails4
Post.find_or_create_by(title: "title6") do |post|
	post.body = "hobby6"
end

# Rails3
Post.where(title: "title6").first_or_create do |post|
	post.body = "hobby6"
end
```

# 更新系

```rb
post.title = "xxx"
post.save

post.update_attribute(:title, "xxx")

post.update_attributes(title: "xxx", body: "bbb")

Post.where(id: 1..3).update_all(title: "xxx", body: "bbb")
```

validationの有無などについてはこちらが詳しい。[ActiveRecord の attribute 更新方法まとめ](http://qiita.com/tyamagu2/items/8abd93bb7ab0424cf084)


### その他更新系メソッド

| メソッド | 概要 |
|:--|:--|
|post.new_record?   |未保存?（新規レコード?）   |
|post.persisted?   |上の反対   |
|post.touch([name])   |update_at/on列を現在時刻で更新。name指定時はその列も。   |
|post.changed?   |取得してから何らかの変更がされたか？   |
|post.destroyed?   |現在のオブジェクトが削除済みか?   |


# 削除系

```rb
post.destroy

Post.destroy(1)

# データの削除のみ（アソシエーション・コールバックは無視される）
Post.delete(1)

Post.destroy_all(["title <> ?", "テスト"])

Post.where('title <> ?', 'テスト').destroy_all

```

# トランザクション

```rb
begin
  Post.transaction do
    xxx
  end
  例外が発生しなかった場合の処理
rescue => e
  例外が発生した場合の処理
end
```

# その他

### scope

```rb
class Post < ActiveRecord::Base
  scope :top3, -> { order("created_at").limit(3) }
end

Post.top3
```

### 複数レコードに対する列挙処理（eachのような）

普通に書くとこんな感じ。

```rb:ruby
Blog.where("? <= id", 2).each do |b|
  do_something
end
```

eachが始まるところで結果**全て**を取得するので、大量データには対応出来ないこともある。
というわけで、そんなときには内部的に何回かに分けて取得してくれる `find_each` を使う。

```rb:ruby
Blog.where("? <= id", 2).find_each(batch_size: 2) do |b|
  do_something
end
```

デフォルトでは1000件ずつ取得され（SELECTが走り）、1件ずつblockに渡される。
この例では、batch_size引数で1回に取得する件数も指定している。（2件）

その際に実行されたSQLがこれ。
Cursorを使っているわけではなく、単純に複数回SELECTが走っているだけ。

```sql:SQL
SELECT * FROM blogs WHERE (2 <= id)
 ORDER BY id ASC LIMIT 2;

SELECT * FROM blogs WHERE (2 <= id) AND (id > 3)
 ORDER BY id ASC LIMIT 2;
```

また、`find_in_batches`メソッドは、1回のSELECTで取得したレコードを1つの配列としてblockに渡してくれる。

### アソシエーション
種類
 - belongs_to
 - has_one
 - has_many
 - has_many :through
 - has_one :through
 - has_and_belongs_to_many

各アソシエーションを定義すると、様々な便利なメソッドが使えるようになる。そのあたりは[Railsガイド](http://railsguides.jp/association_basics.html)に詳しく記載されている。

# 参考サイト・資料
[ドットインストール - ActiveRecord入門](http://dotinstall.com/lessons/basic_activerecord)
[『Ruby on Rails4 アプリケーションプログラミング』](http://gihyo.jp/book/2014/978-4-7741-6410-6)翔泳社
[RailsGuides - Active Record クエリインターフェイス](http://railsguides.jp/active_record_querying.html)