将同一列作为总和,以及根据不同条件对同一列的部分总和

Sum same column as total sum, and also partial sums from that same column based on different conditions

我的架构如下所示:

Purchase属于OrderItem,OrderItem属于一个Order。购买属于通过order_items.

订购

购买有一个名为金额的列,订单有一个名为网关交易 ID 的列。当订单有 gateway_transaction_id 时,即视为在线购买。而当订单没有gateway_transaction_id时,则视为下线。

目前,我需要进行 total_sum 次购买,total_sum 次在线购买,以及 total_sum 次线下购买。

这是我目前拥有的:

all_active_purchases.joins(:order).selecting {
          [
            sum(amount).as('total_purchase_amount'),
            count(id).as('total_purchases_count'),
            count(distinct(purchaser_id)).as('total_purchaser_count')
          ]

这是使用 baby_squeel gem.

完成的

我可以这样添加线上和线下的捐赠范围:

scope :online -> { joins(:order).where.not(orders: {gateway_transaction_id: nil})
scope :offline -> { joins(:order).where(orders: {gateway_transaction_id: nil})

然后我可以得到另外两个总和:

purchases.online.sum(:amount)
purchases.offline.sum(:amount)

不过感觉最后两个查询没有必要。我想计算所有值作为计算 total_purchase_amount 的第一个查询的一部分,以减少数据库查询的数量。是否可以将所有这些计算为一个查询的一部分?我要标记 mysql 标记,我也可以在应用程序中使用 mysql 语法。

这是相关的架构:

create_table "purchases", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
    t.decimal "amount", precision: 10, scale: 2, null: false
    t.integer "purchaser_id", null: false
end
create_table "order_items", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
    t.integer "order_id"
    t.string "item_type"
    t.integer "item_id"
end
create_table "orders", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
      t.string "gateway_transaction_id"
end

示例数据:

Purchases:
id: 1, amount: 20
id: 2, amount: 30
order_items:
id: 1, item_type: "Purchase", item_id: 1, order_id: 1
id: 2, item_type: "Purchase", item_id: 2, order_id: 2
order:
id: 1, gateway_transaction_id: 'abcdef'
id: 2, gateway_transaction_id: 

以上id为1的购买是在线购买,因为它的订单有网关交易id,而id为2的购买是离线的,因为它的订单为零gateway_transaction_id

您可以将 SUMCASE 语句一起使用以获得“过滤器”聚合:

all_active_purchases.joins(:orders)
        .select(
           'SUM(CASE WHEN orders.gateway_transaction_id IS NULL THEN purchases.amount ELSE 0 END) AS offline_amount',
           'SUM(CASE WHEN orders.gateway_transaction_id IS NOT NULL THEN purchases.amount ELSE 0 END) AS online_amount'
        )