簡體   English   中英

Rails 4,PostgreSQL:將find_by_sql聚合函數的結果轉換為OpenStruct

[英]Rails 4, PostgreSQL: convert results of find_by_sql aggregate functions into OpenStruct

在報告/指標頁面上工作時,我需要盡最大可能優化查詢,因此我使用find_by_sql來提高效率。

我的查詢之一是做一些匯總函數,在其中我返回一個計數和一些和。 我將此查詢的結果分配給模型的實例變量。

我有有效的代碼,但是代碼使我感到恐懼。 我已經閱讀了有關使用方法的官方Ruby / Rails文檔,但我仍然認為我的操作存在問題。

def initialize(args)
  @loans_count           = stats.loans_count
  @total_fees_in_cents   = stats.total_fees_in_cents
  @total_amount_in_cents = stats.total_amount_in_cents
end

def stats
  @stats ||= find_stats
end

def find_stats
  if single_year?
    loans = Loan.find_by_sql(["SELECT count(*) as loans_count, sum(amount) as total_amount_in_cents, sum(fee) as total_fees_in_cents FROM loans WHERE account_id = ? AND year = ? LIMIT 1", @account_id, @year]).first
  else
    loans = Loan.find_by_sql(["SELECT count(*) as loans_count, sum(amount) as total_amount_in_cents, sum(fee) as total_fees_in_cents FROM loans WHERE account_id = ? LIMIT 1", @account_id]).first
  end

  # Store results in OpenStruct for ease of access later on
  OpenStruct.new(
    loans_count: loans.loans_count || 0,
    total_fees_in_cents: loans.total_fees_in_cents || 0,
    total_amount_in_cents: loans.total_amount_in_cents || 0
  )
end

關注

  1. find_by_sql應該返回一個數組; 即使找不到匹配項(空值,但有效行),SQL仍將始終返回一行。 但是,有一個原因我不應該在返回的數組上調用.first嗎? 在某些我沒想到的情況下,我擔心會碰到[].first => nil
  2. 我使用stats方法“緩存”結果的方法是僅查詢數據庫1次的合適方法嗎? 似乎很多代碼和方法只是為了獲取一些匯總數據。

擔,

有兩種方法可以解決此問題...

方法1

我想我可以在這里用一個答案(^ _-)解決這兩個問題,關鍵是解決您的恐懼。

您主要擔心整個[].first => nil 其次,您擔心數據庫效率。 第三,您想使它整潔並重構(對您有好處!)。

您的答案...讓PostgreSQL為您完成這項工作,並強制它每次都返回一個非零的答案。 得到它?

  1. 無需使用OpenStruct因為SQL查詢中的標簽相同。
  2. 使用PostgreSQL函數COALESCE()將null強制為零。
  3. 您“可以”將所有這些內容做成一個單行,但是為了方便讀者,讓我們將其切碎。
  4. 這已經是一個求和查詢,您不需要LIMIT 1

讓我們重寫代碼:

def initialize(args)
  @loans_count           = stats.loans_count
  @total_fees_in_cents   = stats.total_fees_in_cents
  @total_amount_in_cents = stats.total_amount_in_cents
end

def stats
  loans = Loan.select("count(*) as loans_count, COALESCE(sum(amount), 0) as total_amount_in_cents, COALESCE(sum(fee), 0) as total_fees_in_cents").where(account_id: @account_id)

  loans = loans.where(year: @year) if single_year?

  loans.first
end

方法2

我個人認為您過於擔心數據庫效率問題。 您始終可以查看您的開發/生產日志,以讀取實際輸出到PSQL服務器的內容,但是我敢肯定,這與您在此處所做的相同。

另外,如果我沒記錯的話,直到查詢到數據時,數據庫查詢才真正執行。 在這段時間內,ActiveRecord只是在准備QUERY字符串。

.to_i會將您的null轉換為零。

讓我們重寫代碼:

def initialize(args)
  stats = Loan.where(account_id: @account_id)
  stats = stats.where(year: @year) if single_year?
  stats.first

  @loans_count           = stats.count
  @total_fees_in_cents   = stats.sum(:fee).to_i
  @total_amount_in_cents = stats.sum(:amount).to_i
end

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM