【问题标题】:PostgreSQL ordering by another attribute by default and then by whatever I define?PostgreSQL 默认按另一个属性排序,然后按我定义的任何属性排序?
【发布时间】:2021-05-14 13:36:44
【问题描述】:

我有一个 IndexQuery,我在其中丰富了一组用户,然后我对其应用不同的过滤器。

class Users::IndexQuery
  def initialize(users)
    @users = users
  end

  def call
    @users.kept.created_desc
      .select(
        "users.*",
        "COALESCE(paid_invoices.paid_amount_cents,0) AS paid_amount_cents",
        "COALESCE(unpaid_invoices.unpaid_amount_cents,0) AS unpaid_amount_cents",
      )
      .joins(joins_paid_invoices, joins_unpaid_invoices, :subscription)
      .includes(:subscription)
  end

  private

  def joins_paid_invoices
    @joins_paid_invoices ||= <<-SQL
      LEFT OUTER JOIN (
        SELECT invoices.user_id as user_id,
               SUM(invoices.amount_cents) as paid_amount_cents
        FROM invoices
        WHERE invoices.status = 'paid'
        GROUP BY user_id
      )
      AS paid_invoices
      ON paid_invoices.user_id = users.id
    SQL
  end

  def joins_unpaid_invoices
    @joins_unpaid_invoices ||= <<-SQL
      LEFT OUTER JOIN (
        SELECT invoices.user_id AS user_id,
               SUM(invoices.amount_cents) as unpaid_amount_cents
        FROM invoices
        WHERE invoices.status = 'unpaid'
        GROUP BY user_id
      )
      AS unpaid_invoices
      ON unpaid_invoices.user_id = users.id
    SQL
  end
end

这是控制器中发生的事情:

enriched_users = ::Users::IndexQuery.new(User.all.includes(:account)).call
if params.dig(:q, :extra_params, :preset_filter).present?
  enriched_users = enriched_users.where("subscriptions.status = ?", "in_trial").order("subscriptions.trial_end ASC")
end

但是,在控制台中运行以 .to_sql 结尾的查询会打印以下内容:

ORDER BY \"users\".\"id\" DESC, subscriptions.trial_end ASC"

在控制器和IndexQuery 中都没有我指定它应该按Users.id 订购。可能有什么问题,为什么默认按该属性排序?

编辑:我在 IndexQuery 中有一个GROUP BY users.id。会不会是这个问题?

【问题讨论】:

  • 可以添加IndexQuery类吗?
  • @SebastianPalma 添加到 OP。
  • User.default_scopes 返回什么?
  • @SebastianPalma 它返回一个空数组 []
  • @SebastianPalma 我想我找到了。它是 IndexQuery 开头的 .created_desc 范围,它按 user.id 范围 :created_desc, -> { order(id: :desc) } 排序。谢谢!

标签: ruby-on-rails database postgresql


【解决方案1】:

在 Postgres 9.4+ 上,您可以大大简化这一点 by using FILTER,它可以让您对聚合函数的结果设置条件:

# see https://github.com/rubocop/ruby-style-guide#namespace-definition
module  Users
  class IndexQuery
    def initialize(users)
      @users = users
    end

    def call
      @users.kept.created_desc
        .joins(:invoices)
        .group(:id)
        .select(
          User.arel_table[:*],
          'SUM(invoices.amount_cents) FILTER (WHERE invoices.paid = true) AS paid_amount_cents',
          'SUM(invoices.amount_cents) FILTER (WHERE invoices.paid = false) AS unpaid_amount_cents'
        )
    end

    # conventiance method so that you don't need to call
    # Users::IndexQuery.new(users).call
    def self.call(users)
       new(users).call
    end
  end
end

但老实说,我不明白为什么要将它提取到它自己的对象中,而不是仅仅在模型中创建一个作用域/类方法作为一个非常复杂的解决方案。

【讨论】:

    猜你喜欢
    • 2011-01-25
    • 2016-07-03
    • 2019-09-19
    • 2021-09-18
    • 1970-01-01
    • 1970-01-01
    • 2018-05-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多