Ruby on Rails如何查询数据库生成药品-商家价格对比表?
解决药品价格对比表格的查询与视图问题
你的原视图代码有两个核心问题:一是每次循环都触发单独的数据库查询(N+1性能问题),效率极低;二是如果某个药品在某家药店没有价格记录时,find_by_pharmacy_id会返回nil,调用.price就会抛出错误。下面是优化后的完整方案:
第一步:优化控制器的数据加载(避免N+1查询)
我们可以提前预加载所有需要的价格数据,并且整理成方便视图调用的结构,这样视图里就不用再单独查数据库了:
pharmacy = Pharmacy.find(params[:id]) longitude = pharmacy.x latitude = pharmacy.y @competitors = Pharmacy.where(x: (longitude-0.005..longitude+0.005), y: (latitude-0.005..latitude+0.005)) competitor_ids = @competitors.pluck(:id) # 预加载药品及其对应的竞争药店价格,同时整理价格映射 @drugs = Drug.all.sort_by(&:name).includes(:drug_pharmacies).map do |drug| # 过滤出当前药品在竞争药店的价格记录,并按药店ID索引 price_map = drug.drug_pharmacies.where(pharmacy_id: competitor_ids).index_by(&:pharmacy_id) # 给药品对象动态添加一个方法,方便视图快速获取对应药店的价格 def drug.price_for(pharmacy_id) # 从临时存储的price_map中取价格,没有的话返回自定义文本(比如"无") price_entry = instance_variable_get(:@price_map)[pharmacy_id] price_entry&.price || "无" end # 把price_map存为药品对象的临时变量 drug.instance_variable_set(:@price_map, price_map) drug end
第二步:修正视图代码(更简洁且容错)
现在视图可以直接遍历竞争药店,调用我们添加的price_for方法,不用再手动处理索引和查询:
<table> <tr> <th>药品名称</th> <% @competitors.each do |apteka| %> <th><%= apteka.name %></th> <% end %> </tr> <% @drugs.each do |drug| %> <tr> <td><%= drug.name %></td> <% @competitors.each do |apteka| %> <td><%= drug.price_for(apteka.id) %></td> <% end %> </tr> <% end %> </table>
可选:用数据库聚合查询优化(另一种思路)
如果你希望更直接地从数据库获取结构化数据,可以用joins和group_by来整理:
# 控制器中获取结构化的价格数据 pharmacy = Pharmacy.find(params[:id]) longitude = pharmacy.x latitude = pharmacy.y @competitors = Pharmacy.where(x: (longitude-0.005..longitude+0.005), y: (latitude-0.005..latitude+0.005)) competitor_ids = @competitors.pluck(:id) @drugs = Drug.all.order(:name) # 查询所有药品在竞争药店的价格,并按药品ID分组 price_data = DrugPharmacy.where(pharmacy_id: competitor_ids, drug_id: @drugs.pluck(:id)) .select(:drug_id, :pharmacy_id, :price) .group_by(&:drug_id) # 把价格数据转换成 {drug_id => {pharmacy_id => price}} 的哈希 @price_hash = price_data.transform_values do |entries| entries.index_by(&:pharmacy_id).transform_values(&:price) end
对应的视图代码:
<table> <tr> <th>药品名称</th> <% @competitors.each do |apteka| %> <th><%= apteka.name %></th> <% end %> </tr> <% @drugs.each do |drug| %> <tr> <td><%= drug.name %></td> <% @competitors.each do |apteka| %> <td><%= @price_hash[drug.id]&.[](apteka.id) || "无" %></td> <% end %> </tr> <% end %> </table>
这两种方案都能解决你的问题,第一种更面向对象,第二种更偏向数据驱动,你可以根据自己的习惯选择。
内容的提问来源于stack exchange,提问作者bigidigi89
相关产品推荐
相关产品推荐

