質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

3回答

9596閲覧

ruby on rails DBから取得した値を効率よく加工する方法 setterメソッドの使い方

hiepita1

総合スコア37

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

1クリップ

投稿2017/04/04 14:49

編集2017/04/06 15:48

大変お世話になっております。またご質問させていただきます。
###前提・実現したいこと
DBから取って来た値を加工してviewに表示したいです。

例えば、column1[金額1],column2[金額2],column3[フラグ]
というレコードをDBからActiverecordで取得して来た時に、フラグの値によって金額の集計方法を変えて、最終的にViewに表示したいです。

###発生している問題・エラーメッセージ
取得して来たレコードを、再度foreachで回すことで、実現は可能かと思うんですが、それではパフォーマンス的に問題があるので、この値の加工を、他言語で言う所のsetterメソッドのような形で処理させたいのですが、実現方法がわかりません。

###該当のソースコード
//今現在書いているコードです。Modelでのeachが原因で処理速度が落ちています。

controller @data = Model.getmethod end Model class self.def getmethod //何十万行のレコードが入ります。 modeldata = Model.all modeldata.each |data| data.display_data = data.flg == 1 ? data.kingaku1 - data.kingaku2 : data.kingaku2 - data.kingaku1 end return modeldata end end

//どのように書くのかはわかりませんが、このような感じで、不要なeachを無くしたいです。データベースから値を取得したタイミングで、データの加工も同時にやってしまうようなイメージです。

controller @data = Model.getmethod end Model class self.def getmethod //何十万行のレコードが入ります。 modeldata = Model.all end def display_data_setter_method return data.display_data = data.flg == 1 ? data.kingaku1 - data.kingaku2 : data.kingaku2 - data.kingaku1 end return modeldata end end

###補足情報(言語/FW/ツール等のバージョンなど)
Ruby on Rails5

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

suzukis

2017/04/04 15:02

とりあえずコードのインデントが適切に反映されるようにマークアップしてください。編集画面で範囲選択して</>ボタンでできます
hiepita1

2017/04/06 15:49

大変失礼致しました。修正しました。
guest

回答3

0

既に十分な回答は出ていますが、せっかくなのでSQLでやる例も回答したいと思います。
自分が使っているのがPostgreSQLなので、PostgreSQLの例だけになりますが・・。

とりあえず、動作確認のためにentriesというテーブル名で試しました。

sql

1CREATE TABLE entries ( 2 id serial primary key, 3 kingaku1 integer NOT NULL DEFAULT 0, 4 kingaku2 integer NOT NULL DEFAULT 0, 5 flg integer NOT NULL DEFAULT 1 6); 7 8INSERT INTO entries (kingaku1, kingaku2, flg) VALUES(10, 20, 1); 9INSERT INTO entries (kingaku1, kingaku2, flg) VALUES(50, 10, 1); 10INSERT INTO entries (kingaku1, kingaku2, flg) VALUES(40, 20, 0); 11INSERT INTO entries (kingaku1, kingaku2, flg) VALUES(140, 20, 0); 12 13SELECT * FROM entries; 14 id | kingaku1 | kingaku2 | flg 15----+----------+----------+----- 16 1 | 10 | 20 | 1 17 2 | 50 | 10 | 1 18 3 | 40 | 20 | 0 19 4 | 140 | 20 | 0 20(4 rows) 21 22SELECT *, 23 CASE WHEN flg = 1 THEN kingaku1 - kingaku2 24 ELSE kingaku2 - kingaku1 25 END 26 AS display_data 27 FROM entries; 28 29 id | kingaku1 | kingaku2 | flg | display_data 30----+----------+----------+-----+-------------- 31 1 | 10 | 20 | 1 | -10 32 2 | 50 | 10 | 1 | 40 33 3 | 40 | 20 | 0 | -20 34 4 | 140 | 20 | 0 | -120 35(4 rows)

SQLではこのようにできているので、あとはこんな感じで。

ruby

1class Entry < ActiveRecord::Base 2 def self.with_display_data 3 select=<<SELECT 4*, 5 CASE WHEN flg = 1 THEN kingaku1 - kingaku2 6 ELSE kingaku2 - kingaku1 7 END 8 AS display_data 9SELECT 10 11 Entry.select(select) 12 end 13end
  • rails console
> puts Entry.with_display_data.all.to_sql => SELECT *, CASE WHEN flg = 1 THEN kingaku1 - kingaku2 ELSE kingaku2 - kingaku1 END AS display_data FROM "entries"
  • view

ruby

1@entries = Entry.with_display_data.all 2 3<% @entries.each do |entry| %> 4 # SELECTでやってる都合上、文字列になるので必要があればto_iする 5 <%= entry.display_data.to_i %> 6<% end %> 7 8

投稿2017/04/05 07:39

mingos

総合スコア4025

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hiepita1

2017/04/06 16:14

ご回答ありがとうございます。 SQLのCASE文で集計する方法も考えたのですが、可能な限りSQLのベタがきは避けるような方針になっているので、ActiveRecordがサポートしていないCase文などはコード上で処理させるようにしたいと考えています。 (情報が後出しで申し訳ありません・・・)
guest

0

ベストアンサー

コードが読みにくいので真意をつかみかねますが、やりたいことはこれでいいような気がします。

元のコードに倣ってますのでメソッド名が適切かは検討してください。

class Model < ActiveRecord::Base def display_data flg == 1 ? kingaku1 - kingaku2 : kingaku2 - kingaku end end
# @models = Model.all (または .where(なんとかかんとか)) <% @models.each do |model| %> <%= model.display_data %> <% end %>

コールバック使ってロード時に計算する、というのがより質問の趣旨に近いような気がしますが多分やり過ぎです。

投稿2017/04/05 06:35

suzukis

総合スコア1449

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hiepita1

2017/04/06 16:06

インデントが崩れていて読みにくい中、ご回答いただきありがとうございます。 コードを参考に実装させていただきました所、希望した通りの動作をしました。ありがとうございます。 >コールバック使ってロード時に計算する、というのがより質問の趣旨に近いような気がしますが多分やり過ぎです。 仰っているように、質問の趣旨的にはこちらの方が近いです。 差し支えなければコールバックでの実装方法も教えていただきたいです。また、やり過ぎというのは、コードの可読性が落ちるということでしょうか?あまり一般的な方法ではないということでしょうか?
suzukis

2017/04/07 00:35

after_findというコールバックがあるのでリファレンス読んでください。インスタンスメソッドで十分なことに余計なことをする必要は無い、ということです。
guest

0

Viewに表示したいです。

indexに一覧を表示したいと言うことでよろしいですか?
ActiveRecordは大変使いやすいですが、数が増えてくるとメモリの関係でもっさりしてきます。
この様な何十万レコードという条件だとActiveRecordの生成コストは馬鹿にならないです。

しかし、実を言うとviewに表示したいだけだったら、Modelを生成する必要はございません。

なので
※pluckはSQLのselect文を実行して結果を配列に格納するmethodです。

ruby

1@models=Model.pluck(:kingaku1,:kingaku2,:flg) 2 3<% @models.foreach do |model| %> 4<%= model[2] == 1 ? model[0] - model[1] : model[1] - model[0] %> 5<% end %>

とやればいいのですが。
なんか計算式を見る限り、差額を求めているだけのようなので
いっそのことflgを無視して絶対値だけを表示すれば幸せになれるのではないでしょうか?

ruby

1@models=Model.pluck("ABS(kingaku1-kingaku2)")#SQLに絶対値を計算させている 2 3<% @models.foreach do |model| %> 4<%= number_with_delimiter(model) %> 5<% end %>

後suzukisさんも言っていますが
</>
を使うと上のように表示されるので便利ですよ

あと初心者マークをつけると皆さん優しいですよ。

投稿2017/04/05 00:38

編集2017/04/05 08:22
moke

総合スコア2241

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

suzukis

2017/04/05 06:52

「モデルは、生成、登録、変更をするためのものです」というのは誤解です。取り出したレコードの値を使って何かをするような処理はモデルのメソッドとして実装するのが基本です。回答のようにビューにロジックを書いてはいけません。あと`pluck`はどうしても必要な場所だけに使い、多用しない方がよいです。特に、複数の値を`pluck`で持ってくるようなコードはインデックスで値を区別しなければならないので可読性が著しく低下します。
moke

2017/04/05 06:58

それはその通りですが、何十万行のレコードを処理する際に、 activerecordの生成コストを払うのはいかがかなと、 状況と質問者様の、理解度から、そのように表現させていただきましたが 確かに、嘘を書くのは良くないですよね。誤解のない様修正しておきます。
hiepita1

2017/04/06 15:59

ご回答ありがとうございます。activerecordのpluckメソッドを使うことで配列にして返すことができるんですね。 実際のコードではカラムの数も多いので、Modelを生成する方向で進めて行こうと思っていたのですが、そもそも何十万レコードをeachで回すコードに問題がありそうですね。 SQLでの絶対値を求める方法や、Pluckの動作など、非常に勉強になりました。 次回から初心者マークもつけていこうと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問