新手问题 请教 ActiveRecord 高手

lotusroot · April 27, 2014 · Last by lotusroot replied at April 30, 2014 · 3604 hits

Rails 的 ActiveRecord 查询虽然可以附加很多诸如 includes、select 方法,但查询结果只返回该 Model 的字段,其它返回的字段都被抛弃,但我们经常会对数据库进行多表组合字段的查询,例如:

A 表:产品、销量;B 表:门店、产品;查询出:门店、产品、销量,甚至要更复杂点的统计:门店、产品、SUM(销量)

一直没有找到正确的解决方案(很困惑:难道需要我在 controller 里写若干循环来进行“结果集合并”吗?),请教 ActiveRecord 或者 Rails 处理 SQL 的高手,正确的做法是怎样的?谢谢!

建表间关系,这样就可以从一个 model 中取到另一个 model 的属性。

includes 可以包括其他 model 的数据的,而且也没有人阻止你用SQL啊。。

这不算是一个 Rails 问题,是 OLAP 类型系统设计问题。

Keywords:OLAP、维度表、star schema、ETL。

.select("*,sum(column) as xxx")
record.xxx

#4 楼 @plusor 这个我记得如果 as 后面跟的不是存在字段名,rails 会给过滤掉

模型的字段,是由其查询后的结果动态生成的,也就是说:如果你返回了 a b c 三个字段,那它即有 a b c 三个方法,而在另一个地方,你返回了 a b c d 那么它就拥有了 a b c d 四个方法。

A.select("table_a.a, table_a.b, table_a.c")
  .select("table_b.d")

另外,你可以自己写 SQL 语句来做复杂的查询

result = ActiveRecord::Base.connection.execute <<-EOF
  select table_a.a, table_a.b table_a.c, table_b.d, sum(column) as xxxx
  from table_a
  inner join table_b on (.......)
EOF

还可以写视图,然后建一个 model 来跟它对应

class Report < ActiveRecord::Base
  self.table_name = 'xxxx_view'
end

@saiga 对的,在 rails c 下执行就可以清楚的看到,只返回 Model 存在的字段

@hz_qiuyuanxin 我理解 select 对 model 固有的字段选择有效。你的后两种方法对我有启迪,ActiveRecord::Base.connection.select_all("..."),可以返回一个包含查询结果的 hash,我在 rails c 下试验成功;视图的方法还没试。

@saiga @lotusroot

#4 楼 @plusor 这个我记得如果 as 后面跟的不是存在字段名,rails 会给过滤掉 @saiga 对的,在 rails c 下执行就可以清楚的看到,只返回 Model 存在的字段

当然不是这样子的,我自己最近写了好多次了

对于复杂的 SQL,要么缓冲表,要么 by_sql

@hz_qiuyuanxin 真的吗?为什么我拿不到? 我又三张表: products(id, product_name) stores(id, store_name) orders(id, store_id, product_id, rmb) 我想执行的 sql 是: select stores.store_name AS store_name, products.product_name AS product_name, sum(orders.rmb) AS amount from ((orders left join products ON ((products.id = orders.product_id))) left join stores ON ((stores.id = orders.dim_store_id))) group by stores.store_name , products.product_name order by stores.store_name , products.product_name

在 Order Model 执行: Order.includes(:product, :store).select("stores.store_name AS store_name, products.product_name AS product_name, sum(orders.rmb) AS amount").group("...").order("...")

返回的结果里有 orders 表的字段,但没有我 select 的三个字段。

何解?

#13 楼 @lotusroot 你理解错 ActiveRecordincludes,好好看看这里的文档

你应该这样

Order.join("LEFT OUTER JOIN products ON (products.id = orders.product.id)")
  .join("LEFT JOIN stores ON (stores.id = orders.store_id)")
  .select("stores.store_name AS store_name")
  .select("products.product_name AS product_name")
  .select("SUM(orders.rmb AS amount)")
  .group("stores.store_name, products.product_name")
  .order("stores.store_name DESC, products.product_name DESC")

而且,按照你这个需要来看,根本就不是 Order 该管的事情,你应该直接写 SQL

report = ActiveRecord::Base.connection.execute <<-EOF
SELECT stores.store_name AS store_name,
  products.product_name AS product_name,
  SUM(orders.rmb) AS amount
FROM orders
LEFT JOIN products ON (products.id = orders.product_id)
LEFT JOIN stores ON (stores.id = orders.dim_store_id)
GROUP BY stores.store_name, products.product_name
ORDER BY stores.store_name DESC, products.product_name DESC
EOF

report.to_a.each do |row|
  puts "#{row["store_name"]} - #{row["product_name"]} - #{row["amount"]}"
end

又或者去建一个 VIEW

最后一点:能看一下写 markdown 的语法么?代码应该用什么包起来?

@hz_qiuyuanxin 高手!赞一个! join,includes,select 看了好多遍,确实没理解透。这个场景确实跟 Order 关系不大,主要是想学习掌握 ActiveRecord 的用法。 这里还有一个问题请教,对于 ActiveRecord::Base.connection.execute 查询结果,用哪个 Grid 组件可以很好的在页面上显示出来(能够分页)?

#15 楼 @lotusroot 我不是高手,这是数据库基础,AR 只是一个 ORM,提供了一套 DSL

分页跟你用什么没有关系,

至于分页插件,现在比较流行的是 kaminari,通过 AR 的 DSL,你可以直接这样

Order.join("LEFT OUTER JOIN products ON (products.id = orders.product.id)")
  .join("LEFT JOIN stores ON (stores.id = orders.store_id)")
  .select("stores.store_name AS store_name")
  .select("products.product_name AS product_name")
  .select("SUM(orders.rmb AS amount)")
  .group("stores.store_name, products.product_name")
  .order("stores.store_name DESC, products.product_name DESC")
  .page(params[:page])
  .per(30)

而如果自己写 SQL,然后用 ActiveRecord::Base.connection.execute 的话,你需要自己在 SQL 里管分页,而页面的显示,可以参考一下我最近写的博文 Custom Pagination in Rails Use Kaminari

@hz_qiuyuanxin 谦虚了!你的博文也让我学到了很多,感谢一下!

You need to Sign in before reply, if you don't have an account, please Sign up first.