新手问题 一个复杂的分组查询如何写,有没有思路?

QueXuQ · 2014年06月26日 · 最后由 billy 回复于 2014年06月26日 · 2282 次阅读

目前我有的数据结构是,3 个 Model:

Class Food, Class Sale, Class Meal

结构是:

Food belongs_to Sale
Food belongs_to Meal

Food 是食品,Sale 是卖出食品,既订单。Meal 是员工的配餐,有员工价或是不需要费用享用。

因为我要统计 Food 每天的出货量,既包括一天的 Sale 金额+Meal 金额。 还有统计 Food 的成本,既包括一天的 Sale 成本+Meal 成本。

比较笨的写法是,通过SaleMeal两个 model 分别统计出来后,然后在相加进行显示。但是我觉得这样不够优雅,觉得应该用 SQL 语句就可以直接写出,但是好像有点复杂,不知道可否提供思路呢?

Food.joins(:sale, :meal)...

把模型贴出来,写 SQL 的话一般这种用 join 一次就算出来了....

#1 楼 @liprais 只用 join 应该还不够。

Model Food
name
price
created_at
updated_at
Model Sale
food_id
sale_price  #销售价
price #成本价
number
...
created_at
updated_at
Model Meal
food_id
sale_price  #销售价
price #成本价
user_id
...
created_at
updated_at

我要计算的是,通过salemealcreated_at计算出,每个 food 每天的销售总额成本总额,并且只显示有记录的 food。

也就是例如是今天,那就找出: 2014-6-26 里,一个 Food 的所有 Sale 和 Meal 的 sale_price 加起来,还有 price 加起来。如果 Food 在今天没有记录,就不显示。

...你需要的不是 joins 是 union 吧。。。

但是难道你不需要分别统计 Sale 和 Meal 么……总觉得没必要这样搞。。。

#3 楼 @Kabie 以前只在同一张表里用过 union,两张表还不知道原来是可以用 union 的。 分别统计是需要的,分别统计就方便了。直接一个 sum 就可以了。 我就想着如果 sql 可以搞定,那就更好了。

我倒觉得分别捞出 Sale 的卖出的总价 和 Meal 的卖出的总价,然后用 ruby 计算总价性能更好点。

SQL 可以满足现在需求,因为你的描述看起来不复杂。

但是,假设需求将来增加了呢?比如说,需要你画出一个图表展示每天的成本,销售和利润变化曲线图。有点傻眼了吧,我也是。好嘛,过一阵老板又说:"我还想看看每周以及每月的变化"。啊.....

虽然不会现在就写将来的需求,但最好能保证将来需求发生合理的变化或者增加时,我不会太傻眼。

这个需求如果是我来写的话,很简单,不用 SQL。

新建一个 model

rails g model Stat sale_amount:integer mean_amount:interger sale_cost:interger total:integer

或者更灵活,直接使用 json field(PostgreSQL) 或者 text field 做 serialize

rails g model Stat data:text

然后 Stat model 加一个 Class methods,计算当天的数据,这些你都写好了,然后新建数据。再做一个 rake task 调用这个 method。

这样就可以了,然后做一个 cron job,每天定时写进去,发送报表给相关人,收工。

算法改变了?需要统计的项目改变了?没问题,写一个 rake task 或者 migrartion 更新所有数据。我不介意在 rake task 里面大量使用 SQL, 但我很介意在应用本身里面大量使用它们。

#5 楼 @Victor 哦,目前我们就是用 ruby 做的。但是突发奇想,想一气呵成。所以才做这个尝试,其实捞出总数后,用 ruby 在加确实方便很多。

#6 楼 @billy 这个半废不错。以后估计确实要做一个定时的比较方便,就是不知道怎么能保证他不出错,例如会不会有一天就不干活了。

@QueXuQ 谁也不能保证不出错 :) 不过 crontab 是 linux 的系统服务,正常情况下都很稳定的。如果有偶然性的错误,应该由系统的监控来负责,应用程序只要测试符合要求可以不必考虑别的部件的状况。

另外,这个 model 其实是一个 aggregation, 不包含原始数据。实在有问题也可以根据原始数据重新生成。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号