Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: rails/docrails
base: master
...
head fork: kwatch/docrails
Checking mergeability… Don’t worry, you can still create the pull request.
  • 1 commit
  • 1 file changed
  • 0 commit comments
  • 1 contributor
Showing with 903 additions and 0 deletions.
  1. +903 −0 railties/guides/source/ja-JP/active_record_querying.textile
View
903 railties/guides/source/ja-JP/active_record_querying.textile
@@ -0,0 +1,903 @@
+h2. Active Record クエリインタフェース
+
+本ガイドでは、Active Record を使ってデータベースからデータをとってくるさまざまな方法を説明します。本ガイドを参照することで、以下のことができるようになるでしょう:
+
+* いろんなメソッドや条件を使ってレコードを検索する
+* 検索できたレコードの順番や、抽出するカラムや、グルーピングなどのプロパティを指定する
+* Eager loading を使うことで、データの抽出に必要なデータベースクエリの数を減らす
+* 動的finderメソッドを使う
+* 特定のレコードについて存在するかどうかを調べる
+* Active Record モデルで様々な計算を実行する
+
+endprologue.
+
+警告: 本ガイドでは Rails 3.0 を使っています。ここで登場するコードは、他のバージョンのRailsでは動作しないでしょう。
+
+もし生のSQLを使ってデータベースレコードを検索しているなら、一般的に言って、Rails では同じことをもっとうまい方法でできることがわかるでしょう。Active Record を使えばほとんどの場合においてSQLを使う必要はありません。
+
+本ガイドに登場するコード例では、次のようなモデルを使います:
+
+TIP: 以下に示すモデルはすべて、特に指定がない限りは主キーとして +id+ を使います。
+
+<br />
+
+<ruby>
+class Client < ActiveRecord::Base
+ has_one :address
+ has_many :orders
+ has_and_belongs_to_many :roles
+end
+</ruby>
+
+<ruby>
+class Address < ActiveRecord::Base
+ belongs_to :client
+end
+</ruby>
+
+<ruby>
+class Order < ActiveRecord::Base
+ belongs_to :client, :counter_cache => true
+end
+</ruby>
+
+<ruby>
+class Role < ActiveRecord::Base
+ has_and_belongs_to_many :clients
+end
+</ruby>
+
+Active Record はデータベースに対してクエリを実行します。また Active Record はほとんどのデータベースシステム (いくつか例を挙げますと MySQL や PostgreSQL や SQLite) で互換性があります。どのデータベースシステムを使おうとも、Active Record のメソッド形式はつねに同じです。
+
+h3. デーベースから 1 件取り出す
+
+データベースからオブジェクトを取り出すために、Active Record は検索用メソッドをいくつか用意しています。これらのメソッドに引数を渡すことで、生のSQLを書くことなしに、データベースに対して特定のクエリを実行することができます。
+
+そのためのメソッドがこちらです:
+* +where+
+* +select+
+* +group+
+* +order+
+* +limit+
+* +offset+
+* +joins+
+* +includes+
+* +lock+
+* +readonly+
+* +from+
+
+これらメソッドはどれも Relation 〔訳注: クラスのオブジェクト〕を返します。
+
+<tt>Model.find(options)</tt> の主な操作をまとめると:
+
+* 与えられたオプションを同等の SQL クエリに変換すること。
+* SQL クエリを実行し、対応する結果をデータベースから抽出すること。
+* 各行に応じたモデルの Ruby オブジェクトを作成すること。
+* もしあれば after_find コールバックを呼び出すこと。
+
+h4. オブジェクトをひとつ取り出す
+
+Active Record は、オブジェクトをひとつ取り出すための異なるメソッドを 3 つ用意しています。
+
+h5. 主キーを使う
+
+<tt>Model.find(primary_key</tt>) を使うことで、 _primary key_ に対応したオブジェクトや (もしあれば) オプションに一致したオブジェクトを取り出せます。たとえば:
+
+<ruby>
+# 主キー (id) が 10 である顧客を検索する
+client = Client.find(10)
+=> #<Client id: 10, name: => "Ryan">
+</ruby>
+
+これと同等の SQl は次の通りです:
+
+<sql>
+SELECT * FROM clients WHERE (clients.id = 10)
+</sql>
+
+<tt>Model.find(primary_key)</tt> は、もし該当するレコードが見つからなければ、+ActiveRecord::RecordNotFound+ 例外を発生させます。
+
+h5. +first+
+
+<tt>Model.first</tt> は指定されたオプションに一致するレコードのうち最初のものを返します。たとえば:
+
+<ruby>
+client = Client.first
+=> #<Client id: 1, name: => "Lifo">
+</ruby>
+
+これに対応する SQL は:
+
+<sql>
+SELECT * FROM clients LIMIT 1
+</sql>
+
+<tt>Model.first</tt> は、もし一致するレコードがなければ nil を返します。例外は発生しません。
+
+h5. +last+
+
+<tt>Model.last</tt> は指定されたオプションに一致するレコードのうち最後のものを返します。たとえば:
+
+<ruby>
+client = Client.last
+=> #<Client id: 221, name: => "Russel">
+</ruby>
+
+これに対応する SQL は:
+
+<sql>
+SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
+</sql>
+
+<tt>Model.last</tt> は、もし一致するレコードがなければ +ni+l を返します。例外は発生しません。
+
+h4. 複数のオブジェクトを取り出す
+
+h5. 複数の主キーを使う
+
+<tt>Model.find(array_of_primary_key)</tt> は、引数として _primary keys_ の配列を取ることもできます。この場合、指定された _primary keys_ にマッチしたレコードが返されます。たとえば:
+
+<ruby>
+# 主キーが 1 と 10 の顧客を検索する
+client = Client.find(1, 10) # Or even Client.find([1, 10])
+=> [#<Client id: 1, name: => "Lifo">, #<Client id: 10, name: => "Ryan">]
+</ruby>
+
+これと同等の SQL は:
+
+<sql>
+SELECT * FROM clients WHERE (clients.id IN (1,10))
+</sql>
+
+<tt>Model.find(array_of_primary_key)</tt> は、指定された主キーにマッチするレコードが<strong>ひとつでも</strong>見つからない場合は、+ActiveRecord::RecordNotFound+ 例外を発生させます〔訳注: すべて見つかった場合のみ例外は発生しない〕。
+
+h4. 複数のオブジェクトをバッチ方式で取り出す
+
+時には、非常にたくさんのレコードに対して繰り返しを行わなければならないことがあります。たとえば、全ユーザに対してニュースレターを送信する場合や、何らかのデータをエクスポート (export) する場合などです。
+
+まず最初に、ごく単純な方法を示します:
+
+<ruby>
+# users テーブルが数千行ある場合はとても効率が悪い
+User.each do |user|
+ NewsLetter.weekly_deliver(user)
+end
+</ruby>
+
+しかしテーブル内の行数が非常に多いと、このアプローチではパフォーマンスが悪かったり、最悪だと実行すらできないことがあります。
+
+これは、+User.each+ が _テーブル全体_ に対して Active Record によるフェッチを行い、行ごとのモデルオブジェクトを作成し、それらをすべてメモリ上の配列に格納してしまうことが原因です。時にはこの方法はあまりに多くのオブジェクトを作成してしまうため、一度にあまりに多くのメモリを消費することになります。
+
+h5. +find_each+
+
+巨大なテーブル全体に対して効率的に繰り返しを行うために、Active Record はバッチ形式の検索用メソッドである +find_each+ を提供しています:
+
+<ruby>
+User.find_each do |user|
+ NewsLetter.weekly_deliver(user)
+end
+</ruby>
+
+*バッチサイズを設定する*
+
++find_each+ の裏側では、1 回のバッチで +1000+ 行がフェッチされてから 1 行ずつ yield が実行されます。この、裏側で行われているバッチのサイズを変更するには、+:batch_size+ オプションを指定します。
+
+バッチサイズ +5000+ で +User+ のレコードを取ってくるには:
+
+<ruby>
+User.find_each(:batch_size => 5000) do |user|
+ NewsLetter.weekly_deliver(user)
+end
+</ruby>
+
+*バッチによる検索を開始するときの主キーを指定する*
+
+レコードは主キーの昇順でフェッチされます。主キーは整数でなければなりません。番号の小さいものは必要としない場合、 +:start+ オプションを使えば、取り出すべき一連のレコードにおける最初の ID を設定できます。これは、たとえばバッチ処理が中断された場合に、最後に処理したレコードの ID をチェックポイントとして保存しておき、あとでその ID から処理を再開するような場合に役に立ちます。
+
+主キーが +2000+ 以降のユーザにだけニュースレターを送信する場合は:
+
+<ruby>
+User.find_each(:batch_size => 5000, :start => 2000) do |user|
+ NewsLetter.weekly_deliver(user)
+end
+</ruby>
+
+*追加できるオプション*
+
++find_each+ は通常の +find+ メソッドと同じオプションが利用できます。ただし +:order+ と +:limit+ は内部的に使用されるため、これらを +find_each+ で明示的に指定することはできません。
+
+h5. +find_in_batches+
+
+1 行ずつ処理を行うかわりに、+find_in_batches+ を使うことでチャンク〔訳注: データの固まり、ここでは配列を意味する〕ごとに処理を行うこともできます。このメソッドは +find_each+ に似てますが、〔訳注: モデルのかわりに〕モデルの配列を受け取って yield します:
+
+<ruby>
+# 1 回につき 1000 件のインボイスを含むチャンクを処理する
+Invoice.find_in_batches(:include => :invoice_lines) do |invoices|
+ export.add_invoices(invoices)
+end
+</ruby>
+
+上のコードは毎回 1000 個のインボイスを使って、指定されたブロックを yield します。
+
+h3. 検索条件
+
+find メソッドでは、返されるレコードを限定するための検索条件を指定できます。これは SQL 文でいうところの WHERE 句で表されます。条件は文字列や配列やハッシュで指定できます。
+
+h4. 文字列だけを使った検索条件
+
+もし find に検索条件を追加するなら、それをたとえば +Client.where("orders_count = '2'")+ のようにその場で指定でき、この場合なら +orders_count+ フィールドの値が 2 であるようなクライアントをすべて検索します。
+
+警告: 条件を文字列だけで組み立てるのは、SQL インジェクションの脆弱性を残してしまう危険性が高いです。たとえば +Client.where("name LIKE '%#{params[:name]}%'")+ は安全ではありません。次のセクションで、配列を使って検索条件を構築するという、より好ましい方法を説明します。
+
+h4. 配列を使った検索条件
+
+たとえば何かの引数をもとにしたり、ユーザレベルのステータスとかなにかをもとにするといった、数値が変化するような場合はどうするのでしょう?
+
+
+Active Record は条件値の最初の要素を取り出し、その中のクエスチョンマーク +(?)+ を残りの要素で置き換えます。
+
+あるいは、もし 2 つの条件を指定したい場合は次のようにします:
+
+<ruby>
+Client.where(["orders_count = ? AND locked = ?", params[:orders], false])
+</ruby>
+
+この例では、1 つ目のクエスチョンマークは +params[:orders]+ の値で置き換わり、2 つ目は +false+ の SQL 的表現 (これはアダプタごとに異なります) で置き換わります。
+
+〔訳注: 「?」を使った〕次のような書き方は:
+
+
+〔訳注: 「#{}」を使った〕次のような書き方よりも:
+
+
+安全性の点において望ましいといえます。条件式の文字列に値を直接埋め込むと、値をデータベースに *そのままの形で* 渡してしまいます。これはつまり、その値というのが、悪意を持ったユーザによるエスケープされてない値である可能性があるということです。もしそうなった場合、悪意を持ったユーザがその弱点を見つけてしまえば、それを突いてデータベースに対して何でもできてしまうため、データベース全体が危険に晒されてしまいます。条件式の文字列に引数を直接埋め込むようなことは決してしてはいけません。
+
+TIP: SQL インジェクションの危険性についてのより詳しい情報は、"Ruby on Rails Security Guide":security.html#sql-injection を参照してください。
+
+h5. プレースホルダを使った検索条件
+
+「 +?+ 」を使った置換と似たような方法として、配列の条件式にハッシュのキーと値で指定することもできます:
+
+<ruby>
+Client.where(
+ ["created_at >= :start_date AND created_at <= :end_date", { :start_date => params[:start_date], :end_date => params[:end_date] }])
+</ruby>
+
+この方法は、条件式の可変部が多い場合は可読性がより高くなります。
+
+h5(#array-range_conditions). 範囲を指定した検索条件
+
+もしテーブル内の範囲 (たとえばある期間内に作成されたユーザなど) を検索しようとしているなら、SQL 文における IN に相当する条件オプションが利用可能です。もし 2 つの日付がコントローラからもたらされるなら、範囲を使った検索は次のようになります:
+
+<ruby>
+Client.where(["created_at IN (?)",
+ (params[:start_date].to_date)..(params[:end_date].to_date)])
+</ruby>
+
+こうすると、Range オブジェクトの下限以上かつ上限以下であるような、適切なクエリが生成されます。たとえば 1 年間 365 日 (または年によっては 366 日) を表すような date オブジェクトの Range オブジェクトを渡すと、次のような SQL が生成されます。
+
+<sql>
+SELECT * FROM users WHERE (created_at IN
+ ('2007-12-31','2008-01-01','2008-01-02','2008-01-03','2008-01-04','2008-01-05',
+ '2008-01-06','2008-01-07','2008-01-08','2008-01-09','2008-01-10','2008-01-11',
+ '2008-01-12','2008-01-13','2008-01-14','2008-01-15','2008-01-16','2008-01-17',
+ '2008-01-18','2008-01-19','2008-01-20','2008-01-21','2008-01-22','2008-01-23',...
+ ‘2008-12-15','2008-12-16','2008-12-17','2008-12-18','2008-12-19','2008-12-20',
+ '2008-12-21','2008-12-22','2008-12-23','2008-12-24','2008-12-25','2008-12-26',
+ '2008-12-27','2008-12-28','2008-12-29','2008-12-30','2008-12-31'))
+</sql>
+
+h5. 時刻 (Time) と日付 (Date) の検索条件
+
+もし Time オブジェクトを渡したら、 *本当に* 大変なことになります。なぜなら、比較対象となるフィールドと、範囲内の *すべての秒数* とを比較しようとするからです:
+
+<ruby>
+Client.where(["created_at IN (?)",
+ (params[:start_date].to_date.to_time)..(params[:end_date].to_date.to_time)])
+</ruby>
+
+<sql>
+SELECT * FROM users WHERE (created_at IN
+ ('2007-12-01 00:00:00', '2007-12-01 00:00:01' ...
+ '2007-12-01 23:59:59', '2007-12-02 00:00:00'))
+</sql>
+
+これは、データベースサーバに予期せぬエラーを発生させる可能性があります。たとえば MySQL だと次のようなエラーが返されます:
+
+
+ここで _query_ はエラーを引き起こした実際のクエリです。
+
+この例では SQL の演算子である「>」や「<」を使うほうがいいでしょう。たとえば:
+
+<ruby>
+Client.where(
+ ["created_at > ? AND created_at < ?", params[:start_date], params[:end_date]])
+</ruby>
+
+もちろん Ruby と同じように、「>=」や「<=」も利用できます:
+
+
+もしより短いシンタックスを望むなら、次のセクション「ハッシュを使った検索条件」を読むことを強くお勧めします。
+
+h4. ハッシュを使った検索条件
+
+Active Record では、Hash による検索条件 (Hash Conditions) を使うことができます。これを使うと、条件式の可読性が向上します。Hash による検索条件を使うことで、条件式で使いたいフィールド名をキーとし、条件として使いたい値を Hash の値に指定できます:
+
+NOTE: 同様に、範囲式やサブセットチェック (subset checking)〔訳注: ???〕も Hash による検索条件で利用可能です。
+
+h5. 等号による検索条件
+
+<ruby>
+Client.where({ :locked => true })
+</ruby>
+
+フィールド名は Symbol である必要はなく、String でもよいです:
+
+<ruby>
+Client.where({ 'locked' => true })
+</ruby>
+
+h5(#hash-range_conditions). Range を使った検索条件
+
+Hash による検索条件のよいところは、フィールドの Range オブジェクトを渡した場合に、前のセクションで見たような巨大なクエリを生成しないという点です。
+
+<ruby>
+Client.where({ :created_at => (Time.now.midnight - 1.day)..Time.now.midnight})
+</ruby>
+
+この場合だと、昨日作成されたすべての顧客情報を、SQL 文の BETWEEN を使って検索します:
+
+<sql>
+SELECT * FROM clients WHERE (clients.created_at BETWEEN '2008-12-21 00:00:00' AND '2008-12-22 00:00:00')
+</sql>
+
+このデモンストレーションは、配列による検索条件よりも短く書ける例となっています。
+
+h5. 部分集合による検索条件
+
+もし +IN+ 演算子を使ってレコードを検索したいなら、ハッシュの値に配列を渡せばよいです:
+
+<ruby>
+Client.where({ :orders_count => [1,3,5] })
+</ruby>
+
+この場合なら次のような SQL が生成されます:
+
+<sql>
+SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
+</sql>
+
+h4. 順序
+
+データベースからある特定の順番でレコードを抽出するには、+:order+ オプションつきで +find+ メソッドを呼び出します。
+
+たとえば、+created_at+ フィールドの昇順でテーブルからレコードを取り出したいなら:
+
+
+ASC〔訳注: 昇順〕または DESC〔訳注: 降順〕を指定できます:
+
+<ruby>
+Client.order("created_at DESC")
+# または
+Client.order("created_at ASC")
+</ruby>
+
+あるいは複数のフィールドを指定して:
+
+<ruby>
+Client.order("orders_count ASC, created_at DESC")
+</ruby>
+
+h4. 特定のフィールドだけを選択する
+
+デフォルトでは、<tt>Model.find</tt> は +select *+を使って結果セット (result set) からすべてのフィールドを選択します。
+
+結果セットから一部のフィールドだけを取り出すには、select メソッドでフィールド名を指定する必要があります。
+
+NOTE: select メソッドが使われると、返されるオブジェクトはすべて "読み取り専用":#readonly-objects 〔訳注: 変更ができない〕になります。
+
+<br />
+
+たとえば、 +viewable_by+ カラムと +locked+ カラムのみを選択するには:
+
+<ruby>
+Client.select("viewable_by, locked")
+</ruby>
+
+この find メソッド呼び出しによって生成される SQL クエリはだいたい次の通りです:
+
+<sql>
+SELECT viewable_by, locked FROM clients
+</sql>
+
+モデルオブジェクトのうち、指定されたフィールドのみが初期化されることに注意してください。もし初期化されてないフィールドにアクセスすると、次のようなエラーになります:
+
+
+ここで +&lt;attribute&gt;+ はアクセスしようとしている属性です。+id+ メソッドは +ActiveRecord::MissingAttributeError+ を発生させませんが、アソシエーションは id メソッドが正しく機能することが必要なので、 +id+ メソッドを使う場合は注意してください。
+
+また select オプション内で SQL 関数を指定することができます。たとえば、 +DISTINCT+ 関数を使うことであるフィールドにおける一意な値ごとに 1 つのレコードのみを取り出したいなら、次のようにします:
+
+<ruby>
+Client.select("DISTINCT(name)")
+</ruby>
+
+h4. Limit と Offset
+
++Model.find+ メソッドで SQL フィールドに +LIMIT+ を適用するには、+limit+ および +offset+ メソッドをリレーションに指定します。
+
+もし抽出される全レコードのうち、レコードの総数を制限したサブセットのみを取り出したい場合、 +limit+ オプションと、必要ならば +offset+ オプションも使用します。 Limit オプションはクエリから抽出されるレコードの最大数を表し、offset オプションは抽出されたレコードセットの読み取り開始場所を指定します。たとえば:
+
+<ruby>
+Client.limit(5)
+</ruby>
+
+このコードは最大で 5 個の Client オブジェクトを返します。また offset オプションが指定されてないので、テーブルの最初から数えて 5 番目までを返します。上のコードは次のような SQL を実行します:
+
+
+あるいは +limit+ と +offset+ の両方を指定するには:
+
+<ruby>
+Client.limit(5).offset(5)
+</ruby>
+
+このコードは最大で 5 個の Client オブジェクトを返しますが、今回は offset が指定されているので、clients テーブルにおいて 5 番目のレコードから開始します。SQL は次の通りです:
+
+<sql>
+SELECT * FROM clients LIMIT 5, 5
+</sql>
+
+h4. Group
+
+検索用メソッドで実行される SQL に +GROUP BY+ を適用するには、 +group+ メソッドを指定します。
+
+たとえば、すべての日付を取り出し、かつ日付順でソートしたい場合:
+
+<ruby>
+Order.group("date(created_at)").order("created_at")
+</ruby>
+
+この場合なら、データベース内の日付順で、各日付ごとに +Order+ オブジェクトをひとつ返します。
+
+実行される SQL は次のようなものになります:
+
+<sql>
+SELECT * FROM orders GROUP BY date(created_at)
+</sql>
+
+h4. Having
+
++GROUP BY+ で指定したフィールドに対する条件は、SQL では +HAVING+ 句で指定します。 +Model.find+ の +:having+ オプションを使うと、SQL での +HAVING+ 句を指定できます。
+
+たとえば:
+
+<ruby>
+Order.group("date(created_at)".having(["created_at > ?", 1.month.ago])
+</ruby>
+
+実行される SQL は次のようなものになります:
+
+<sql>
+SELECT * FROM orders GROUP BY date(created_at) HAVING created_at > '2009-01-15'
+</sql>
+
+この例だと各日付ごとに Order オブジェクトをひとつ返しますが、日付が条件を満たす場合のみです。
+
+h4. 読み取り専用オブジェクト
+
+リレーションによって返されたレコードが変更されたり破壊されるのを明示的に防ぐには、 +readonly+ メソッドに +true+ を指定しておいてから、find の呼び出しを繋げます。
+
+読取専用レコードに対して変更や削除を行うことはできず、例外 +ActiveRecord::ReadOnlyRecord+ が発生します。このオプションを指定するには、次のようにしてください:
+
+<ruby>
+Client.first.readonly(true)
+</ruby>
+
+もしこのレコードを変数 client に代入して、次のようなコードを実行すると、 +ActiveRecord::ReadOnlyRecord+ 例外が発生します:
+
+<ruby>
+client = Client.first.readonly(true)
+client.locked = false
+client.save
+</ruby>
+
+h4. 更新用にレコードをロックする
+
+ロックは、データベース中のレコードを更新するときに競合条件 (Race condition) を避けるのに役立ちます。また更新をアトミックに行うことも保証してくれます。 Active Record は 2 種類のロックメカニズムを提供しています:
+
+* 楽観的ロック
+* 悲観的ロック
+
+h5. 楽観的ロック
+
+楽観的ロックは、複数のユーザが同じレコードに更新のためにアクセスする場合において、データの衝突 (conflict) があまりないことを仮定した方法です。この方法では、対象レコードを取り出して以降、〔訳注: 更新する際に〕他のプロセスがそのレコードに対して変更を行ったかどうかをチェックします。もし〔訳注: 他プロセスによる変更が〕行われていた場合は +ActiveRecord::StaleObjectError+ が発生し、更新は行われません。
+
+<strong>楽観的ロック用のカラム</strong>
+
+楽観的ロッックを使用する場合、 +lock_version+ という名前のカラムがテーブルに必要です。レコードが更新されるたびに、Active Record は +lock_version+ カラムの値を 1 増やします。こうすることでロック機構は、同じレコードが 2 度インスタンス化されてかつ最初のほうが更新された場合、2 つめのほうがセーブされたときに +ActiveRecord::StaleObjectError+ 例外を必ず発生させます。
+
+<ruby>
+c1 = Client.find(1)
+c2 = Client.find(1)
+
+c1.name = "Michael"
+c1.save
+
+c2.name = "should fail"
+c2.save # ActiveRecord::StaleObjectError 例外が発生する
+</ruby>
+
+更新が衝突したときに、例外を rescue して、ロールバックしたり更新をマージしたりあるいは衝突を解消する何らかのビジネスロジックを実行するのは、開発者の仕事となります。
+
+NOTE: データベーススキーマで、 +lock_version+ カラムのデフォルトが +0+ になっていることを確認してください。
+
+<br />
+
+この挙動は <tt>ActiveRecord::Base.lock_optimistically = false</tt> によってオフにすることができます。
+
++lock_version+ カラムの名前を変更するために、+ActiveRecord::Base+ は +set_locking_column+ というメソッドを用意しています:
+
+<ruby>
+class Client < ActiveRecord::Base
+ set_locking_column :lock_client_column
+end
+</ruby>
+
+h5. 悲観的ロック
+
+悲観的ロックは、データベースが持っている機能を使ったロックメカニズムです。 +Model.find+ を +:lock => true+ に渡すことで、選択された行に排他的ロックをかけることができます。 +:lock+ つきの +Model.find+ は通常、デッドロック条件を防ぐためにトランザクション内で使用されます。
+
+たとえば:
+
+<ruby>
+Item.transaction do
+ i = Item.first(:lock => true)
+ i.name = 'Jones'
+ i.save
+end
+</ruby>
+
+このセッションは次のような SQL を生成します (MySQL の場合):
+
+<sql>
+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
+</sql>
+
+また +:lock+ オプションには生の SQL を渡すことで別の種類のロックを使うこともできます。たとえば MySQL には +LOCK IN SHARE MODE+ という式があり、これはレコードにロックをかけるけど他のユーザがクエリで読み取ることは許すというものです。この式を指定するには、lock オプションにその式をそのまま渡します:
+
+<ruby>
+Item.transaction do
+ i = Item.find(1, :lock => "LOCK IN SHARE MODE")
+ i.increment!(:views)
+end
+</ruby>
+
+h3. テーブルのジョイン
+
+<tt>Model.find</tt> は SQL における +JOIN+ 句を指定するための +:joins+ オプションを提供しています。+:joins+ オプションの指定方法は複数あります:
+
+h4. 文字列による SQL フラグメントを使った方法
+
++:joins+ オプションに、+JOIN+ 句を表す生の SQL を指定できます。たとえば:
+
+<ruby>
+Client.all(:joins => 'LEFT OUTER JOIN addresses ON addresses.client_id = clients.id')
+</ruby>
+
+これは次のような SQL になります:
+
+<sql>
+SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id = clients.id
+</sql>
+
+h4. 名前付きアソシエーションの配列またはハッシュを使う
+
+WARNING: この方法は +INNER JOIN+ に対してのみ有効。
+
+
+Active Record では +:joins+ オプションを指定するときに、モデルクラスで定義された "アソシエーション":association_basics.html の名前を短縮名として指定できます。
+
+たとえば、 +Category+ と +Post+ と +Comments+ と +Guest+ というモデルがあったとします:
+
+<ruby>
+class Category < ActiveRecord::Base
+ has_many :posts
+end
+
+class Post < ActiveRecord::Base
+ belongs_to :category
+ has_many :comments
+ has_many :tags
+end
+
+class Comments < ActiveRecord::Base
+ belongs_to :post
+ has_one :guest
+end
+
+class Guest < ActiveRecord::Base
+ belongs_to :comment
+end
+</ruby>
+
+この場合、以下に示す例のすべてで、 +INNER JOIN+ を使った期待通りの join クエリが生成されます:
+
+h5. 単一のアソシエーションによるジョイン
+
+<ruby>
+Category.joins(:posts)
+</ruby>
+
+この結果は次の通りです:
+
+<sql>
+SELECT categories.* FROM categories
+ INNER JOIN posts ON posts.category_id = categories.id
+</sql>
+
+h5. 複数のアソシエーションによるジョイン
+
+<ruby>
+Post.joins(:category, :comments)
+</ruby>
+
+この結果は次の通りです:
+
+<sql>
+SELECT posts.* FROM posts
+ INNER JOIN categories ON posts.category_id = categories.id
+ INNER JOIN comments ON comments.post_id = posts.id
+</sql>
+
+h5. 入れ子 (1 段) になったアソシエーションによるジョイン
+
+<ruby>
+Post.joins(:comments => :guest)
+</ruby>
+
+h5. 入れ子 (多段) になったアソシエーションによるジョイン
+
+<ruby>
+Category.joins(:posts => [{:comments => :guest}, :tags])
+</ruby>
+
+h4. ジョイン用テーブルを使った条件の指定
+
+通常の "配列による条件指定":#array-conditions または "文字列による条件指定":#pure-string-conditions を、ジョイン用テーブル (joined table) に対して指定できます。 "ハッシュによる条件指定":#hash-conditions は、ジョイン用テーブルに対して特別な文法を提供します:
+
+<ruby>
+time_range = (Time.now.midnight - 1.day)..Time.now.midnight
+Client.joins(:orders).where('orders.created_at' => time_range)
+</ruby>
+
+より明確な別の方法として、ハッシュによる条件指定を入れ子にする方法があります:
+
+<ruby>
+time_range = (Time.now.midnight - 1.day)..Time.now.midnight
+Client.joins(:orders).where(:orders => {:created_at => time_range})
+</ruby>
+
+この例では SQL の +BETWEEN+ 式を使って、昨日に注文を作成した顧客をすべて検索します。
+
+h3. Eager ローディングアソシエーション
+
+Eager ローディングは、 +Model.find+ によって返されるオブジェクトに関連した (associated) レコードを、できるだけ少ないクエリを使って読み込むメカニズムです。
+
+<strong>N <plus> 1 クエリ問題</strong>
+
+次のようなコードを考えます。これは、顧客を 10 名検索してそれらの郵便番号を表示しています:
+
+
+
+このコードは、一見問題がなさそうに思えます。しかし実行されるクエリの総数が問題となります。上のコードでは、1 個 ( 顧客 10 名を検索するため ) <plus> 10 個 ( 顧客ごとに住所を読み込むため ) = <strong>11 個</strong>のクエリが実行されます。
+
+<strong>N <plus> 1 クエリ問題を解決する方法</strong>
+
+Active Record では、読み込まれるであろうアソシエーションを事前にすべて指定できます。これは +Model.find+ の呼び出しに +includes+ メソッドを指定することで実現できます。Active Record では、すべての指定されたアソシエーションが、最小のクエリ数で読み込まれます。
+
+上で出た例を改訂して、eager ローディングを使って住所を読み込むよう +Client.all+ を書き直してみましょう:
+
+
+
+前は <strong>11 個</strong>のクエリが実行されたのに対し、上のコードはちょうど <strong>2 個</strong>のクエリだけが実行されます。
+
+<sql>
+SELECT * FROM clients LIMIT 10
+SELECT addresses.* FROM addresses
+ WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))
+</sql>
+
+h4. 複数のアソシエーションに体する Eager ローディング
+
+Active Record では、配列やハッシュやそれらの入れ子を +includes+ メソッドに指定することで、ひとつの +Model.find+ 呼び出しに対して任意の数のアソシエーションを Eager ローディングで読み込めます。
+
+h5. 複数のアソシエーションを含む配列
+
+<ruby>
+Post.includes(:category, :comments)
+</ruby>
+
+この例はすべての投稿 (post) と、各投稿に関連したカテゴリやコメントを読み込みます。
+〔訳注: どこに配列が?〕
+
+h5. ネストしたアソシエーションのハッシュ
+
+<ruby>
+Category.find(1).includes(:posts => [{:comments => :guest}, :tags])
+</ruby>
+
+上のコードは、id が 1 であるカテゴリを検索し、そのカテゴリに関連したすべての投稿 (post) を Eager ローディングを使って読み込みます。また読み込まれた投稿に関連するタグとコメントも Eager ローディングを使って読み込みます。同様にすべてのコメントにおいても、ゲストのアソシエーションが Eager ローディングで読み込まれます。
+
+h4. Eager ローディングで読み込まれたアソシエーションに対する条件指定
+
+Active Record は Eager ローディングで読み込まれたアソシエーションに対しても、ちょうど +joins+ のときのように条件指定を行うことができますが、素直に "joins":#joining-tables メソッドを使うほうをお勧めします。
+
+h3. 動的ファインダ
+
+Active Record では、テーブルに定義したどのフィールド (属性ともいう) にも使えるようなファインダ (finder) メソッド〔訳注: 検索用メソッドのこと〕が用意されています。たとえば +Client+ モデルに +name+ というフィールドがあるとすると、Active Record では +find_by_name+ と +find_all_by_name+ というメソッドが自動的に利用可能になります。もし +locked+ というフィールドが +Client+ モデルにあれば、 +find_by_locked+ と +find_all_by_locked+ が利用可能です。〔訳注: これらは事前に定義されるものではなく、呼ばれたときに初めて定義される検索用メソッドであり、これらを動的ファインダ (dynamic finder) といいます。〕
+
+また引数にマッチした中で最後のレコードを取ってくるための +find_last_by_*+ メソッドも利用できます。
+
+たとえば +Client.find_by_name!("Ryan")+ のように、エクスクラメーションポイント (「<tt>!</tt>」のこと) を動的ファインダ名の最後につけると、該当するレコードが見つからなかった場合は +ActiveRecord::RecordNotFound+ エラーが発生します。
+
+name と locked の両方のフィールドを使って検索したい場合は、たとえば +Client.find_by_name_and_locked("Ryan", true)+ のように、単にそれらを +and+ でつなげるだけでいいです。
+
+
+動的ファインダの別の種類として、オブジェクトを検索し、見つからなければ新規作成/初期化を行うというものもあります。これらは、メソッド名が +find_or_create_by_name(params[:name])+ のようになる以外は、他のファインダと同じように動作します。
+
+<sql>
+SELECT * FROM clients WHERE (clients.name = 'Ryan') LIMIT 1
+BEGIN
+INSERT INTO clients (name, updated_at, created_at, orders_count, locked)
+ VALUES('Ryan', '2008-09-28 15:39:12', '2008-09-28 15:39:12', 0, '0')
+COMMIT
+</sql>
+
++find_or_create+'s の兄弟版として +find_or_initialize+ があります。これは、まず検索を行い、見つからなかった場合は渡された引数を使って〔訳注: +create+ ではなく〕 +new+ を呼び出します。
+
+<ruby>
+client = Client.find_or_initialize_by_name('Ryan')
+</ruby>
+
+上の場合だと、名前が "Ryan" である既存の顧客オブジェクトをローカル変数 client に代入するか、〔訳注: そのような顧客が見つからなければ〕 +Client.new(:name => 'Ryan')+ を呼び出して新しいオブジェクトを作成し初期化します。ここから、 +client.locked = true+ のようにセッターメソッドを使って顧客の他のフィールドを変更できます。またそれをデータベースに保存したいときは +save+ メソッドを呼び出します。
+
+h3. SQL による検索
+
++find_by_sql+ を使って、テーブル中のレコードを検索するための独自の SQL を指定できます。 +find_by_sql+ メソッドは、たとえ 1 つのレコードしか返さないような SQL が指定されたとしても、オブジェクトの配列を返します。たとえば次のようなクエリが実行できます:
+
+<ruby>
+Client.find_by_sql("SELECT * FROM clients
+ INNER JOIN orders ON clients.id = orders.client_id
+ ORDER clients.created_at desc")
+</ruby>
+
++find_by_sql+ を使えば、データベースのカスタムコールを呼び出してインスタンス化されたオブジェクトを取り出すのが、簡潔に実行できます。
+
+h3. +select_all+
+
+<tt>find_by_sql</tt> とよく似たものに +connection#select_all+ というのがあります。 +select_all+ は +find_by_sql+ と同様にカスタム SQL を使ってデータベースからオブジェクトを取り出しますが、インスタンス化を行いません。かわりに、レコードごとのハッシュを要素とする配列が返されます。
+
+<ruby>
+Client.connection.select_all("SELECT * FROM clients WHERE id = '1'")
+</ruby>
+
+h3. オブジェクトの存在確認
+
+オブジェクトが存在するかどうかをチェックしたいだけなら、 +exists?+ というメソッドが使えます。このメソッドは +find+ と同じクエリを使ってデータベースを検索しますが、オブジェクトやそのコレクションを返すかわりに、 +true+ または +false+ を返します。
+
+<ruby>
+Client.exists?(1)
+</ruby>
+
++exists?+ メソッドは複数の id をとることができますが、そのうちのどれかひとつでも存在していれば true を返します。
+
+<ruby>
+Client.exists?(1,2,3)
+# or
+Client.exists?([1,2,3])
+</ruby>
+
+もっと言うと、 +exists+ は find によく似た +conditions+ オプションを取ることができます:
+
+<ruby>
+Client.exists?(:conditions => "first_name = 'Ryan'")
+</ruby>
+
+引数なしで +exists?+ を呼び出すことも可能です。
+
+<ruby>
+Client.exists?
+</ruby>
+
+この場合、 +clients+ テーブルが空なら +false+ が返され、そうでなければ +true+ が返されます。
+
+h3. 計算
+
+このセクションの前書きでは例として count メソッドを使用しますが、説明されたオプションはすべてのサブセクションで利用可能です。
+
+<tt>count</tt> では +exists?+ と同じ方法で検索条件を指定できます:
+
+<ruby>
+Client.count(:conditions => "first_name = 'Ryan'")
+</ruby>
+
+これは次のような SQL を実行します:
+
+<sql>
+SELECT count(*) AS count_all FROM clients WHERE (first_name = 'Ryan')
+</sql>
+
+より複雑なことをするのに +includes+ メソッドや +joins+ メソッドを使うことができます:
+
+<ruby>
+Client.count.where("clients.first_name = 'Ryan' AND orders.status = 'received'").includes("orders")
+</ruby>
+
+これは次のような SQL を実行します:
+
+<sql>
+SELECT count(DISTINCT clients.id) AS count_all FROM clients
+ LEFT OUTER JOIN orders ON orders.client_id = client.id WHERE
+ (clients.first_name = 'Ryan' AND orders.status = 'received')
+</sql>
+
+このコードでは、ジョイン用のテーブル〔訳注: である orders 〕も +first_name+ というカラム名を持っている場合を考慮し、 +clients.first_name+ を指定しています。またジョイン用のテーブルが +orders+ なので、 +orders.status+ を指定しています。〔訳注: 要レビュー〕
+
+h4. カウント
+
+モデルのテーブル中にどれだけのレコードがあるか調べたい場合、 +Client.count+ を呼ぶとレコード数を返します。もっと詳しく調べたい、たとえばデータベース中のすべてのクライアントを現在の年齢とともに調べたい場合は、 +Client.count(:age)+ のようにします。〔訳注: 要レビュー〕
+
+オプションについては、前の "Calculations":#calculations セクションをご覧下さい。
+
+h4. 平均値
+
+もしテーブル中のある数値の平均値を知りたい場合は、そのテーブルと対応するクラスの +average+ メソッドを呼び出します。このメソッド呼び出しは次のような感じになるでしょう:
+
+<ruby>
+Client.average("orders_count")
+</ruby>
+
+この例だと、フィールドの平均値を表す数 (典型的には 3.14159265 のような浮動小数点数) を返します。
+
+利用可能なオプションについては前の "計算":#calculations のセクションをご覧下さい。
+
+h4. 最小値
+
+もしテーブル中のあるフィールドの最小値を調べたいなら、そのテーブルと対応するクラスの +minimum+ メソッドを呼び出します。このメソッド呼び出しは次のような感じになります:
+
+<ruby>
+Client.minimum("age")
+</ruby>
+
+利用可能なオプションについては前の "計算":#calculations のセクションをご覧下さい。
+
+h4. 最大値
+
+もしテーブル中のあるフィールドの最大値を調べたいなら、そのテーブルと対応するクラスの +maximum+ メソッドを呼び出します。このメソッド呼び出しは次のような感じになります:
+
+<ruby>
+Client.maximum("age")
+</ruby>
+
+利用可能なオプションについては前の "計算":#calculations のセクションをご覧下さい。
+
+h4. 合計値
+
+もしテーブル中のあるフィールドの合計値を調べたいなら、そのテーブルと対応するクラスの +sum+ メソッドを呼び出します。このメソッド呼び出しは次のような感じになります:
+
+<ruby>
+Client.sum("orders_count")
+</ruby>
+
+利用可能なオプションについては前の "計算":#calculations のセクションをご覧下さい。
+
+h3. 更新履歴
+
+"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16
+
+* April 7, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
+* February 3, 2010: Update to Rails 3 by "James Miller":credits.html#bensie
+* February 7, 2009: Second version by "Pratik":credits.html#lifo
+* December 29 2008: Initial version by "Ryan Bigg":credits.html#radar

No commit comments for this range

Something went wrong with that request. Please try again.