ruby-on-rails 活动记录:避免在查询响应上示例化对象

dluptydi  于 2023-04-22  发布在  Ruby
关注(0)|答案(4)|浏览(121)

有没有一种直接的方法可以避免在查询时示例化对象?
我想得到一个哈希数组,而不是一个没有示例化阶段的ActiveRecord对象数组,以提高性能。
现在,我正在做这个例子:

sql = User.where(name: 'John', ...).to_sql
ActiveRecord::Base.connection.execute(sql).to_a

有没有一个神奇的方法,比如:User.where(name: 'John', ...).skip_instantiation.to_a

bf1o4zei

bf1o4zei1#

要将SQL结果Map到哈希数组中,可以使用OpenStruct类。

require 'ostruct'

query = "SELECT * FROM USERS"

results = ActiveRecord::Base.connection.execute(query)

mapped = results.map {|r| OpenStruct.new(r)}

参见https://ruby-doc.org/stdlib-2.0.0/libdoc/ostruct/rdoc/OpenStruct.html

gblwokeq

gblwokeq2#

关于Pluck:https://guides.rubyonrails.org/active_record_querying.html#pluck

Customer.where(code: ["Bob01", "Fred01"]).pluck(:id, :code, :name)

#    (0.4ms)  SELECT "customers"."id", "customers"."code", "customers"."name" FROM "customers" WHERE "customers"."code" IN ($1, $2)  [["code", "Bob01"], ["code", "Fred01"]]
# => [[1, "Bob01", "Bob Bobson"], [2, "Fred01", "Fred Fredson"]]

如果你想要一个哈希数组,那么:

columns = [:id, :code, :name]
Customer.
    where(code: ["Bob01", "Fred01"]).
    pluck(*columns).
    map {|row| columns.zip(row).to_h }

# => [{:id=>1, :code=>"Bob01", :name=>"Bob Bobson"}, {:id=>2, :code=>"Fred01", :name=>"Fred Fredson"}]
2guxujil

2guxujil3#

ActiveRecord::Relation类中添加了这个方法:

# config/initializers/active_record_relation.rb
ActiveRecord::Relation.class_eval do
  def run_sql
    ActiveRecord::Base.connection.execute(to_sql).to_a
  end
end

现在我可以在不示例化对象的情况下运行查询:

User.select('name as custom_name').run_sql
s6fujrry

s6fujrry4#

是,使用.pluckas Ben Stephens outlined
然而,我想更详细地讨论它可能不够的情况。

用例1,如果我想从连接表的列中提取数据,该怎么办?
**A:**简单,使用字符串参数:

User.where(name: 'John', ...).joins(:posts).pluck(:id, "#{Post.table_name}.message")

Case 2,如果我想提取任意SQL,例如,从JSONB列的哈希中提取一个值,该怎么办?
**A:**有点复杂。首先,.pluck不支持任意SQL,所以你必须使用.select。其次,使用AS something SQL语句为选择的任意“列”指定一个名称很重要。第三,为了跳过AR对象的示例化,请使用其他人建议的ActiveRecord::Base.connection.execute。将它们放在一起:

sql = User.where(name: 'John', ...).select(:id, "(metadata->'age')::integer AS age").to_sql
ActiveRecord::Base.connection.execute(sql).to_a
#=> [{"id" => 1, "age" => 28}]

相关问题