2015-12-03 28 views
-1

我在名爲CashTransaction的模型中有以下方法。在Rails中提取滿足模型函數的記錄

def is_refundable? 
    self.amount > self.total_refunded_amount 
end 

def total_refunded_amount 
    self.refunds.sum(:amount) 
end 

現在我需要提取所有滿足上述功能,即記錄其返回true記錄。

我得到的工作,通過使用如下語句:

CashTransaction.all.map { |x| x if x.is_refundable? }

但結果是Array。我正在尋找ActiveRecord_Relation對象,因爲我需要對結果執行join

我覺得我錯過了一些東西,因爲它看起來並不難。無論如何,它讓我陷入困境。建設性的建議會很好。

注意:只需amountCashTransaction列。

編輯

SQL做這項工作。如果我可以將其更改爲ORM,它仍然可以完成這項工作。

SELECT `cash_transactions`.* FROM `cash_transactions` INNER JOIN `refunds` ON `refunds`.`cash_transaction_id` = `cash_transactions`.`id` WHERE (cash_transactions.amount > (SELECT SUM(`amount`) FROM `refunds` WHERE refunds.cash_transaction_id = cash_transactions.id GROUP BY `cash_transaction_id`)); 

共享進展

我設法得到它通過以下ORM工作:

CashTransaction 
    .joins(:refunds) 
    .group('cash_transactions.id') 
    .having('cash_transactions.amount > sum(refunds.amount)') 

但是,實際上,我是在尋找是這樣的:

CashTransaction.joins(:refunds).where(is_refundable? : true) 

哪裏is_refundable?是一個模型功能。最初我認爲設置is_refundable?attr_accesor將工作。但是我錯了。

只是一個想法,可以在問題被固定在使用Arel一種優雅的方式。

+0

@AndreyDeineko,我發現了一個工作。你能看看我的編輯並分享你的觀點嗎? – abhinavmsra

+0

嘿,我的回答有幫助嗎?你有沒有得到它的工作? –

回答

3

有兩種選擇。

1)完成,你已經開始(,這是非常低效的,當涉及到數據的更大的量,因爲這一切是考慮到加工前的記憶):

CashTransaction.all.map(&:is_refundable?) # is the same to what you've written, but shorter. 

所以得到的ID:

ids = CashTransaction.all.map(&:is_refundable?).map(&:id) 

現在,讓ActiveRecord的關係:

CashTransaction.where(id: ids) # will return a relation 

2)將CALCULAT離子到SQL:

CashTransaction.where('amount > total_refunded_amount') 

第二種方法是在任何可能的方式更快,更高效。

當您處理數據庫時,嘗試在數據庫級別處理它,並儘可能減少Ruby的參與。

編輯

根據此編輯的問題是,你將如何達到預期的效果:

CashTransaction.joins(:refunds).where('amount > SUM(refunds.amount)') 

編輯#2

至於有問題的更新 - 我真的不明白,爲什麼你鎖定了is_refundable?作爲實例方法,它可以用於查詢,這在AR中基本上是不可能的,但是..

我的建議是建立一個範圍is_refundable

scope :is_refundable, -> { CashTransaction 
    .joins(:refunds) 
    .group('cash_transactions.id') 
    .having('cash_transactions.amount > sum(refunds.amount)') 
} 

現在是在儘可能短的符號可作爲

CashTransaction.is_refundable 

是短比更清晰的目標

CashTransaction.where('is_refundable = ?', true) 
+0

我編輯了這個問題。 'total_refunded_amount'不是表格列。 – abhinavmsra

+0

@abhinavmsra如果'total_refunded_amount'不是數據庫中的實際列,那麼你不能使用SQL來完成。請看我的答案。 –

+0

@abhinavmsra所以你現在期待我做什麼?我怎樣才能爲您提供更多信息?我已經展示瞭如何按照我們的方式進行。如果您想查看SQL解決方案,請向我展示'total_refunded_amount'方法,可能是SQL中可能進行的計算。 –

1

你可以這樣做:

cash_transactions = CashTransaction.all.map { |x| x if x.is_refundable? } # Array 
CashTransaction.where(id: cash_transactions.map(&:id)) # ActiveRecord_Relation 

但是,與其他答覆者一樣,這是一種無效的方式。

可以使用SQL做,如果amounttotal_refunded_amount是,這將是更加高效和表現在數據庫中cash_transactions表的列:

CashTransaction.where('amount > total_refunded_amount') 

但是,如果amounttotal_refunded_amount是不實際數據庫中的列,那麼你不能這樣做。那麼,我想你已經用另一種方式來處理,比使用原始SQL更高效。

0

我想你應該預先計算is_refundable結果(在新列)當CashTransaction和他的退款是通過使用回調更新(應該has_many):

class CashTransaction 
    before_save :update_is_refundable 
    def update_is_refundable 
    is_refundable = amount > total_refunded_amount 
    end 
    def total_refunded_amount 
    self.refunds.sum(:amount) 
    end 
end 

class Refund 
    belongs_to :cash_transaction 
    after_save :update_cash_transaction_is_refundable 
    def update_cash_transaction_is_refundable 
    cash_transaction.update_is_refundable 
    cash_transaction.save! 
    end 
end 

注:上面的代碼一定要進行優化,以避免某些查詢

他們可以查詢is_refundable柱:

CashTransaction.where(is_refundable: true) 
0

我認爲這不是不好做這兩個查詢,而不是一個連接表,像這樣

def refundable 
    where('amount < ?', total_refunded_amount) 
end 

這將做一個總和查詢然後使用和在第二個查詢,當表變得越來越大,你可能會發現,這是比在數據庫中進行連接更快。