ActiveRecord/Postgres:PGError 必须出现在 GROUP BY 子句中或在聚合函数中使用

发布于 2024-11-04 23:47:10 字数 3460 浏览 4 评论 0原文

在我的模型中,我有几个可以相继使用(和重复使用)的查询。其中之一应该是总计金额。这在 SQLite 上运行良好,但在 Postgres 上抛出错误:

ActiveRecord::StatementInvalid (PGError: ERROR:  column "entries.date" must appear in the GROUP BY clause or be used in an aggregate function
: SELECT sum(case when joint = false then amount else amount / 2 end) as total, sum(case when joint = false then amount else 0 end) as sum_personal, sum(case when joint = true and user_id = 1 then amount / 2 else 0 end) as sum_user_joint, sum(case when joint = true and user_id = 2 then amount / 2 else 0 end) as sum_partner_joint  FROM "entries" WHERE (1 = entries.user_id OR (2 = entries.user_id AND entries.joint = 't')) AND ('2011-04-01' <= entries.date AND entries.date <= '2011-04-30') AND (amount_calc > 0 AND compensation = 'f') ORDER BY date asc)

Model.rb 的相关部分

  # all entries of one month
  def self.all_entries_month(year, month, user_id, partner_id)
    mydate = Date.new(year, month, 1)

    where(':user_id = entries.user_id OR (:partner_id = entries.user_id AND entries.joint = :true)', {
        :user_id => user_id,
        :partner_id => partner_id,
        :true => true
    }).
    where(':first_day <= entries.date AND entries.date <= :last_day', { 
        :first_day => mydate,
        :last_day => mydate.at_end_of_month
    })
  end

  def self.income
    where('amount_calc > 0 AND compensation = ?', false)
  end

  def self.cost
    where('amount_calc <= 0 AND compensation = ?', false)
  end

  def self.order_by_date
    order('date asc')
  end

  # group by tag and build sum of groups named group_sum
  def self.group_by_tag(order)
    group('tag').
    select('tag, ' +
           'sum(case when joint = "f" then amount else amount / 2 end) as tag_sum'
          ).
    order('tag_sum ' + order)
  end

  def self.multiple_sums(user_id, partner_id)
    case ActiveRecord::Base.connection.adapter_name  
      when 'SQLite'
        select('sum(case when joint = "f" then amount else amount / 2 end) as total, ' +
               'sum(case when joint = "f" then amount else 0 end) as sum_personal, ' + 
               'sum(case when joint = "t" and user_id = ' + user_id.to_s + ' then amount / 2 else 0 end) as sum_user_joint, ' + 
               'sum(case when joint = "t" and user_id = ' + partner_id.to_s + ' then amount / 2 else 0 end) as sum_partner_joint ' 
        )
      when 'PostgreSQL'
        select('sum(case when joint = false then amount else amount / 2 end) as total, ' +
               'sum(case when joint = false then amount else 0 end) as sum_personal, ' + 
               'sum(case when joint = true and user_id = ' + user_id.to_s + ' then amount / 2 else 0 end) as sum_user_joint, ' + 
               'sum(case when joint = true and user_id = ' + partner_id.to_s + ' then amount / 2 else 0 end) as sum_partner_joint ' 
        )
    else
      raise 'Query not implemented for this DB adapter'
    end  
  end

控制器

# get all entries of given month
@cost = Entry.all_entries_month(@year, @month, current_user.id, current_partner.id).cost

# group cost by categories
@group_cost = @cost.group_by_tag('asc')

# still need to sort by date
@cost = @cost.order_by_date

@calc_cost = @cost.multiple_sums(current_user.id, current_partner.id)[0]  

如何在不破坏其他查询的情况下更改我的查询 multiple_sums?或者我是否需要从地面实现 multiple_sums 而不使用现有的?

In my model I have a couple of queries that can be used (and re-used) one after another. One of those should aggregate amounts. This works fine on SQLite and throws an error on Postgres:

ActiveRecord::StatementInvalid (PGError: ERROR:  column "entries.date" must appear in the GROUP BY clause or be used in an aggregate function
: SELECT sum(case when joint = false then amount else amount / 2 end) as total, sum(case when joint = false then amount else 0 end) as sum_personal, sum(case when joint = true and user_id = 1 then amount / 2 else 0 end) as sum_user_joint, sum(case when joint = true and user_id = 2 then amount / 2 else 0 end) as sum_partner_joint  FROM "entries" WHERE (1 = entries.user_id OR (2 = entries.user_id AND entries.joint = 't')) AND ('2011-04-01' <= entries.date AND entries.date <= '2011-04-30') AND (amount_calc > 0 AND compensation = 'f') ORDER BY date asc)

Relevant part of Model.rb

  # all entries of one month
  def self.all_entries_month(year, month, user_id, partner_id)
    mydate = Date.new(year, month, 1)

    where(':user_id = entries.user_id OR (:partner_id = entries.user_id AND entries.joint = :true)', {
        :user_id => user_id,
        :partner_id => partner_id,
        :true => true
    }).
    where(':first_day <= entries.date AND entries.date <= :last_day', { 
        :first_day => mydate,
        :last_day => mydate.at_end_of_month
    })
  end

  def self.income
    where('amount_calc > 0 AND compensation = ?', false)
  end

  def self.cost
    where('amount_calc <= 0 AND compensation = ?', false)
  end

  def self.order_by_date
    order('date asc')
  end

  # group by tag and build sum of groups named group_sum
  def self.group_by_tag(order)
    group('tag').
    select('tag, ' +
           'sum(case when joint = "f" then amount else amount / 2 end) as tag_sum'
          ).
    order('tag_sum ' + order)
  end

  def self.multiple_sums(user_id, partner_id)
    case ActiveRecord::Base.connection.adapter_name  
      when 'SQLite'
        select('sum(case when joint = "f" then amount else amount / 2 end) as total, ' +
               'sum(case when joint = "f" then amount else 0 end) as sum_personal, ' + 
               'sum(case when joint = "t" and user_id = ' + user_id.to_s + ' then amount / 2 else 0 end) as sum_user_joint, ' + 
               'sum(case when joint = "t" and user_id = ' + partner_id.to_s + ' then amount / 2 else 0 end) as sum_partner_joint ' 
        )
      when 'PostgreSQL'
        select('sum(case when joint = false then amount else amount / 2 end) as total, ' +
               'sum(case when joint = false then amount else 0 end) as sum_personal, ' + 
               'sum(case when joint = true and user_id = ' + user_id.to_s + ' then amount / 2 else 0 end) as sum_user_joint, ' + 
               'sum(case when joint = true and user_id = ' + partner_id.to_s + ' then amount / 2 else 0 end) as sum_partner_joint ' 
        )
    else
      raise 'Query not implemented for this DB adapter'
    end  
  end

Controller

# get all entries of given month
@cost = Entry.all_entries_month(@year, @month, current_user.id, current_partner.id).cost

# group cost by categories
@group_cost = @cost.group_by_tag('asc')

# still need to sort by date
@cost = @cost.order_by_date

@calc_cost = @cost.multiple_sums(current_user.id, current_partner.id)[0]  

How can I change my query multiple_sums without breaking the other queries? Or do I need to implement multiple_sums from ground without using the existing ones?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

想你只要分分秒秒 2024-11-11 23:47:10

删除 order by 子句,据我所知,无论如何,这都是无用的,因为您将分组为单行。

请重新格式化您的查询,以便它们可见可读

Remove order by clause, which is useless as far as I can see anyway because you're grouping into single row.

And please - reformat your queries so that they will be visible and readable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文