Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
231 views
in Technique[技术] by (71.8m points)

sql - Calculate Percent of total from scoped records and display virtual attributes (Rails / Active Record)

Create a scope, maybe something like this..

scope :mv, select('*,quantity*market_price as market_value, quantity*market_price/sum(quantity*market_price) as percent')

that creates two virtual attributes, market_value and percent. The problem I am having is creating the percent with the sum() included. If I add sum(), the scope returns one record.

I need to calculate the percent of market_value relative to the total market value of the scoped record set.

example:

1, market_value: 100, percent: .10
2, market_value: 100, percent: .10
3, market_value: 100, percent: .10
4, market_value: 100, percent: .10
5, market_value: 100, percent: .10
6, market_value: 500, percent: .50
Total is 1000

however, if I scope it to where market_value < 6, I should see

1, market_value: 100, percent: .20
2, market_value: 100, percent: .20
3, market_value: 100, percent: .20
4, market_value: 100, percent: .20
5, market_value: 100, percent: .20
Total 500

How can I accomplish this?

I created a self.pct method but problem with the self.pct method is that it needs to be run after all the scopes. if rescoped, the solution is wrong

So far,

class Position < ActiveRecord::Base
  attr_accessible :account_id, :account_type, :market_price, :quantity, :report_date, :symbol

  scope :long_only, where(:account_type => 'L')
  scope :short_only, where(:account_type=>"S")
  scope :equity_only, :conditions => ["symbol <> 'USD'"]

 scope :mv, select('*,quantity*market_price as market_value, quantity*market_price/sum(quantity*market_price) as percent')

 scope :mv1, lambda{|total| select(total) }

  #the problem with the self.pct method is that it needs to be run after all the scopes. if rescoped, the solution is wrong

 def self.pct 
   string="*,(quantity*market_price) as market_value, (market_price*quantity/#{sum_market_value}) as percent"
   mv1(string)
 end


  def market_value
    self.market_price*self.quantity
  end


  def self.sum_market_value
    sum('quantity*market_price')
  end
end
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I don't know if there's a way to do this in a single query, but we can get it in two queries:

require 'active_record'

ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'

ActiveRecord::Schema.define do
  self.verbose = false
  create_table :positions do |t|
    t.integer :quantity
    t.integer :market_price
  end
end

class Position < ActiveRecord::Base
  def self.with_market_value
    select "*, 
            quantity*market_price as market_value,
            quantity*market_price/#{total.to_f} as percent"
  end

  def self.total
    select('sum(quantity*market_price) as sum_of_market_values').first.sum_of_market_values
  end
end

Position.create! quantity: 25, market_price: 4
Position.create! quantity: 25, market_price: 4
Position.create! quantity: 25, market_price: 4
Position.create! quantity: 25, market_price: 4
Position.create! quantity: 25, market_price: 4
Position.create! quantity: 25, market_price: 20

Position.with_market_value.map { |p| [p.market_value, p.percent] }
# => [[100, 0.1], [100, 0.1], [100, 0.1], [100, 0.1], [100, 0.1], [500, 0.5]]

Position.where('market_price < 10').with_market_value.map { |p| [p.market_value, p.percent] }
# => [[100, 0.2], [100, 0.2], [100, 0.2], [100, 0.2], [100, 0.2]]

# ** NOTE THAT IT EXECUTES EAGERLY **
Position.with_market_value.where('market_price < 10').map { |p| [p.market_value, p.percent] }
# => [[100, 0.1], [100, 0.1], [100, 0.1], [100, 0.1], [100, 0.1]]

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...