最近、アプリケーションを sqlite から Oracle に移行し始めたところ、次の問題が発生し始めました。
Rails 3.2.13、Oracle 11.2.0.3.0、および activerecord-oracle_enhanced-adapter (1.4.2) では、コントローラーに次のものがあります。
def show
if params.has_key?('user_id')
@user = User.find(params[:user_id])
else
@user = current_user
end
@user_id = @user.id
@activity_date = Date.parse(params[:id])
#@activity_date = Activity.all.first.activity_date
@activities = Activity.where("user_id = ? AND activity_date = ?", @user.id, @activity_date)
logger.warn "----- count is #{@activities.count} ------"
return
アプリケーションは、正確に 0 レコードを検出します(ロガー出力に従って、@user および @activity_date の適切な値を使用)。
development.log は、生成されたクエリを次のように報告します。
SELECT COUNT(*) FROM "ACTIVITIES" WHERE (user_id = 10594 AND to_date(activity_date) = TO_DATE('2013-06-05','YYYY-MM-DD HH24:MI:SS'))
このクエリを SQL*Plus から実行すると、正確に 4 つのレコードが取得されます。 - したがって、AR によって返された結果と、AR がログに報告しているクエリによって返された結果との間に矛盾があるようです。
さらに、いじくり回して手動で場所呼び出しの前に日付を設定すると、
@activity_date = Activity.all.first.activity_date
最初のアクティビティの日付がたまたま「正しい」アクティビティの日付である場合、Rails は 4 行すべてを返すため、コンソールとアプリは同じデータベースを指しています。
私の知る限り:
- Rails は有効なクエリを生成しているため、.where メソッドに入力される値は問題ありません。
- このクエリは、*SQL*Plus から実行すると*、「適切な」数の結果を返します。
- コンソールとアプリケーションが同じデータベースを指している
- しかし、Rails は間違った数の結果を「見ている」ようです。
どうしたの ?抜く毛が少なくなってきました。
EDIT以下の David Aldridge の提案に従って、 activity_date プレースホルダーの周りの TO_DATE 呼び出しを削除しました。まだ正しい結果セットが得られません。 編集Davidの提案に従って:
@user_id=10594
@activity_date=Date.parse('2013-06-13')
####### **Returned wrong set of results**
####### Note class of @activity_date is Date
bundler-0.9.24 :083 > @activity_date.class
=> Date
bundler-0.9.24 :084 > Activity.where("user_id = ? AND activity_date = ?", @userid, @activity_date).explain
Activity Load (1.8ms) SELECT "ACTIVITIES".* FROM "ACTIVITIES" WHERE (user_id = NULL AND activity_date = TO_DATE('2013-06-13','YYYY-MM-DD HH24:MI:SS'))
EXPLAIN (7.8ms) EXPLAIN PLAN FOR SELECT "ACTIVITIES".* FROM "ACTIVITIES" WHERE (user_id = NULL AND activity_date = TO_DATE('2013-06-13','YYYY-MM-DD HH24:MI:SS'))
####### **Returned right set of results**
bundler-0.9.24 :089 > @activity_date=Activity.all.first.activity_date
Activity Load (1.5ms) SELECT "ACTIVITIES".* FROM "ACTIVITIES"
=> Wed, 05 Jun 2013 04:00:00 UTC +00:00
#### Note class of @activity_date is different from above**
bundler-0.9.24 :090 > @activity_date.class
=> ActiveSupport::TimeWithZone
bundler-0.9.24 :091 > Activity.where("user_id = ? AND activity_date = ?", @userid, @activity_date).explain
**And note generated query includes a time specification whereas previous query did not**
Activity Load (2.7ms) SELECT "ACTIVITIES".* FROM "ACTIVITIES" WHERE (user_id = NULL AND activity_date = TO_DATE('2013-06-05 04:00:00','YYYY-MM-DD HH24:MI:SS'))
解決する activity_date フィールドを作成するときに、Date.civil を使用しました。オラクルが「日付」型と呼んでいるものには、実際には時間コンポーネントも含まれていることを考慮していませんでした。
Date.civil はタイム ゾーンを使用しないため、保存した activity_date は基本的に DateTime であり、ローカル タイムゾーンによってオフセットされます(Date.civil はタイム ゾーンを使用しないため)。私のアプリケーションは実際の時間を無視するので、Date.civil の代わりに DateTime.civil を使用して activity_date を計算することでこれを解決しました。David Aldridge 氏の助けに感謝します。