【问题标题】:How to get JOIN data when using ActiveRecord::connection.exec_query?使用 ActiveRecord::connection.exec_query 时如何获取 JOIN 数据?
【发布时间】:2019-11-22 17:07:34
【问题描述】:

为了提高速度,我正在构建一个报告并想要进行查询并返回一个 Hash 数组而不是模型。

sql = Car.joins(:parts).to_sql
# => "SELECT  `cars`.* FROM `cars` INNER JOIN `parts` ON `parts`.`car_id` = `cars`.`id`"

results = ActiveRecord::Base.connection.exec_query(sql).to_a
# => [ { id: 1, ...}, { id: 2, ...} ]

这有效,但我如何访问关联?

我想要[{ id: 1, parts: { id: 1, ... } }, { id: 2, parts: { id: 2, ... } } ]之类的东西

我假设由于 ActiveRecord 执行一个查询并且可以提供此数据,因此有一种方法。

【问题讨论】:

  • 您必须添加includes 才能访问连接表ActiveRecord::Base.connection.exec_query(Car.includes(:parts).joins(:parts).to_sql).to_at* 前缀表示该表。或者您可以使用选择ActiveRecord::Base.connection.exec_query(Car.joins(:parts).select('cars.*, parts.*').to_sql)
  • @SebastianPalma 谢谢,这是朝着正确方向迈出的一步。我看起来需要转换输出。我敢打赌 ActiveRecord 内部有一些东西可以做到这一点。

标签: ruby-on-rails ruby-on-rails-5 mysql2


【解决方案1】:
sql = Car.select(:id, :brand, 'parts.id AS part_id', 'parts.name AS part_name')
         .joins(:parts)
         .to_sql

results = Car.connection.execute(sql)
results.each_with_object({}) do |row, memo|
  id = row["id"]
  memo[id] ||= {
    id: id,
    brand: row["brand"],
    parts: []
  }
  memo[id][:parts].push({
    id: row["part_id"],
    name: row["part_name"]
  }) unless row["part_id"].nil?
end.values

这里发生了很多事情,但请记住 SQL 是表格的,因此您在结果中得到的是每个部分的一行加上汽车的列:

 id |   brand  | part_id | part_name 
----+----------+---------+-----------
  2 | Ford     |       1 | Steeling wheel
  2 | Ford     |       2 | Clutch
  2 | Ford     |       3 | Spark plug
  3 | Volvo    |       4 | Battery
  3 | Volvo    |       5 | Carburetor
# ...

在这里运行$ rails db 并在数据库控制台中运行查询并将结果作为表格查看非常有用。

所以使用results.each_with_object({}),我们在构建哈希的同时遍历结果行:

{
  # hash key is the same as the id
  2 => {
    id: 2, 
    brand: 'Ford'
    parts: [
      {
        id: 1,
        Steeling wheel
      }
    ]
  }
  3 => {
     # ...
  }
}

.values 将其转换为数组。现在您所要做的就是将其与仅使用 .includes.eager_load 进行基准测试,看看它是否真的值得大惊小怪。

【讨论】:

    【解决方案2】:

    不知道你为什么会遇到所有这些麻烦,要么你构建了一个查询,它返回行,所以数组(这就是 SQL 所做的),或者 也许你在渲染页面时遇到了性能问题,并且想要避免 N+1 查询,在这种情况下有一个更简单的解决方案:您可以使用 includes 预先加载关联,所以写

    cars = Car.includes(:parts) 
    

    这将触发两个查询:第一个是获取所有汽车,一个是获取所有关联的零件(而不是对每辆车的查询来获取它的所有零件)。

    如果您确实想使用 sql,并且不使用模型中的任何功能(您似乎是从模型开始,但也许这是为了大量报告/处理),我会调整您的查询以在 @ 上订购987654323@ 然后将接收到的行(数组)转换为散列会非常简单/更容易(但感觉就像你正在做 Rails 所做的很多工作并且失去了很多轻松,但又一次它使在您的具体情况下值得付出努力)。

    【讨论】:

    • 是的,这是一个特定的案例。该报告涉及大量数据。我想避免创建 ActiveRecord 模型实例的开销。
    • 您是否正在寻找详细说明如何将数组转换为哈希的答案?因为您似乎是一位经验丰富的程序员,而且看起来很漂亮……或者它必须简洁/可读,还是最佳方式(时间/内存方面?)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多