SlideShare a Scribd company logo
1 of 40
Download to read offline
Active Record の
クエリインタフェースについて
2018/11/21
大手町.rb #11
大手町.rb #11 「Active Record クエリインタフェースについて」
1自己紹介
Tomoya Kawanishi a.k.a. @cuzic
エネチェンジ株式会社 チーフエンジニア
電力会社、ガス会社を切り替えるなら、エネチェンジ経由で!
一般家庭も!法人も!
Ruby関西の中の人
発表者として登壇くださる方、あとで声かけください。
第84回 Ruby関西勉強会 12月1日(土)
大手町.rb の中の人
毎月 大手町.rb の開催を予定
東京駅、各線大手町駅から直結!
Ruby の初級者がメインターゲット
大手町.rb #11 「Active Record クエリインタフェースについて」
Disclaimer
大手町.rb は祝!11回!
人数がとても増えてきました!
大手町.rb は(比較的)初級者向けの勉強会
とはいえ、初級者向けのままだと、ネタ切れしちゃう
同じネタをリピートするか、レベルアップするか。
大手町.rb はレベルアップしていく方向!
今のオーディエンスを大切にしていく!
2
大手町.rb #11 「Active Record クエリインタフェースについて」
今日のテーマ
Active Record クエリインタフェースについて
Ruby on Rails を使うなら、みんな使ってる?
とはいえ、時間をとって学ぶことがあまりない
というのも事実
Rails ガイドを熟読すればいいのですが、
なかなか上から読むのは大変です。
今日は一緒に学んでいきましょう
3
大手町.rb #11 「Active Record クエリインタフェースについて」
前提となるモデル
Rails ガイドの内容をベースに紹介します。
もろもろ、Rails の命名規則に従っている前提です。
id が主キー
4
class Role < ApplicationRecord
has_and_belongs_to_many :clients
end
class Address < ApplicationRecord
belongs_to :client
end
class Order < ApplicationRecord
belongs_to :client, counter_cache: true
end
class Client < ApplicationRecord
has_one :address
has_many :orders
has_and_belongs_to_many :roles
end
大手町.rb #11 「Active Record クエリインタフェースについて」
find: 主キーによるレコードの取得
find :主キーにマッチするオブジェクトを取得
複数レコードを同時に取得できる
5
clients = Client.find(10) #=> #<Client id: 10, first_name: "Ryan">
SELECT * FROM clients WHERE (clients.id = 10) LIMIT 1
clients = Client.find([1, 10])
# Client.find(1, 10) でも同じ結果
# => [
# <Client id: 1, first_name: "Lifo">,
# <Client id: 10, first_name: "Ryan">
# ]
SELECT * FROM clients WHERE (clients.id IN (1,10))
対応するレコードがないと、 ActiveRecord::RecordNotFound 例外が発生する
# Client.find([1, 2], [3, 4]) のような書き方もできる
大手町.rb #11 「Active Record クエリインタフェースについて」
take: 任意のレコードの取得
take :どのレコードか指定せず、1レコード取得
take(2) で最大2個のレコードを取得
モデルに1つもレコードがない場合は nil を返す
6
Client.take #=> #<Client id: 1, first_name: "Lifo">,
SELECT * FROM clients LIMIT 1
clients = Client.take(2)
# Client.find(1, 10) でも同じ結果
# => [
# #<Client id: 1, first_name: "Lifo">,
# #<Client id: 220, first_name: "Sara">
# ]
SELECT * FROM clients LIMIT 2
take! を使うと、マッチしない場合、ActiveRecord::RecordNotFound 例外が発生する
大手町.rb #11 「Active Record クエリインタフェースについて」
first:最初のレコードを取得
first:主キー順で最初のレコードを取得
デフォルトスコープがあれば、
デフォルトスコープで指定された順序で最初
first(3) で3個のレコードを取得できる
7
Client.first #=> #<Client id: 1, first_name: "Lifo">,
SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1
clients = Client.first(3)
# => [
# #<Client id: 1, first_name: "Lifo">,
# #<Client id: 2, first_name: "Fifo">,
# #<Client id: 3, first_name: "Filo">
# ]
SELECT * FROM clients ORDER BY clients.id ASC LIMIT 3
order を使って順序を変更すると、first はその順で最初のレコードを返す
大手町.rb #11 「Active Record クエリインタフェースについて」
last:最後のレコードを取得
last:主キー順で最後のレコードを取得
デフォルトスコープがあれば、
デフォルトスコープで指定された順序で最後
last(3) で3個のレコードを取得できる
8
Client.last #=> #<Client id: 221, first_name: "Russel">
SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
clients = Client.last(3)
# => [
# #<Client id: 219, first_name: "James">,
# #<Client id: 220, first_name: "Sara">,
# #<Client id: 221, first_name: "Russel">
# ]
SELECT * FROM clients ORDER BY clients.id DESC LIMIT 3
order を使って順序を変更すると、last はその順で最後のレコードを返す
大手町.rb #11 「Active Record クエリインタフェースについて」
find_by:マッチする最初のレコードを取得
find_by:与えられた条件にマッチする最初のレコード
9
Client.find_by(first_name: 'Lifo')
#=> #<Client id: 1, first_name: "Lifo">
# Client.where(first_name: 'Lifo').take と同じ意味
SELECT * FROM clients WHERE (clients.first_name = 'Lifo') LIMIT 1
find_by! を使うと、マッチするレコードが見つからなかった場合に、
ActiveRecord::RecordNotFound例外が発生する
大手町.rb #11 「Active Record クエリインタフェースについて」
find_each : バッチ処理でメモリを節約
多数のレコードに対して反復処理するとき、
素朴に実装すると、メモリが大量に必要となる
find_each を使うとメモリを節約できる
ただし、数千件程度であれば、素朴な実装でも問題ない
10
# User.all のためのメモリを消費する
User.all.each do |user|
NewsMailer.weekly(user).deliver_now
end
# レコードを 1,000件ずつ取得する
User.find_each do |user|
NewsMailer.weekly(user).deliver_now
end
# レコードを 500件ずつ、id=2000 から id=10000 番まで順に処理
User.find_each(batch_size: 500, start: 2000, finish: 10000) do |user|
NewsMailer.weekly(user).deliver_now
end
大手町.rb #11 「Active Record クエリインタフェースについて」
find_in_batches: レコードの配列でバッチ処理
find_each は省メモリだが、使う側は1件ずつ
find_in_batches では使う側も1度に 1000件処理できる
ユースケース
複数のワーカスレッド、ジョブキューなどを使っている
各ワーカスレッドに 1,000件ずつ投入するときなどにベンリ
11
# 1回あたりadd_invoicesに納品書1000通の配列を渡す
Invoice.find_in_batches do |invoices|
export.add_invoices(invoices)
end
大手町.rb #11 「Active Record クエリインタフェースについて」
ハッシュを使用した条件 1/2
where はハッシュを使うと簡潔に書けて読みやすい
12
# 等値条件
Client.where(locked: true)
# (SQL) SELECT * FROM clients WHERE (clients.locked = 1)
# belongs_to リレーションシップを使った関連付け
Article.where(author: author)
# where(author_id: author.id) と同じ意味になる
# id は省略でき、簡潔に書ける
# 範囲条件
Client.where(created_at:
(Time.now.midnight - 1.day)..Time.now.midnight)
# (SQL) SELECT * FROM clients WHERE (clients.created_at BETWEEN
'2008-12-21 00:00:00' AND '2008-12-22 00:00:00')
大手町.rb #11 「Active Record クエリインタフェースについて」
ハッシュを使用した条件 2/2
.or の使い方が覚えにくい。
必要な都度検索し、調べれば OK
13
# サブセット条件
Client.where(orders_count: [1,3,5])
# (SQL) SELECT * FROM clients WHERE (clients.orders_count IN
(1,3,5))
# NOT条件
Client.where.not(locked: true)
# OR条件
Client.where(locked: true).
or(Client.where(orders_count: [1,3,5]))
# (SQL) SELECT * FROM clients WHERE (clients.locked = 1 OR
clients.orders_count IN (1,3,5))
大手町.rb #11 「Active Record クエリインタフェースについて」
引数による条件 14
# サブセット条件
Client.where("orders_count = ?", params[:orders])
# (SQL) SELECT * FROM clients WHERE (clients.orders_count IN
(1,3,5))
# 複数の条件を一度に指定
Client.where("orders_count = ? AND locked = ?", params[:orders],
false)
# 個人的には Client.where(orders_count: params[:orders]).
# where(locked: false) のように書くことが多い
# Client.where("orders_count = #{params[:orders]}") はダメ。絶対。
# SQL インジェクション攻撃のおそれがある
# プレースホルダを使って、記述することもできる
Client.where("created_at >= :start_date AND created_at <= :end_date",
start_date: params[:start_date], end_date: params[:end_date])
大手町.rb #11 「Active Record クエリインタフェースについて」
並び順 15
Client.order(:created_at) # デフォルトは昇順
Client.order("created_at")
Client.order(created_at: :desc) # 降順
Client.order(created_at: :asc) # :asc はなくてもいいけど、あってもいい
Client.order("created_at DESC") # 文字列で明示的に指定することもできる
Client.order("created_at ASC")
Client.order(orders_count: :asc, created_at: :desc)
Client.order(:orders_count, created_at: :desc)
Client.order("orders_count ASC, created_at DESC")
Client.order("orders_count ASC", "created_at DESC")
Client.order("orders_count ASC").order("created_at DESC")
# SELECT * FROM clients ORDER BY orders_count ASC, created_at DESC
大手町.rb #11 「Active Record クエリインタフェースについて」
特定のフィールドだけを取り出す 16
Client.select("viewable_by, locked")
Client.select(:viewable_by, :locked)
# 個人的には select は下記の場合に使うことが多い
## 1. アソシエーション先だけが欲しいとき
Order.where("created_at > ?", 2.days.ago).
select(:client_id).distinct.map(&:client)
## 2. 複雑な SQL を使ったフィルタをしたいとき
Client.where(id: Order.complex_scope.select(:client_id).distinct)
## complex_scope のところはなんか複雑なことをしていると想像してください
大手町.rb #11 「Active Record クエリインタフェースについて」
limit と offset
limit と offset は1ページあたり10件表示とかで、
次のページのレコードを取得するときなどによく使う
17
Client.limit(5)
# SELECT * FROM clients LIMIT 5
Client.limit(5).offset(30)
# SELECT * FROM clients LIMIT 5 OFFSET 30
大手町.rb #11 「Active Record クエリインタフェースについて」
グループ(カウント) 18
# 日付ごとの合計金額
# Order の配列が返る
Order.select("date_trunc(created_at, 'date') as ordered_date,
sum(price) as total_price").group("date(created_at)")
# (SQL) SELECT date(created_at) as ordered_date, sum(price) as
total_price FROM orders GROUP BY date(created_at)
# ステータスごとの件数
# ステータスがキーで、件数が値のハッシュが返る
Order.group(:status).count
# => { 'awaiting_approval' => 7, 'paid' => 12 }
# (SQL) SELECT COUNT (*) AS count_all, status AS status
FROM "orders" GROUP BY status
大手町.rb #11 「Active Record クエリインタフェースについて」
グループ(平均、最大、最小、合計) 19
# count 以外にも average、maximum、minimum、sum がある
# ステータスがキーで、値が平均、最大等のハッシュが返る
Order.group(:status).average(:price)
Order.group(:status).maximum(:price)
Order.group(:status).minimum(:price)
Order.group(:status).sum(:price)
# 複数の列で集約するときは、Ruby on Rais 4系では group を2回書く
# キーが client_id と status の配列、値が price の合計値のハッシュが返る
Order.group(:client_id).group(:status).sum(:price)
# Ruby on Rails 5系では下記のように書ける
# Order.group(:client_id, :status).sum(:price)
大手町.rb #11 「Active Record クエリインタフェースについて」
グループ having 20
# having を使うと、集約関数の計算結果でフィルタできる
Order.select("date(created_at) as ordered_date, sum(price) as
total_price").group("date(created_at)").having("sum(price) > ?", 100)
# (SQL) SELECT date(created_at) as ordered_date, sum(price) as
total_price FROM orders GROUP BY date(created_at) HAVING sum(price) >
100
## group と having を複雑に組み合わせた例
### 3件よりも件数がある場合だけを返す
Order.group(:client_id).group(:status).having("count(1) >
3").sum(:price)
大手町.rb #11 「Active Record クエリインタフェースについて」
条件を上書きする 1/2
unscope: 条件を取り除くことができる
only: 使用する条件を特定のものに限定できる
reorder: デフォルトスコープの並び順を上書きできる
21
class Article < ApplicationRecord
has_many :comments, -> { order('posted_at DESC') }
end
article = Article.find(10)
article.comments.reorder('name')
# (SQL) SELECT * FROM comments WHERE article_id = 10 ORDER BY name
article.comments.order('name')
# (SQL) SELECT * FROM comments WHERE article_id = 10 ORDER BY
posted_at DESC, name
article.comments
# (SQL) SELECT * FROM comments WHERE article_id = 10 ORDER BY name
大手町.rb #11 「Active Record クエリインタフェースについて」
条件を上書きする 2/2
reverse_order:並び順を逆にする
rewhere: 既存の where 条件を上書きする
22
Article.where(trashed: true).rewhere(trashed: false)
# (SQL) SELECT * FROM articles WHERE `trashed` = 0
Article.where(trashed: true).where(trashed: false)
# (SQL) SELECT * FROM articles WHERE `trashed` = 1 AND `trashed` = 0
大手町.rb #11 「Active Record クエリインタフェースについて」
none: Nullリレーション
none メソッドは、結果として空のリレーションを返す
リレーションを返すことが必要でかつ、結果を返したく
ない場合にベンリ。
23
# visible_articles メソッドはリレーションを返すことが期待されている
@articles = current_user.visible_articles.where(name: params[:name])
def visible_articles
case role
when 'Country Manager'
Article.where(country: country)
when 'Reviewer'
Article.published
when 'Bad User'
Article.none # => []またはnilを返すと、このコード例では呼び出し元のコード
を壊してしまう
end
end
大手町.rb #11 「Active Record クエリインタフェースについて」
readonly: 読み取り専用オブジェクト
readonly : 変更を明示的に禁止したレコードを返す
24
client = Client.readonly.first
client.visits += 1
client.save
# ActiveRecord::ReadOnlyRecord 例外が発生する
大手町.rb #11 「Active Record クエリインタフェースについて」
楽観的ロック
楽観的ロックとは
同時編集、データの衝突があまりない利用シーンが前提
同時に同一レコードが編集された場合、あとから更新された方
について、 ActiveRecord::StaleObjectError が発生する
いわゆる「先勝ち」にできる
楽観的ロックをしなければ、最初に更新したのはまったく失われ、
あとから更新した方だけが保存して残ることになる
いわゆる「後勝ち」
integer 型の lock_version 列を作ると自動的にできる
Ruby on Rails ベンリ
フォームなどでは表示時点での lock_version 列を
hidden で埋め込んでおくことが大事
詳しくは Qiita とかで検索してみてください。
update_column や update_all は lock_version の機構を使わ
ないので楽観的ロックを使うときは注意が必要
25
大手町.rb #11 「Active Record クエリインタフェースについて」
悲観的ロック
データベースが提供するロック機構を使う
ロックが取得できない場合は、ロックできるまで待つ
多くのDBには行単位でロックできる
with_lockメソッドを使うのが一般的
26
i = Item.first
i.with_lock do
i.name = 'Jones'
i.save!
end
SQL (0.2ms) BEGIN
Item Load (0.3ms) SELECT * FROM `items`
LIMIT 1 FOR UPDATE
Item Update (0.4ms) UPDATE `items` SET
`updated_at` = '2009-02-07 18:05:56', `name` =
'Jones' WHERE `id` = 1
SQL (0.8ms) COMMIT
大手町.rb #11 「Active Record クエリインタフェースについて」
テーブルの結合(SQLフラグメント文字列)
SQL フラグメント文字列を使う方法
27
Author.joins("INNER JOIN posts ON posts.author_id = authors.id AND
posts.published = 't'")
SELECT authors.* FROM authors INNER JOIN posts ON posts.author_id =
authors.id AND posts.published = 't'
大手町.rb #11 「Active Record クエリインタフェースについて」
ここからのモデル 28
class Category < ApplicationRecord
has_many :articles
end
class Article < ApplicationRecord
belongs_to :category
has_many :comments
has_many :tags
end
class Comment < ApplicationRecord
belongs_to :article
has_one :guest
end
class Guest < ApplicationRecord
belongs_to :comment
end
class Tag < ApplicationRecord
belongs_to :article
end
大手町.rb #11 「Active Record クエリインタフェースについて」
テーブルの結合(joins) 29
Category.joins(:articles)
SELECT categories.* FROM categories
INNER JOIN articles ON articles.category_id = categories.id
Article.joins(:category, :comments)
SELECT articles.* FROM articles
INNER JOIN categories ON categories.id = articles.category_id
INNER JOIN comments ON comments.article_id = articles.id
Article.joins(comments: :guest)
SELECT articles.* FROM articles
INNER JOIN comments ON comments.article_id = articles.id
INNER JOIN guests ON guests.comment_id = comments.id
大手町.rb #11 「Active Record クエリインタフェースについて」
テーブルの結合(複数かつネストがある場合)
複雑かつネストがある場合
結合されたテーブルについて条件を書きたい場合
30
Category.joins(articles: [{ comments: :guest }, :tags])
SELECT categories.* FROM categories
INNER JOIN articles ON articles.category_id = categories.id
INNER JOIN comments ON comments.article_id = articles.id
INNER JOIN guests ON guests.comment_id = comments.id
INNER JOIN tags ON tags.article_id = articles.id
time_range = (Time.now.midnight - 1.day)..Time.now.midnight
Client.joins(:orders).where(orders: { created_at: time_range })
大手町.rb #11 「Active Record クエリインタフェースについて」
テーブルの結合(左外部結合)
左外部結合を使った例
joins だとゼロ件のときをうまく扱えないが、
left_outer_joins を使うことで正しくゼロ件も含めて
カウントできる
31
Author.left_outer_joins(:posts).distinct.select('authors.*,
COUNT(posts.*) AS posts_count').group('authors.id')
SELECT DISTINCT authors.*, COUNT(posts.*) AS posts_count
FROM "authors"
LEFT OUTER JOIN posts ON posts.author_id = authors.id
GROUP BY authors.id
大手町.rb #11 「Active Record クエリインタフェースについて」
N + 1 問合せ問題と includes
素朴に書くと、 N+1回(下記の場合、10+1 = 11回)の
DB 問合せが実行される
includes を使うことで、2回に減らすことができる
32
clients = Client.limit(10)
clients.each do |client|
puts client.address.postcode
end
clients = Client.includes(:address).limit(10)
clients.each do |client|
puts client.address.postcode
end
SELECT * FROM clients LIMIT 10
SELECT addresses.* FROM addresses
WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))
大手町.rb #11 「Active Record クエリインタフェースについて」
複雑な includes
複数のアソシエーションの指定
ハッシュを使うことで、ネストにも対応可能、配列と組合せも OK
33
Article.includes(:category, :comments)
Category.includes(articles: [{ comments: :guest }, :tags]).find(1)
大手町.rb #11 「Active Record クエリインタフェースについて」
スキップ
scope: よく使うクエリ条件に別名を与えられる
dynamic_finders: find_by_first_name みたいなやつ。
Ruby on Rails 登場当初もてはやされたが、
いまやブームが過ぎ、あまり使われない
Enums: いわゆる列挙型。スコープが自動生成され便利
メソッドチェイン:メソッドを次々つなげるやつ
検索とビルド: あれば更新なければ作成したいときに便利
34
大手町.rb #11 「Active Record クエリインタフェースについて」
SQL の直接利用(select_all)
select_all を使うと SQL を直接発行できる
ActiveRecord ではなく connection のメソッド
私は頻繁に間違える
返り値を to_hash すると、ハッシュの配列になる
遅い ActiveRecord の生成処理が不要になる分高速化
select_all 以外にも SQL を直接利用する方法はある
execute、select_one、select_rows, select_values, select_value
select_all が一番使うし、ほかはあまり使わない
35
Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE
id = '1'").to_hash
# => [
# {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"},
# {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"}
# ]
大手町.rb #11 「Active Record クエリインタフェースについて」
pluck、ids
pluck を使うと高速化できる
下記の書き方をリファクタリングできる
36
Client.where(active: true).pluck(:id)
# SELECT id FROM clients WHERE active = 1
# => [1, 2, 3]
Client.distinct.pluck(:role)
# SELECT DISTINCT role FROM clients
# => ['admin', 'member', 'guest']
Client.pluck(:id, :name)
# SELECT clients.id, clients.name FROM clients
# => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
Client.select(:id).map { |c| c.id } # Client.pluck(:id)、 Client.ids と同じ
Client.select(:id).map(&:id) # Client.pluck(:id)、 Client.ids と同じ
Client.select(:id, :name).map { |c| [c.id, c.name] }
# Client.pluck(:id, :name)
大手町.rb #11 「Active Record クエリインタフェースについて」
オブジェクトの存在チェック 37
# 引数のある exists?
Client.exists?(id: [1,2,3])
Client.exists?(name: ['John', 'Sergei'])
# 引数のない exists?
Client.where(first_name: 'Ryan').exists?
# via a model
Article.any? # .count > 0 と同じ。 .present? よりも速い。(ARオブジェクトを生成しない)
Article.many? # .count > 1 と同じ。
# 名前付きスコープを経由
Article.recent.any?
Article.recent.many?
# リレーション経由
Article.where(published: true).any?
Article.where(published: true).many?
# 関連付け経由
Article.first.categories.any? # アソシエーションでも使える
Article.first.categories.many?
大手町.rb #11 「Active Record クエリインタフェースについて」
まとめ
今日は Active Record クエリインタフェースについて、
ピックアップして、紹介しました。
個人的には joins や eager_load でネストして
取得ができるのが便利だと思っています。
ドキュメントを熟読しましょう!
気が付かなかった発見がたくさんあります。
私自身、たくさんありました
Rails Guide と Ruby on Rails API は必読です。
https://railsguides.jp/
https://api.rubyonrails.org/
38
ご清聴ありがとう
ございました

More Related Content

What's hot

Ruby on Rails on MySQL チューニング入門
Ruby on Rails on MySQL チューニング入門Ruby on Rails on MySQL チューニング入門
Ruby on Rails on MySQL チューニング入門だいすけ さとう
 
PHPの今とこれから2021
PHPの今とこれから2021PHPの今とこれから2021
PHPの今とこれから2021Rui Hirokawa
 
about Thrift
about Thriftabout Thrift
about ThriftNaoya Ito
 
最新PHP事情 (2000年7月22日,PHPカンファレンス)
最新PHP事情 (2000年7月22日,PHPカンファレンス)最新PHP事情 (2000年7月22日,PHPカンファレンス)
最新PHP事情 (2000年7月22日,PHPカンファレンス)Rui Hirokawa
 
Hack/HHVM 入門
Hack/HHVM 入門Hack/HHVM 入門
Hack/HHVM 入門y-uti
 
WebAPIではじめるphp入門
WebAPIではじめるphp入門WebAPIではじめるphp入門
WebAPIではじめるphp入門Hiroaki Murayama
 
勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration勉強会force#4 Chatter Integration
勉強会force#4 Chatter IntegrationKazuki Nakajima
 
Elasticsearch入門 pyfes 201207
Elasticsearch入門 pyfes 201207Elasticsearch入門 pyfes 201207
Elasticsearch入門 pyfes 201207Jun Ohtani
 
PHPからJavaへ乗り換えた。そんな昔話をしよう
PHPからJavaへ乗り換えた。そんな昔話をしようPHPからJavaへ乗り換えた。そんな昔話をしよう
PHPからJavaへ乗り換えた。そんな昔話をしよう優介 黒河
 
PHPの今とこれから 2013
PHPの今とこれから 2013PHPの今とこれから 2013
PHPの今とこれから 2013Rui Hirokawa
 
Titanium Mobile
Titanium MobileTitanium Mobile
Titanium MobileNaoya Ito
 
PHPの今とこれから2014
PHPの今とこれから2014PHPの今とこれから2014
PHPの今とこれから2014Rui Hirokawa
 
Doma SQLテンプレートのしくみ
Doma SQLテンプレートのしくみDoma SQLテンプレートのしくみ
Doma SQLテンプレートのしくみToshihiro Nakamura
 
PHP, Now and Then 2011
PHP, Now and Then 2011PHP, Now and Then 2011
PHP, Now and Then 2011Rui Hirokawa
 
FxUG in Toyama - ASphalt2 container -
FxUG in Toyama - ASphalt2 container -FxUG in Toyama - ASphalt2 container -
FxUG in Toyama - ASphalt2 container -Akio Katayama
 
O/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐO/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐkwatch
 
分割と整合性と戦う
分割と整合性と戦う分割と整合性と戦う
分割と整合性と戦うYugo Shimizu
 

What's hot (20)

Ruby on Rails on MySQL チューニング入門
Ruby on Rails on MySQL チューニング入門Ruby on Rails on MySQL チューニング入門
Ruby on Rails on MySQL チューニング入門
 
Zabbix API
Zabbix APIZabbix API
Zabbix API
 
PHPの今とこれから2021
PHPの今とこれから2021PHPの今とこれから2021
PHPの今とこれから2021
 
about Thrift
about Thriftabout Thrift
about Thrift
 
最新PHP事情 (2000年7月22日,PHPカンファレンス)
最新PHP事情 (2000年7月22日,PHPカンファレンス)最新PHP事情 (2000年7月22日,PHPカンファレンス)
最新PHP事情 (2000年7月22日,PHPカンファレンス)
 
Hack/HHVM 入門
Hack/HHVM 入門Hack/HHVM 入門
Hack/HHVM 入門
 
WebAPIではじめるphp入門
WebAPIではじめるphp入門WebAPIではじめるphp入門
WebAPIではじめるphp入門
 
勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration
 
Elasticsearch入門 pyfes 201207
Elasticsearch入門 pyfes 201207Elasticsearch入門 pyfes 201207
Elasticsearch入門 pyfes 201207
 
PHPからJavaへ乗り換えた。そんな昔話をしよう
PHPからJavaへ乗り換えた。そんな昔話をしようPHPからJavaへ乗り換えた。そんな昔話をしよう
PHPからJavaへ乗り換えた。そんな昔話をしよう
 
PHPの今とこれから 2013
PHPの今とこれから 2013PHPの今とこれから 2013
PHPの今とこれから 2013
 
Titanium Mobile
Titanium MobileTitanium Mobile
Titanium Mobile
 
VBCPP - ICT+R 2012
VBCPP - ICT+R 2012VBCPP - ICT+R 2012
VBCPP - ICT+R 2012
 
PHPの今とこれから2014
PHPの今とこれから2014PHPの今とこれから2014
PHPの今とこれから2014
 
Doma SQLテンプレートのしくみ
Doma SQLテンプレートのしくみDoma SQLテンプレートのしくみ
Doma SQLテンプレートのしくみ
 
PHP, Now and Then 2011
PHP, Now and Then 2011PHP, Now and Then 2011
PHP, Now and Then 2011
 
HHVM Hack
HHVM HackHHVM Hack
HHVM Hack
 
FxUG in Toyama - ASphalt2 container -
FxUG in Toyama - ASphalt2 container -FxUG in Toyama - ASphalt2 container -
FxUG in Toyama - ASphalt2 container -
 
O/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐO/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐ
 
分割と整合性と戦う
分割と整合性と戦う分割と整合性と戦う
分割と整合性と戦う
 

Similar to Active record query interface

2019年度若手技術者向け講座 実践SQL
2019年度若手技術者向け講座 実践SQL2019年度若手技術者向け講座 実践SQL
2019年度若手技術者向け講座 実践SQLkeki3
 
ElastiCacheを利用する上でキャッシュをどのように有効に使うべきか
ElastiCacheを利用する上でキャッシュをどのように有効に使うべきかElastiCacheを利用する上でキャッシュをどのように有効に使うべきか
ElastiCacheを利用する上でキャッシュをどのように有効に使うべきかAmazon Web Services Japan
 
Ruby の String のメソッドについて
Ruby の String のメソッドについてRuby の String のメソッドについて
Ruby の String のメソッドについてTomoya Kawanishi
 
負荷テストことはじめ
負荷テストことはじめ負荷テストことはじめ
負荷テストことはじめKazumune Katagiri
 
Active Support のコア拡張機能について
Active Support のコア拡張機能についてActive Support のコア拡張機能について
Active Support のコア拡張機能についてTomoya Kawanishi
 
Ruby の制御構造とリテラルについて
Ruby の制御構造とリテラルについてRuby の制御構造とリテラルについて
Ruby の制御構造とリテラルについてTomoya Kawanishi
 
Using Amazon Aurora for Enterprise Workloads
Using Amazon Aurora for Enterprise WorkloadsUsing Amazon Aurora for Enterprise Workloads
Using Amazon Aurora for Enterprise WorkloadsAmazon Web Services Japan
 
ストリーム処理エンジン「Zero」の開発と運用
ストリーム処理エンジン「Zero」の開発と運用ストリーム処理エンジン「Zero」の開発と運用
ストリーム処理エンジン「Zero」の開発と運用Eiichi Sato
 
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい) 泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい) Akihiro Kuwano
 
Cookpad 17 day Tech internship 2017 言語処理系入門 Rubyをコンパイルしよう
Cookpad 17 day Tech internship 2017 言語処理系入門 RubyをコンパイルしようCookpad 17 day Tech internship 2017 言語処理系入門 Rubyをコンパイルしよう
Cookpad 17 day Tech internship 2017 言語処理系入門 RubyをコンパイルしようKoichi Sasada
 
MlnagoyaRx02
MlnagoyaRx02MlnagoyaRx02
MlnagoyaRx02mega80b
 
Complex Event Processing on Ruby, Fluentd and Norikra #rubykaigi
Complex Event Processing on Ruby, Fluentd and Norikra #rubykaigiComplex Event Processing on Ruby, Fluentd and Norikra #rubykaigi
Complex Event Processing on Ruby, Fluentd and Norikra #rubykaigiSATOSHI TAGOMORI
 
Programming Hive Reading #3
Programming Hive Reading #3Programming Hive Reading #3
Programming Hive Reading #3moai kids
 
新しくプログラミング言語・・・Rubyでやってみた
新しくプログラミング言語・・・Rubyでやってみた新しくプログラミング言語・・・Rubyでやってみた
新しくプログラミング言語・・・RubyでやってみたTomoaki Ueda
 
Net opscoding#4発表資料
Net opscoding#4発表資料Net opscoding#4発表資料
Net opscoding#4発表資料Kenta Hattori
 
Data processing at spotify using scio
Data processing at spotify using scioData processing at spotify using scio
Data processing at spotify using scioJulien Tournay
 
Ruby での外部コマンドの実行について
Ruby での外部コマンドの実行についてRuby での外部コマンドの実行について
Ruby での外部コマンドの実行についてTomoya Kawanishi
 

Similar to Active record query interface (20)

2019年度若手技術者向け講座 実践SQL
2019年度若手技術者向け講座 実践SQL2019年度若手技術者向け講座 実践SQL
2019年度若手技術者向け講座 実践SQL
 
ElastiCacheを利用する上でキャッシュをどのように有効に使うべきか
ElastiCacheを利用する上でキャッシュをどのように有効に使うべきかElastiCacheを利用する上でキャッシュをどのように有効に使うべきか
ElastiCacheを利用する上でキャッシュをどのように有効に使うべきか
 
Ruby の String のメソッドについて
Ruby の String のメソッドについてRuby の String のメソッドについて
Ruby の String のメソッドについて
 
負荷テストことはじめ
負荷テストことはじめ負荷テストことはじめ
負荷テストことはじめ
 
Active Support のコア拡張機能について
Active Support のコア拡張機能についてActive Support のコア拡張機能について
Active Support のコア拡張機能について
 
MlnagoyaRx
MlnagoyaRxMlnagoyaRx
MlnagoyaRx
 
Ruby の制御構造とリテラルについて
Ruby の制御構造とリテラルについてRuby の制御構造とリテラルについて
Ruby の制御構造とリテラルについて
 
Haikara
HaikaraHaikara
Haikara
 
Using Amazon Aurora for Enterprise Workloads
Using Amazon Aurora for Enterprise WorkloadsUsing Amazon Aurora for Enterprise Workloads
Using Amazon Aurora for Enterprise Workloads
 
ストリーム処理エンジン「Zero」の開発と運用
ストリーム処理エンジン「Zero」の開発と運用ストリーム処理エンジン「Zero」の開発と運用
ストリーム処理エンジン「Zero」の開発と運用
 
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい) 泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
 
Cookpad 17 day Tech internship 2017 言語処理系入門 Rubyをコンパイルしよう
Cookpad 17 day Tech internship 2017 言語処理系入門 RubyをコンパイルしようCookpad 17 day Tech internship 2017 言語処理系入門 Rubyをコンパイルしよう
Cookpad 17 day Tech internship 2017 言語処理系入門 Rubyをコンパイルしよう
 
MlnagoyaRx02
MlnagoyaRx02MlnagoyaRx02
MlnagoyaRx02
 
Complex Event Processing on Ruby, Fluentd and Norikra #rubykaigi
Complex Event Processing on Ruby, Fluentd and Norikra #rubykaigiComplex Event Processing on Ruby, Fluentd and Norikra #rubykaigi
Complex Event Processing on Ruby, Fluentd and Norikra #rubykaigi
 
Programming Hive Reading #3
Programming Hive Reading #3Programming Hive Reading #3
Programming Hive Reading #3
 
Aerospike deep dive LDTs
Aerospike deep dive LDTsAerospike deep dive LDTs
Aerospike deep dive LDTs
 
新しくプログラミング言語・・・Rubyでやってみた
新しくプログラミング言語・・・Rubyでやってみた新しくプログラミング言語・・・Rubyでやってみた
新しくプログラミング言語・・・Rubyでやってみた
 
Net opscoding#4発表資料
Net opscoding#4発表資料Net opscoding#4発表資料
Net opscoding#4発表資料
 
Data processing at spotify using scio
Data processing at spotify using scioData processing at spotify using scio
Data processing at spotify using scio
 
Ruby での外部コマンドの実行について
Ruby での外部コマンドの実行についてRuby での外部コマンドの実行について
Ruby での外部コマンドの実行について
 

More from Tomoya Kawanishi

ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例Tomoya Kawanishi
 
エンジニア転職のノウハウ
エンジニア転職のノウハウエンジニア転職のノウハウ
エンジニア転職のノウハウTomoya Kawanishi
 
Ruby の文字列について
Ruby の文字列についてRuby の文字列について
Ruby の文字列についてTomoya Kawanishi
 
Ruby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構についてRuby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構についてTomoya Kawanishi
 
Ruby初心者からよく質問されること
Ruby初心者からよく質問されることRuby初心者からよく質問されること
Ruby初心者からよく質問されることTomoya Kawanishi
 
RubyGems と Bundler について
RubyGems と Bundler についてRubyGems と Bundler について
RubyGems と Bundler についてTomoya Kawanishi
 
Ruby の正規表現について
Ruby の正規表現についてRuby の正規表現について
Ruby の正規表現についてTomoya Kawanishi
 
Ruby のワンライナーについて
Ruby のワンライナーについてRuby のワンライナーについて
Ruby のワンライナーについてTomoya Kawanishi
 
AWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったことAWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったことTomoya Kawanishi
 
PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選Tomoya Kawanishi
 
HTTPと Webクローリングについて
HTTPと WebクローリングについてHTTPと Webクローリングについて
HTTPと WebクローリングについてTomoya Kawanishi
 
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナーRuby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナーTomoya Kawanishi
 
RubyのDir、File、IO について
RubyのDir、File、IO についてRubyのDir、File、IO について
RubyのDir、File、IO についてTomoya Kawanishi
 
Thread の利用事例紹介
Thread の利用事例紹介Thread の利用事例紹介
Thread の利用事例紹介Tomoya Kawanishi
 
RubyGems と Bundler について
RubyGems と Bundler についてRubyGems と Bundler について
RubyGems と Bundler についてTomoya Kawanishi
 
Ruby on Rails プロジェクトでの他言語エコシステムとの共存方法
Ruby on Rails プロジェクトでの他言語エコシステムとの共存方法Ruby on Rails プロジェクトでの他言語エコシステムとの共存方法
Ruby on Rails プロジェクトでの他言語エコシステムとの共存方法Tomoya Kawanishi
 
マークアップで使えるRuby
マークアップで使えるRubyマークアップで使えるRuby
マークアップで使えるRubyTomoya Kawanishi
 
エネチェンジでの Side ci 利用事例について
エネチェンジでの Side ci 利用事例についてエネチェンジでの Side ci 利用事例について
エネチェンジでの Side ci 利用事例についてTomoya Kawanishi
 

More from Tomoya Kawanishi (20)

英単語の覚え方
英単語の覚え方英単語の覚え方
英単語の覚え方
 
ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例
 
エンジニア転職のノウハウ
エンジニア転職のノウハウエンジニア転職のノウハウ
エンジニア転職のノウハウ
 
Ruby の文字列について
Ruby の文字列についてRuby の文字列について
Ruby の文字列について
 
Ruby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構についてRuby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構について
 
Ruby初心者からよく質問されること
Ruby初心者からよく質問されることRuby初心者からよく質問されること
Ruby初心者からよく質問されること
 
RubyGems と Bundler について
RubyGems と Bundler についてRubyGems と Bundler について
RubyGems と Bundler について
 
Ruby の正規表現について
Ruby の正規表現についてRuby の正規表現について
Ruby の正規表現について
 
Ruby のワンライナーについて
Ruby のワンライナーについてRuby のワンライナーについて
Ruby のワンライナーについて
 
AWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったことAWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったこと
 
PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選
 
HTTPと Webクローリングについて
HTTPと WebクローリングについてHTTPと Webクローリングについて
HTTPと Webクローリングについて
 
Rake
RakeRake
Rake
 
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナーRuby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナー
 
RubyのDir、File、IO について
RubyのDir、File、IO についてRubyのDir、File、IO について
RubyのDir、File、IO について
 
Thread の利用事例紹介
Thread の利用事例紹介Thread の利用事例紹介
Thread の利用事例紹介
 
RubyGems と Bundler について
RubyGems と Bundler についてRubyGems と Bundler について
RubyGems と Bundler について
 
Ruby on Rails プロジェクトでの他言語エコシステムとの共存方法
Ruby on Rails プロジェクトでの他言語エコシステムとの共存方法Ruby on Rails プロジェクトでの他言語エコシステムとの共存方法
Ruby on Rails プロジェクトでの他言語エコシステムとの共存方法
 
マークアップで使えるRuby
マークアップで使えるRubyマークアップで使えるRuby
マークアップで使えるRuby
 
エネチェンジでの Side ci 利用事例について
エネチェンジでの Side ci 利用事例についてエネチェンジでの Side ci 利用事例について
エネチェンジでの Side ci 利用事例について
 

Recently uploaded

[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略Ryo Sasaki
 
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Yuma Ohgami
 
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)Hiroki Ichikura
 
論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A surveyToru Tamaki
 
TSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdfTSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdftaisei2219
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムsugiuralab
 
SOPを理解する 2024/04/19 の勉強会で発表されたものです
SOPを理解する       2024/04/19 の勉強会で発表されたものですSOPを理解する       2024/04/19 の勉強会で発表されたものです
SOPを理解する 2024/04/19 の勉強会で発表されたものですiPride Co., Ltd.
 
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...Toru Tamaki
 
論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNetToru Tamaki
 

Recently uploaded (9)

[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
 
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
 
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
 
論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey
 
TSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdfTSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdf
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システム
 
SOPを理解する 2024/04/19 の勉強会で発表されたものです
SOPを理解する       2024/04/19 の勉強会で発表されたものですSOPを理解する       2024/04/19 の勉強会で発表されたものです
SOPを理解する 2024/04/19 の勉強会で発表されたものです
 
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
 
論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet
 

Active record query interface

  • 2. 大手町.rb #11 「Active Record クエリインタフェースについて」 1自己紹介 Tomoya Kawanishi a.k.a. @cuzic エネチェンジ株式会社 チーフエンジニア 電力会社、ガス会社を切り替えるなら、エネチェンジ経由で! 一般家庭も!法人も! Ruby関西の中の人 発表者として登壇くださる方、あとで声かけください。 第84回 Ruby関西勉強会 12月1日(土) 大手町.rb の中の人 毎月 大手町.rb の開催を予定 東京駅、各線大手町駅から直結! Ruby の初級者がメインターゲット
  • 3. 大手町.rb #11 「Active Record クエリインタフェースについて」 Disclaimer 大手町.rb は祝!11回! 人数がとても増えてきました! 大手町.rb は(比較的)初級者向けの勉強会 とはいえ、初級者向けのままだと、ネタ切れしちゃう 同じネタをリピートするか、レベルアップするか。 大手町.rb はレベルアップしていく方向! 今のオーディエンスを大切にしていく! 2
  • 4. 大手町.rb #11 「Active Record クエリインタフェースについて」 今日のテーマ Active Record クエリインタフェースについて Ruby on Rails を使うなら、みんな使ってる? とはいえ、時間をとって学ぶことがあまりない というのも事実 Rails ガイドを熟読すればいいのですが、 なかなか上から読むのは大変です。 今日は一緒に学んでいきましょう 3
  • 5. 大手町.rb #11 「Active Record クエリインタフェースについて」 前提となるモデル Rails ガイドの内容をベースに紹介します。 もろもろ、Rails の命名規則に従っている前提です。 id が主キー 4 class Role < ApplicationRecord has_and_belongs_to_many :clients end class Address < ApplicationRecord belongs_to :client end class Order < ApplicationRecord belongs_to :client, counter_cache: true end class Client < ApplicationRecord has_one :address has_many :orders has_and_belongs_to_many :roles end
  • 6. 大手町.rb #11 「Active Record クエリインタフェースについて」 find: 主キーによるレコードの取得 find :主キーにマッチするオブジェクトを取得 複数レコードを同時に取得できる 5 clients = Client.find(10) #=> #<Client id: 10, first_name: "Ryan"> SELECT * FROM clients WHERE (clients.id = 10) LIMIT 1 clients = Client.find([1, 10]) # Client.find(1, 10) でも同じ結果 # => [ # <Client id: 1, first_name: "Lifo">, # <Client id: 10, first_name: "Ryan"> # ] SELECT * FROM clients WHERE (clients.id IN (1,10)) 対応するレコードがないと、 ActiveRecord::RecordNotFound 例外が発生する # Client.find([1, 2], [3, 4]) のような書き方もできる
  • 7. 大手町.rb #11 「Active Record クエリインタフェースについて」 take: 任意のレコードの取得 take :どのレコードか指定せず、1レコード取得 take(2) で最大2個のレコードを取得 モデルに1つもレコードがない場合は nil を返す 6 Client.take #=> #<Client id: 1, first_name: "Lifo">, SELECT * FROM clients LIMIT 1 clients = Client.take(2) # Client.find(1, 10) でも同じ結果 # => [ # #<Client id: 1, first_name: "Lifo">, # #<Client id: 220, first_name: "Sara"> # ] SELECT * FROM clients LIMIT 2 take! を使うと、マッチしない場合、ActiveRecord::RecordNotFound 例外が発生する
  • 8. 大手町.rb #11 「Active Record クエリインタフェースについて」 first:最初のレコードを取得 first:主キー順で最初のレコードを取得 デフォルトスコープがあれば、 デフォルトスコープで指定された順序で最初 first(3) で3個のレコードを取得できる 7 Client.first #=> #<Client id: 1, first_name: "Lifo">, SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1 clients = Client.first(3) # => [ # #<Client id: 1, first_name: "Lifo">, # #<Client id: 2, first_name: "Fifo">, # #<Client id: 3, first_name: "Filo"> # ] SELECT * FROM clients ORDER BY clients.id ASC LIMIT 3 order を使って順序を変更すると、first はその順で最初のレコードを返す
  • 9. 大手町.rb #11 「Active Record クエリインタフェースについて」 last:最後のレコードを取得 last:主キー順で最後のレコードを取得 デフォルトスコープがあれば、 デフォルトスコープで指定された順序で最後 last(3) で3個のレコードを取得できる 8 Client.last #=> #<Client id: 221, first_name: "Russel"> SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 clients = Client.last(3) # => [ # #<Client id: 219, first_name: "James">, # #<Client id: 220, first_name: "Sara">, # #<Client id: 221, first_name: "Russel"> # ] SELECT * FROM clients ORDER BY clients.id DESC LIMIT 3 order を使って順序を変更すると、last はその順で最後のレコードを返す
  • 10. 大手町.rb #11 「Active Record クエリインタフェースについて」 find_by:マッチする最初のレコードを取得 find_by:与えられた条件にマッチする最初のレコード 9 Client.find_by(first_name: 'Lifo') #=> #<Client id: 1, first_name: "Lifo"> # Client.where(first_name: 'Lifo').take と同じ意味 SELECT * FROM clients WHERE (clients.first_name = 'Lifo') LIMIT 1 find_by! を使うと、マッチするレコードが見つからなかった場合に、 ActiveRecord::RecordNotFound例外が発生する
  • 11. 大手町.rb #11 「Active Record クエリインタフェースについて」 find_each : バッチ処理でメモリを節約 多数のレコードに対して反復処理するとき、 素朴に実装すると、メモリが大量に必要となる find_each を使うとメモリを節約できる ただし、数千件程度であれば、素朴な実装でも問題ない 10 # User.all のためのメモリを消費する User.all.each do |user| NewsMailer.weekly(user).deliver_now end # レコードを 1,000件ずつ取得する User.find_each do |user| NewsMailer.weekly(user).deliver_now end # レコードを 500件ずつ、id=2000 から id=10000 番まで順に処理 User.find_each(batch_size: 500, start: 2000, finish: 10000) do |user| NewsMailer.weekly(user).deliver_now end
  • 12. 大手町.rb #11 「Active Record クエリインタフェースについて」 find_in_batches: レコードの配列でバッチ処理 find_each は省メモリだが、使う側は1件ずつ find_in_batches では使う側も1度に 1000件処理できる ユースケース 複数のワーカスレッド、ジョブキューなどを使っている 各ワーカスレッドに 1,000件ずつ投入するときなどにベンリ 11 # 1回あたりadd_invoicesに納品書1000通の配列を渡す Invoice.find_in_batches do |invoices| export.add_invoices(invoices) end
  • 13. 大手町.rb #11 「Active Record クエリインタフェースについて」 ハッシュを使用した条件 1/2 where はハッシュを使うと簡潔に書けて読みやすい 12 # 等値条件 Client.where(locked: true) # (SQL) SELECT * FROM clients WHERE (clients.locked = 1) # belongs_to リレーションシップを使った関連付け Article.where(author: author) # where(author_id: author.id) と同じ意味になる # id は省略でき、簡潔に書ける # 範囲条件 Client.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight) # (SQL) SELECT * FROM clients WHERE (clients.created_at BETWEEN '2008-12-21 00:00:00' AND '2008-12-22 00:00:00')
  • 14. 大手町.rb #11 「Active Record クエリインタフェースについて」 ハッシュを使用した条件 2/2 .or の使い方が覚えにくい。 必要な都度検索し、調べれば OK 13 # サブセット条件 Client.where(orders_count: [1,3,5]) # (SQL) SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5)) # NOT条件 Client.where.not(locked: true) # OR条件 Client.where(locked: true). or(Client.where(orders_count: [1,3,5])) # (SQL) SELECT * FROM clients WHERE (clients.locked = 1 OR clients.orders_count IN (1,3,5))
  • 15. 大手町.rb #11 「Active Record クエリインタフェースについて」 引数による条件 14 # サブセット条件 Client.where("orders_count = ?", params[:orders]) # (SQL) SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5)) # 複数の条件を一度に指定 Client.where("orders_count = ? AND locked = ?", params[:orders], false) # 個人的には Client.where(orders_count: params[:orders]). # where(locked: false) のように書くことが多い # Client.where("orders_count = #{params[:orders]}") はダメ。絶対。 # SQL インジェクション攻撃のおそれがある # プレースホルダを使って、記述することもできる Client.where("created_at >= :start_date AND created_at <= :end_date", start_date: params[:start_date], end_date: params[:end_date])
  • 16. 大手町.rb #11 「Active Record クエリインタフェースについて」 並び順 15 Client.order(:created_at) # デフォルトは昇順 Client.order("created_at") Client.order(created_at: :desc) # 降順 Client.order(created_at: :asc) # :asc はなくてもいいけど、あってもいい Client.order("created_at DESC") # 文字列で明示的に指定することもできる Client.order("created_at ASC") Client.order(orders_count: :asc, created_at: :desc) Client.order(:orders_count, created_at: :desc) Client.order("orders_count ASC, created_at DESC") Client.order("orders_count ASC", "created_at DESC") Client.order("orders_count ASC").order("created_at DESC") # SELECT * FROM clients ORDER BY orders_count ASC, created_at DESC
  • 17. 大手町.rb #11 「Active Record クエリインタフェースについて」 特定のフィールドだけを取り出す 16 Client.select("viewable_by, locked") Client.select(:viewable_by, :locked) # 個人的には select は下記の場合に使うことが多い ## 1. アソシエーション先だけが欲しいとき Order.where("created_at > ?", 2.days.ago). select(:client_id).distinct.map(&:client) ## 2. 複雑な SQL を使ったフィルタをしたいとき Client.where(id: Order.complex_scope.select(:client_id).distinct) ## complex_scope のところはなんか複雑なことをしていると想像してください
  • 18. 大手町.rb #11 「Active Record クエリインタフェースについて」 limit と offset limit と offset は1ページあたり10件表示とかで、 次のページのレコードを取得するときなどによく使う 17 Client.limit(5) # SELECT * FROM clients LIMIT 5 Client.limit(5).offset(30) # SELECT * FROM clients LIMIT 5 OFFSET 30
  • 19. 大手町.rb #11 「Active Record クエリインタフェースについて」 グループ(カウント) 18 # 日付ごとの合計金額 # Order の配列が返る Order.select("date_trunc(created_at, 'date') as ordered_date, sum(price) as total_price").group("date(created_at)") # (SQL) SELECT date(created_at) as ordered_date, sum(price) as total_price FROM orders GROUP BY date(created_at) # ステータスごとの件数 # ステータスがキーで、件数が値のハッシュが返る Order.group(:status).count # => { 'awaiting_approval' => 7, 'paid' => 12 } # (SQL) SELECT COUNT (*) AS count_all, status AS status FROM "orders" GROUP BY status
  • 20. 大手町.rb #11 「Active Record クエリインタフェースについて」 グループ(平均、最大、最小、合計) 19 # count 以外にも average、maximum、minimum、sum がある # ステータスがキーで、値が平均、最大等のハッシュが返る Order.group(:status).average(:price) Order.group(:status).maximum(:price) Order.group(:status).minimum(:price) Order.group(:status).sum(:price) # 複数の列で集約するときは、Ruby on Rais 4系では group を2回書く # キーが client_id と status の配列、値が price の合計値のハッシュが返る Order.group(:client_id).group(:status).sum(:price) # Ruby on Rails 5系では下記のように書ける # Order.group(:client_id, :status).sum(:price)
  • 21. 大手町.rb #11 「Active Record クエリインタフェースについて」 グループ having 20 # having を使うと、集約関数の計算結果でフィルタできる Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("date(created_at)").having("sum(price) > ?", 100) # (SQL) SELECT date(created_at) as ordered_date, sum(price) as total_price FROM orders GROUP BY date(created_at) HAVING sum(price) > 100 ## group と having を複雑に組み合わせた例 ### 3件よりも件数がある場合だけを返す Order.group(:client_id).group(:status).having("count(1) > 3").sum(:price)
  • 22. 大手町.rb #11 「Active Record クエリインタフェースについて」 条件を上書きする 1/2 unscope: 条件を取り除くことができる only: 使用する条件を特定のものに限定できる reorder: デフォルトスコープの並び順を上書きできる 21 class Article < ApplicationRecord has_many :comments, -> { order('posted_at DESC') } end article = Article.find(10) article.comments.reorder('name') # (SQL) SELECT * FROM comments WHERE article_id = 10 ORDER BY name article.comments.order('name') # (SQL) SELECT * FROM comments WHERE article_id = 10 ORDER BY posted_at DESC, name article.comments # (SQL) SELECT * FROM comments WHERE article_id = 10 ORDER BY name
  • 23. 大手町.rb #11 「Active Record クエリインタフェースについて」 条件を上書きする 2/2 reverse_order:並び順を逆にする rewhere: 既存の where 条件を上書きする 22 Article.where(trashed: true).rewhere(trashed: false) # (SQL) SELECT * FROM articles WHERE `trashed` = 0 Article.where(trashed: true).where(trashed: false) # (SQL) SELECT * FROM articles WHERE `trashed` = 1 AND `trashed` = 0
  • 24. 大手町.rb #11 「Active Record クエリインタフェースについて」 none: Nullリレーション none メソッドは、結果として空のリレーションを返す リレーションを返すことが必要でかつ、結果を返したく ない場合にベンリ。 23 # visible_articles メソッドはリレーションを返すことが期待されている @articles = current_user.visible_articles.where(name: params[:name]) def visible_articles case role when 'Country Manager' Article.where(country: country) when 'Reviewer' Article.published when 'Bad User' Article.none # => []またはnilを返すと、このコード例では呼び出し元のコード を壊してしまう end end
  • 25. 大手町.rb #11 「Active Record クエリインタフェースについて」 readonly: 読み取り専用オブジェクト readonly : 変更を明示的に禁止したレコードを返す 24 client = Client.readonly.first client.visits += 1 client.save # ActiveRecord::ReadOnlyRecord 例外が発生する
  • 26. 大手町.rb #11 「Active Record クエリインタフェースについて」 楽観的ロック 楽観的ロックとは 同時編集、データの衝突があまりない利用シーンが前提 同時に同一レコードが編集された場合、あとから更新された方 について、 ActiveRecord::StaleObjectError が発生する いわゆる「先勝ち」にできる 楽観的ロックをしなければ、最初に更新したのはまったく失われ、 あとから更新した方だけが保存して残ることになる いわゆる「後勝ち」 integer 型の lock_version 列を作ると自動的にできる Ruby on Rails ベンリ フォームなどでは表示時点での lock_version 列を hidden で埋め込んでおくことが大事 詳しくは Qiita とかで検索してみてください。 update_column や update_all は lock_version の機構を使わ ないので楽観的ロックを使うときは注意が必要 25
  • 27. 大手町.rb #11 「Active Record クエリインタフェースについて」 悲観的ロック データベースが提供するロック機構を使う ロックが取得できない場合は、ロックできるまで待つ 多くのDBには行単位でロックできる with_lockメソッドを使うのが一般的 26 i = Item.first i.with_lock do i.name = 'Jones' i.save! end SQL (0.2ms) BEGIN Item Load (0.3ms) SELECT * FROM `items` LIMIT 1 FOR UPDATE Item Update (0.4ms) UPDATE `items` SET `updated_at` = '2009-02-07 18:05:56', `name` = 'Jones' WHERE `id` = 1 SQL (0.8ms) COMMIT
  • 28. 大手町.rb #11 「Active Record クエリインタフェースについて」 テーブルの結合(SQLフラグメント文字列) SQL フラグメント文字列を使う方法 27 Author.joins("INNER JOIN posts ON posts.author_id = authors.id AND posts.published = 't'") SELECT authors.* FROM authors INNER JOIN posts ON posts.author_id = authors.id AND posts.published = 't'
  • 29. 大手町.rb #11 「Active Record クエリインタフェースについて」 ここからのモデル 28 class Category < ApplicationRecord has_many :articles end class Article < ApplicationRecord belongs_to :category has_many :comments has_many :tags end class Comment < ApplicationRecord belongs_to :article has_one :guest end class Guest < ApplicationRecord belongs_to :comment end class Tag < ApplicationRecord belongs_to :article end
  • 30. 大手町.rb #11 「Active Record クエリインタフェースについて」 テーブルの結合(joins) 29 Category.joins(:articles) SELECT categories.* FROM categories INNER JOIN articles ON articles.category_id = categories.id Article.joins(:category, :comments) SELECT articles.* FROM articles INNER JOIN categories ON categories.id = articles.category_id INNER JOIN comments ON comments.article_id = articles.id Article.joins(comments: :guest) SELECT articles.* FROM articles INNER JOIN comments ON comments.article_id = articles.id INNER JOIN guests ON guests.comment_id = comments.id
  • 31. 大手町.rb #11 「Active Record クエリインタフェースについて」 テーブルの結合(複数かつネストがある場合) 複雑かつネストがある場合 結合されたテーブルについて条件を書きたい場合 30 Category.joins(articles: [{ comments: :guest }, :tags]) SELECT categories.* FROM categories INNER JOIN articles ON articles.category_id = categories.id INNER JOIN comments ON comments.article_id = articles.id INNER JOIN guests ON guests.comment_id = comments.id INNER JOIN tags ON tags.article_id = articles.id time_range = (Time.now.midnight - 1.day)..Time.now.midnight Client.joins(:orders).where(orders: { created_at: time_range })
  • 32. 大手町.rb #11 「Active Record クエリインタフェースについて」 テーブルの結合(左外部結合) 左外部結合を使った例 joins だとゼロ件のときをうまく扱えないが、 left_outer_joins を使うことで正しくゼロ件も含めて カウントできる 31 Author.left_outer_joins(:posts).distinct.select('authors.*, COUNT(posts.*) AS posts_count').group('authors.id') SELECT DISTINCT authors.*, COUNT(posts.*) AS posts_count FROM "authors" LEFT OUTER JOIN posts ON posts.author_id = authors.id GROUP BY authors.id
  • 33. 大手町.rb #11 「Active Record クエリインタフェースについて」 N + 1 問合せ問題と includes 素朴に書くと、 N+1回(下記の場合、10+1 = 11回)の DB 問合せが実行される includes を使うことで、2回に減らすことができる 32 clients = Client.limit(10) clients.each do |client| puts client.address.postcode end clients = Client.includes(:address).limit(10) clients.each do |client| puts client.address.postcode end SELECT * FROM clients LIMIT 10 SELECT addresses.* FROM addresses WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))
  • 34. 大手町.rb #11 「Active Record クエリインタフェースについて」 複雑な includes 複数のアソシエーションの指定 ハッシュを使うことで、ネストにも対応可能、配列と組合せも OK 33 Article.includes(:category, :comments) Category.includes(articles: [{ comments: :guest }, :tags]).find(1)
  • 35. 大手町.rb #11 「Active Record クエリインタフェースについて」 スキップ scope: よく使うクエリ条件に別名を与えられる dynamic_finders: find_by_first_name みたいなやつ。 Ruby on Rails 登場当初もてはやされたが、 いまやブームが過ぎ、あまり使われない Enums: いわゆる列挙型。スコープが自動生成され便利 メソッドチェイン:メソッドを次々つなげるやつ 検索とビルド: あれば更新なければ作成したいときに便利 34
  • 36. 大手町.rb #11 「Active Record クエリインタフェースについて」 SQL の直接利用(select_all) select_all を使うと SQL を直接発行できる ActiveRecord ではなく connection のメソッド 私は頻繁に間違える 返り値を to_hash すると、ハッシュの配列になる 遅い ActiveRecord の生成処理が不要になる分高速化 select_all 以外にも SQL を直接利用する方法はある execute、select_one、select_rows, select_values, select_value select_all が一番使うし、ほかはあまり使わない 35 Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE id = '1'").to_hash # => [ # {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"}, # {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"} # ]
  • 37. 大手町.rb #11 「Active Record クエリインタフェースについて」 pluck、ids pluck を使うと高速化できる 下記の書き方をリファクタリングできる 36 Client.where(active: true).pluck(:id) # SELECT id FROM clients WHERE active = 1 # => [1, 2, 3] Client.distinct.pluck(:role) # SELECT DISTINCT role FROM clients # => ['admin', 'member', 'guest'] Client.pluck(:id, :name) # SELECT clients.id, clients.name FROM clients # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']] Client.select(:id).map { |c| c.id } # Client.pluck(:id)、 Client.ids と同じ Client.select(:id).map(&:id) # Client.pluck(:id)、 Client.ids と同じ Client.select(:id, :name).map { |c| [c.id, c.name] } # Client.pluck(:id, :name)
  • 38. 大手町.rb #11 「Active Record クエリインタフェースについて」 オブジェクトの存在チェック 37 # 引数のある exists? Client.exists?(id: [1,2,3]) Client.exists?(name: ['John', 'Sergei']) # 引数のない exists? Client.where(first_name: 'Ryan').exists? # via a model Article.any? # .count > 0 と同じ。 .present? よりも速い。(ARオブジェクトを生成しない) Article.many? # .count > 1 と同じ。 # 名前付きスコープを経由 Article.recent.any? Article.recent.many? # リレーション経由 Article.where(published: true).any? Article.where(published: true).many? # 関連付け経由 Article.first.categories.any? # アソシエーションでも使える Article.first.categories.many?
  • 39. 大手町.rb #11 「Active Record クエリインタフェースについて」 まとめ 今日は Active Record クエリインタフェースについて、 ピックアップして、紹介しました。 個人的には joins や eager_load でネストして 取得ができるのが便利だと思っています。 ドキュメントを熟読しましょう! 気が付かなかった発見がたくさんあります。 私自身、たくさんありました Rails Guide と Ruby on Rails API は必読です。 https://railsguides.jp/ https://api.rubyonrails.org/ 38