在Rails应用程序中,我有两个模型如下
class Trip
has_many :items
# has a 'fuel' column
end
class Item
belongs_to :trip
# has a 'quantity' column
end
对于仪表板视图,我需要聚合这些数据,以便它们可以在图表中显示。
以下方法生成正确的输出
def self.to_dashboard_chart
data = [['Fuel', 'Quantity']]
self.all.each do |trip|
data << [trip.fuel, trip.items.sum(:quantity)] unless trip.fuel.nil?
end
data
end
但也非常低效,特别是当数据库变大时!
我正在尝试将其改编为单个查询。
def self.to_dashboard_chart_new
self.joins(:items).
where.not(fuel: nil).
select("fuel, sum(items.quantity) as trip_quantity").
group(:id).
map{ |t| [t.fuel, t.trip_quantity] }.
unshift(['Fuel','Quantity'])
end
然而,这并没有返回相同的回应
Trip.to_dashboard_chart == Trip.to_dashboard_chart_new
=> false
Trip.to_dashboard_chart.length == Trip.to_dashboard_chart_new.length
=> false
我无法理解为什么这些会返回不同的结果。我在俯瞰什么?
我认为我已经将问题跟踪到如何处理没有项目的旅行。
let!(:t1) { create :trip, fuel: 50 }
let!(:t2) { create :trip, fuel: 100 }
let!(:t3) { create :trip, fuel: 200 }
let!(:t4) { create :trip, fuel: nil }
let!(:i1) { create :item, trip: t1, quantity: 10 }
let!(:i2) { create :item, trip: t1, quantity: 20 }
let!(:i3) { create :item, trip: t2, quantity: 50 }
it "returns the correct response with old" do
expect(Trip.to_dashboard_chart_old).to eq(
[
["Fuel", "Quantity"],
[50,30],
[100,50],
[200,0]
]
)
end
it "returns the correct response with new" do
expect(Trip.to_dashboard_chart_new).to eq(
[
["Fuel", "Quantity"],
[50,30],
[100,50],
[200,0]
]
)
end
Trip.to_dashboard_chart_old
次通过,而Trip.to_dashboard_chart_new
则失败
expected: [["Fuel", "Quantity"], [50, 30], [100, 50], [200, 0]]
got: [["Fuel", "Quantity"], [50, 30], [100, 50]]
如何修改查询以确保为没有任何项目的旅行返回0
的数量?
答案 0 :(得分:2)
def self.to_dashboard_chart_my
arr = [['Fuel','Quantity']]
items = self.left_outer_joins(:items).
where('trips.fuel is not null').
select("fuel, COALESCE(sum(items.quantity),0) as trip_quantity").
group(:id)
for item in items do
arr << [item.fuel, item.trip_quantity]
end
arr
end
Warming up --------------------------------------
Your One 148.000 i/100ms
My one 165.000 i/100ms
Calculating -------------------------------------
Your One 1.461k (±10.3%) i/s - 7.252k in 5.052092s
My one 1.676k (± 8.8%) i/s - 8.250k in 5.004467s
答案 1 :(得分:1)
好吧,经过多次头疼,我最终发现问题是查询是如何处理Trips而没有项目的。
将joins
更改为left_outer_joins
可确保包含所有记录,并将COALESCE
添加到SUM
函数可确保返回0
而不是{{} 1}}。
nil
性能要好得多!在具有50,000条记录的数据库中
def self.to_dashboard_chart_new
self.left_outer_joins(:items).
where.not(fuel: nil).
select("fuel, COALESCE(sum(items.quantity),0) as trip_quantity").
group(:id).
map{ |t| [t.fuel, t.trip_quantity] }.
unshift(['Fuel','Quantity'])
end
如果有人能提出更高效的查询,我很乐意将其标记为正确答案。