我有一个RoR应用程序,可以经常通过个人查询来访问数据库。问题是每个查询花费100毫秒,并且由于ActiveRecord正在生成的单个查询的数量,一些控制器操作需要很长时间才能完成。
另一个问题是我不能使用AR includes()(它只生成一个查询),因为我遇到了AR-sqlserver-adapter和unicode字符串忽略索引的问题,我正在从sql查询中删除unicode前缀(这是一个长篇故事......)
示例:
#model
class Company < ActiveRecord::Base
has_one: city
end
#controller
sql_string = where(:code => ['12B1', '34C8', '87DD', ...]).to_sql
sql_string = sql_string.gsub("N'","'") #remove unicode prefix
companies = find_by_sql(sql_string)
#companies = Company.where(:code => ['12B1', '34C8', '87DD', ...]).includes(:city) #I wish I could use this line
当我访问公司阵列中的城市时,我会在控制台中看到每个城市的几个单独查询,这会减慢很多事情。
我希望仅 2个查询:一个用于使用SQL IN
的公司,一个用于城市。
我已经手动完成了(使用虚拟属性),但是否有“Rails方式”呢?
到目前为止我所做的(谨慎,未来的丑陋代码):
def self.get_companies_by_code(code_array)
comps = Company.where(company_code: ['12B1', '34C8', '87DD', ...])
cities_id_array = comps.map {|c| c.city_id}.compact.uniq
cities = City.find(cities_id_array)
comps.each {|co|
co.virtual_city = cities.select{|ct| co.city_id==ct.id}.first }
comps
end
答案 0 :(得分:0)
comps = Company.where(company_code: ['12B1', '34C8', '87DD', ...]).select(:city_id).distinct(:city_id)
cities = City.where(id: comps.collect { |c| c.city_id })
那就是说,rails方式真的是用“包括”。我强烈建议在核心修复unicode问题,这样你就不会在你的代码中永久分散很多变通方法(比如这样)。
答案 1 :(得分:0)
比2个查询(一个用于公司,一个用于城市)更好,只有一个查询使用连接:
def self.get_companies_by_code(code_array)
sql_string = Company.where(company_code: code_array).joins(:city).select("*").to_sql #['12B1', '34C8', '87DD', ...]
sql_string = sql_string.gsub("N'","'") #remove unicode prefix
companies = find_by_sql(sql_string)
end
我在控制台上只获得了这个查询:
Company Load (60.6ms) EXEC sp_executesql N'SELECT * FROM
[companies] INNER JOIN [cities] ON [cities].[id] = [companies].[city_id]
WHERE [companies].[code] IN (''12B1'', ''34C8'', ''87DD'', ''9AA2'')'
现在,奇怪的部分:在控制台上,它返回的公司对象数组不会显示已加入的城市,但它们就在那里!我的意思是,如果我做result.first.attributes
,它会列出公司和加入城市的所有属性
然后,我可以访问返回数组中的“不可见”属性:
> result = Company.get_companies_by_code( ['12B1', '34C8', '87DD'] )
> result.first.city_name
> "Atlanta"
我只能想象如果碰撞碰撞名称会发生什么......
更多“Rails”方法是手动设置关联:
def self.get_companies_by_code(code_array)
sql_string = Company.where(company_code: code_array).to_sql
sql_string = sql_string.gsub("N'","'") #remove unicode prefix
companies = find_by_sql(sql_string)
cities_ids_array = companies.map(&:city_id)
cities_array = City.where(:id => cities_ids_array)
cities_hash = cities_array.group_by {|c| c.id}
companies.each {|comp|
association = comp.association(:city)
association.target = cities_hash[comp.city_id]
}
companies
end
一些参考文献:
http://blog.bigbinary.com/2013/07/01/preload-vs-eager-load-vs-joins-vs-includes.html