kasei_sanのブログ

かせいさんのIT系のおぼえがきです。胡乱の方はnoteとtwitterへ

Railsガイド Active Record クエリインターフェイス:2.条件〜11.ロック

Active Record クエリインターフェイス | Rails ガイド

2.条件

# 条件を配列で
Client.where('orders_count=? AND locked=?', 2, true)

# プレースホルダで書くとわかりやすい
Client.where('orders_count=:count AND locked=:locked', count: 2, locked: true)

# 以下の様な書き方はSQLインジェクションの危険があるので非推奨
Client.where("orders_count='#{params[:count]}'")

# 条件をHashで指定(普段はこれを使うことが多い)
Client.where(locked: true)

# 範囲指定
Client.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight) 

# IN
Client.where(orders_count: [1,3,5])

# NOT
Client.where.not(orders_count: 1)

3. 並び順

Client.where.not(orders_count: 1).order_by(:created_at)
Client.where.not(orders_count: 1).order_by(created_at: :asc, orders_count: :asc)

4.特定のフィールドだけを取り出す

  • #where は、SELECT * FROM が実行される
  • 特定のフィールドのみ取得したい場合は、#select を使う
User.select(:name)

SQL

SELECT "users"."name" FROM "users"

戻り値

[#<User:0x007fe664264948 id: nil, name: "kobayashi">,
 #<User:0x007fe6642645d8 id: nil, name: "yoshida">,
 #<User:0x007fe664264150 id: nil, name: "tanaka">]

SELECT DISTINCT

User.select(:name).distinct

5. limit と offset

Client.limit(5) # 上限を指定
Client.offset(5) # スキップするレコード数
Client.limit(10).offset(5) # 両方

6.グループ

Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("date(created_at)")
SELECT date(created_at) as ordered_date, sum(price) as total_price
FROM orders
GROUP BY date(created_at)

7.Having

Order.select("date(created_at) as ordered_date, sum(price) as total_price").
  group("date(created_at)").having("sum(price) > ?", 100)
SELECT date(created_at) as ordered_date, sum(price) as total_price
FROM orders
GROUP BY date(created_at)
HAVING sum(price) > 100

8.条件を上書きする

現状使うところがあんまりないので、こんなのがあるんだ程度で

  • #unscope : 特定の条件を取り除く
  • #only : 条件を上書き
  • #reorder : 並び順の指定を上書き
  • #reverse_order : 並び順を逆に
  • #rewhere : where を上書き

9. Nullリレーション

  • メソッドチェインできるけど、空のリレーションを生成する
  • 特定の条件の場合、まだメソッドチェインは続くけど、結果は空としたい時に使うと便利

10. 読み取り専用オブジェクト

#readonly をつけると、読み取り専用になる

11. レコードを更新できないようロックする

楽観的ロック

  • 基本的には競合しないという前提
  • データの更新をした時に、更新カウンタを増やす
  • 変更の直前に更新カウンタをチェックして、増えていたら更新しない(ActiveRecord::StaleObjectError)
  • 更新カウンタは(lock_version:integer)を、テーブルに追加するだけでOK!
  • 具体的には、#find したデータを #save する時に、lock_version をチェックする
  • ActiveRecord::Base.lock_optimistically = false でOFFにできる

悲観的ロック

  • 排他処理を行う
  • #transaction を使う
Item.transaction do
  i = Item.lock.first
  i.name = 'Jones'
  i.save!
end

もしくは

item = Item.first
item.with_lock do
  item.increment!(:views)
end

MySQLならば、BEGIN 〜 COMMIT が実行される