Rails 为什么在 select 里用 sum 查询,而 sum 消失了?

wfwdex · March 17, 2017 · Last by wfwdex replied at March 17, 2017 · 2512 hits

我有一个表 Device:

id model platform quantity
1 iphone7 ios 3
2 iphone5 ios 2
3 xiaomi4 android 2
4 huawei android 1

我在 model 里加了个 scope 用来做 group 查询:

class Device
   scope :stat, -> { select("model, platform, SUM(quantity) AS total").group("model, platform").order("total DESC") }
end

我在 controller 里调用:

class DevicesController < ApplicationController
  def stat
    @devices = Device.stat
  end
end

但会出现错误:

Mysql2::Error: Unknown column 'total' in 'order clause': SELECT COUNT(*) AS count_all, model, platform AS model_platform FROM devices GROUP BY model, platform ORDER BY total DESC

就是说我的 scope 里的SUM(quantity) AS total消失了。

尝试

在 console 里测试

我用rails console打开,重新调用Device.stat,发现又是正常的。

去掉 order

我把 scope 里的 order 去掉,log 中会输出如下两个信息:

(0.5ms)  SELECT COUNT(*) AS count_all, device_model, platform AS model_platform FROM `device` GROUP BY model, platform

Device Load (0.3ms)  SELECT model, platform, SUM(quantity) AS total FROM `devices` GROUP BY model, platform

感觉问题出在第一条语句上,为什么在 console 里会没有第一条语句,为什么第一条语句里会丢失 sum?

有分页吧

Reply to hooopo

谢谢回复,我有引入 kaminari 到 gemfile,但这个 model 的查询并没使用它,我刚才试着把 kaminari 从 gemfile 里去掉,并 bundle install ,重新 rails s,问题还在,第一条语句还是出现。

Reply to hooopo

找到原因了,是因为我在 views 里,调用了 @devices.any? 用来判断有没有查询结果,换成 @devices.present? 第一条语句就消失了,看起来是因为我在 @devices.each 之前使用了 @devices.any? 所以会先生成第一句语句。

但为什么调用 @deivces.any? 的时候,第一条语句里的 sum 会消失的问题,还没搞明白。

这个讲了 any?和 empty?的差别: https://robots.thoughtbot.com/any-empty

但是如果要深究的话,可以看下面 不知道你的 rails 是什么版本,下面是 4.2.8 的源代码。 这是 any? 的源码

def any?
  if block_given?
    to_a.any? { |*block_args| yield(*block_args) }
  else
    !empty?
  end
end

因为你没有传block,所以 any? == !empty?,而 empty?的源码是这样的

def empty?
  return @records.empty? if loaded?

  if limit_value == 0
    true
  else
    c = count(:all)
    c.respond_to?(:zero?) ? c.zero? : c.empty?
  end
end

因为没有limit(0),所有走了else分之,再细看 count(:all)的代码,代码有点多,就不贴了,其实它根本就没有管select里的内容。 你可以试一下 Device.select(:id).count(:all)看输出的 SQL 就知道了,所以total就消失了。 Rails 5 的虽然代码不同,但是里面有一段代码是relation.except(:select ...,也就是说select也会被剔除。

Reply to tesla_lee

明白了,非常感谢,以后遇到这种需要 select 指定列的情况会避免用any?empty? 👍

.order('SUM(quantity) DESC') 应该也可以?

Reply to msg7086

不可以,会报错。

wfwdex closed this topic. 18 Mar 08:12
You need to Sign in before reply, if you don't have an account, please Sign up first.