【问题标题】:Rails Merging Multiple ActiveRecord QueriesRails 合并多个 ActiveRecord 查询
【发布时间】:2013-12-10 05:01:01
【问题描述】:

有人能指出 ActiveRecord 是如何从左外连接查询生成嵌套散列的吗?

我正在为一个复杂的查询做一个急切的加载,其中不同的 WHERE 子句需要应用于 has_many 关联的表。当 Rails 生成查询以获取应用条件的关联时,我没有得到预期的结果。

我能够分段构建结果 - 用于驱动表,并分别用于 has_many 关联。现在我需要将关联表的结果合并到驱动表中。

或者更简单地说,我在应用程序中有以下表格。

Items
___________________
| id | item_name  |
___________________
| 1  | "Item One" |
| 1  | "Item One" |
| 1  | "Item One" |
| 1  | "Item One" |
| 1  | "Item One" |
___________________

Inventories
_________________
| id | quantity |
_________________
| 1  | 10       |
| 1  | 10       |
| 1  | 10       |
| 1  | 10       |
| 1  | 10       |
_________________

Transactions
_________________________________________
| id | item_id | tran_status | tran_qty |
_________________________________________
| 1  | 1       | "ordered"   | 1        |
| 2  | 1       | "picked"    | 1        |
| 3  | 1       | "ordered"   | 4        |
| 4  | 1       | "canceled"  | 5        |
| 5  | 1       | "ordered    | 2        |
_________________________________________

Adjustments
_______________________________________
| id | item_id | adj_status | adj_qty |
_______________________________________
| 1  | 1       | "adjusted" | 1       |
| 4  | 1       | "canceled" | 1       |
_______________________________________

我正在尝试为以下格式的 AJAX 操作创建 JSON 响应(使用 JBuilder)。

[{"id": 1, 
  "name": "Item One",
  "inventory": {"quantity": 10},
  "transactions": [ {"tran_status": "Ordered", "tran_qty": 1},
                    {"tran_status": "Picked",  "tran_qty": 1},
                    {"tran_status": "Ordered", "tran_qty": 4},
                    {"tran_status": "Ordered", "tran_qty": 2} ],
  "adjustments":  [ {"adj_status": "Adjusted", "adj_qty": 1} ]
 }
]

请注意,状态为“已取消”的记录被排除在外。

现在可能会为多个项目请求这,我想在查询期间进行急切加载。

但是,以下查询不起作用。 Rails 会生成一个左外连接查询,这会破坏派对。

  Item.includes(:inventory).
       includes(:transactions).
       where("transactions.status not in ('canceled')").
       references(:transactions).
       includes(:adjustments).
       where("adjustments.status not in ('canceled')").
       references(:adjustments).
       where(id: params[:item])

所以,我要做的是分三步执行查询,并在最后创建一个哈希或 Openstruct 以便在 JBuilder 中进行迭代。

  items = Item.includes(:inventory).
  item_ids =  items.collect(&:id)
  trans = Transaction.where(item_id: item_ids).where("status not in ('canceled')")
  adjs = Adjustment.where(item_id: item_ids).where("status not in ('canceled')")

现在我想将所有这些合并到一个哈希数组中。

我意识到 Rails 会在内部做类似的事情,并考虑利用现有知识而不是用我有限的知识构建一种效率较低的方法。

任何帮助将不胜感激。

更新

问题是在我自己尝试构建解决方案的背景下。核心问题是让 Rails 生成带有应用于关联表的条件的查询。接受的答案是与条件建立关联的建议(可以在接受的答案的讨论线程中找到)。

【问题讨论】:

    标签: ruby-on-rails rails-activerecord


    【解决方案1】:

    如果我没听错,您需要迭代项目:

    items = Item.includes(:inventory)
    result = []
    items.each do |i|
      result << {
         :item => i,
         :trans => Transaction.where(item_id: i.id ).where("status not in ('canceled')"),
         :adjs => Adjustment.where(item_id: i.id).where("status not in ('canceled')")
      }    
    end
    

    或者您可以通过一次收集将其放入数组中:

    result = Item.includes(:inventory, :transactions, :adjustments).collect { |i| i, 
           i.transactions.where("status not in ('canceled')"), 
           i.adjustments.where("status not in ('canceled')") 
       }
    

    ,前提是您已正确设置关联

    【讨论】:

    • 但是,这不会触发每个项目的两个查询吗?我想限制查询的数量并合并结果,Ruby 方式或 Rails 方式。
    • 您也可以在第一个场景中预先加载关联
    • 如果我在没有 WHERE 子句的情况下进行预加载,它会将整个事务表拉入内存吗?
    • 如果你有适当的关联,它应该只加载与找到的项目相关联的内容。
    • 哦,我明白了,您需要排除已取消的交易。我相信这可以通过项目类中 has_many 的 :conditions 来实现。
    【解决方案2】:
    require 'json'
    
    hash_arr = []
    
    Item.includes(:inventories,:transactions,:adjustments).all.each do |item|
      hash = {"id"=>item.id,
              "name"=>item.name,
               "inventory"=>item.inventories.count,
      "transactions"=>item.transactions.select("tran_status,tran_qty").collect{ |x|          
           {:tran_status=>x.tran_status,:tran_qty=>x.tran_qty} 
      },
      "adjustments"=>item.adjustments.select("adj_status,adj_qty").collect{|x| {:adj_status=>x.adj_status,:adj_qty=>x.adj_qty}
    }
       }
      hash_arr.push hash
    end
    
    hash_arr.to_json
    

    希望这符合您的要求

    或者您可以使用as_json 并指定您自己的 JSON 格式(这样会更简洁)。检查here

    【讨论】:

    • 问题在于在交易和调整表上应用 WHERE 子句。否则所有关闭的事务都会加载到内存中,这不会是低效的。我遇到的障碍是当条件应用于关联表时,Rails 会使用 LEFT OUTER JOIN 生成查询。而且由于我有两个“事务表”——事务和调整,Rails 生成的 LEFT OUTER JOIN 并没有给我预期的结果。那时我决定单独触发这些查询并自己合并。一旦生成结果,我就被困在“合并”部分。
    • 所以你想要一个更好的方法来合并你的结果?
    • 是的。有点儿。这很可能会解决问题。另一种选择可能是将“事务”和“调整”重构为一个具有多态关联的表,依此类推。我想非常谨慎地迈出这一步。
    猜你喜欢
    • 2018-01-31
    • 1970-01-01
    • 2011-04-05
    • 2014-08-13
    • 1970-01-01
    • 2018-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多